This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[MIPS, committed] Add OP_OPTIONAL_REG


In the initial conversion to mips_operand structures, I'd kept the list
of case statements for optional registers.  This was because I had vague
hopes of detecting optional operands programmatically, such as by looking
for cases where the first and second operands have the same register type
and where the first is an output and the second an input.

In the end though I don't think that's a good idea.  There are too many
odd cases.  E.g. I don't think it makes sense for "move $4" to be allowed
as a no-op move from $4 to $4.  Then there are things like indexed loads,
where the destination and index match the "output-gpr,input-gpr" pattern,
but where it would be confusing to allow the index to be omitted.
Then there's "jalr", which again matches the "output-gpr,input-gpr" pattern,
but where the registers must explicitly be different.  (The last one doesn't
matter in practice because of the implicit-$31 form, but I still think
it's an example of why the rule doesn't really work in general.)

This patch therefore adds a operand type for optional registers.
Tested on various targets and applied.

Richard


include/opcode/
	* mips.h (OP_OPTIONAL_REG): New mips_operand_type.
	(mips_optional_operand_p): New function.

opcodes/
	* mips-formats.h (OPTIONAL_REG, OPTIONAL_MAPPED_REG): New macros.
	* micromips-opc.c (decode_micromips_operand): Use OPTIONAL_REG
	and OPTIONAL_MAPPED_REG.
	* mips-opc.c (decode_mips_operand): Likewise.
	* mips16-opc.c (decode_mips16_operand): Likewise.
	* mips-dis.c (print_insn_arg): Handle OP_OPTIONAL_REG.

gas/
	* config/tc-mips.c (operand_reg_mask, match_operand): Handle
	OP_OPTIONAL_REG.
	(mips_ip, mips16_ip): Use mips_optional_operand_p to check
	for optional operands.

Index: include/opcode/mips.h
===================================================================
--- include/opcode/mips.h	2013-08-19 19:13:13.972190295 +0100
+++ include/opcode/mips.h	2013-08-19 19:13:19.008234935 +0100
@@ -346,6 +346,10 @@ enum mips_operand_type {
   /* Described by mips_reg_operand.  */
   OP_REG,
 
+  /* Like OP_REG, but can be omitted if the register is the same as the
+     previous operand.  */
+  OP_OPTIONAL_REG,
+
   /* Described by mips_reg_pair_operand.  */
   OP_REG_PAIR,
 
@@ -574,6 +578,15 @@ struct mips_pcrel_operand
   unsigned int flip_isa_bit : 1;
 };
 
+/* Return true if the assembly syntax allows OPERAND to be omitted.  */
+
+static inline bfd_boolean
+mips_optional_operand_p (const struct mips_operand *operand)
+{
+  return (operand->type == OP_OPTIONAL_REG
+	  || operand->type == OP_REPEAT_PREV_REG);
+}
+
 /* Return a version of INSN in which the field specified by OPERAND
    has value UVAL.  */
 
Index: opcodes/mips-formats.h
===================================================================
--- opcodes/mips-formats.h	2013-08-19 19:13:13.972190295 +0100
+++ opcodes/mips-formats.h	2013-08-19 19:13:19.010234953 +0100
@@ -69,6 +69,14 @@ #define REG(SIZE, LSB, BANK) \
     return &op.root; \
   }
 
+#define OPTIONAL_REG(SIZE, LSB, BANK) \
+  { \
+    static const struct mips_reg_operand op = { \
+      { OP_OPTIONAL_REG, SIZE, LSB }, OP_REG_##BANK, 0 \
+    }; \
+    return &op.root; \
+  }
+
 #define MAPPED_REG(SIZE, LSB, BANK, MAP) \
   { \
     typedef char ATTRIBUTE_UNUSED \
@@ -78,6 +86,16 @@ #define MAPPED_REG(SIZE, LSB, BANK, MAP)
     }; \
     return &op.root; \
   }
+
+#define OPTIONAL_MAPPED_REG(SIZE, LSB, BANK, MAP) \
+  { \
+    typedef char ATTRIBUTE_UNUSED \
+      static_assert[(1 << (SIZE)) == ARRAY_SIZE (MAP)]; \
+    static const struct mips_reg_operand op = { \
+      { OP_OPTIONAL_REG, SIZE, LSB }, OP_REG_##BANK, MAP \
+    }; \
+    return &op.root; \
+  }
 
 #define REG_PAIR(SIZE, LSB, BANK, MAP) \
   { \
Index: opcodes/micromips-opc.c
===================================================================
--- opcodes/micromips-opc.c	2013-08-19 19:13:13.972190295 +0100
+++ opcodes/micromips-opc.c	2013-08-19 19:13:19.009234944 +0100
@@ -53,9 +53,9 @@ decode_micromips_operand (const char *p)
 	{
 	case 'a': MAPPED_REG (0, 0, GP, reg_28_map);
 	case 'b': MAPPED_REG (3, 23, GP, reg_m16_map);
-	case 'c': MAPPED_REG (3, 4, GP, reg_m16_map);
+	case 'c': OPTIONAL_MAPPED_REG (3, 4, GP, reg_m16_map);
 	case 'd': MAPPED_REG (3, 7, GP, reg_m16_map);
-	case 'e': MAPPED_REG (3, 1, GP, reg_m16_map);
+	case 'e': OPTIONAL_MAPPED_REG (3, 1, GP, reg_m16_map);
 	case 'f': MAPPED_REG (3, 3, GP, reg_m16_map);
 	case 'g': MAPPED_REG (3, 0, GP, reg_m16_map);
 	case 'h': REG_PAIR (3, 7, GP, reg_h_map);
@@ -144,7 +144,7 @@ decode_micromips_operand (const char *p)
     case 'R': REG (5, 6, FP);
     case 'S': REG (5, 16, FP);
     case 'T': REG (5, 21, FP);
-    case 'V': REG (5, 16, FP);
+    case 'V': OPTIONAL_REG (5, 16, FP);
 
     case 'a': JUMP (26, 0, 1);
     case 'b': REG (5, 16, GP);
@@ -158,12 +158,12 @@ decode_micromips_operand (const char *p)
     case 'o': SINT (16, 0);
     case 'p': BRANCH (16, 0, 1);
     case 'q': HINT (10, 6);
-    case 'r': REG (5, 16, GP);
+    case 'r': OPTIONAL_REG (5, 16, GP);
     case 's': REG (5, 16, GP);
     case 't': REG (5, 21, GP);
     case 'u': HINT (16, 0);
-    case 'v': REG (5, 16, GP);
-    case 'w': REG (5, 21, GP);
+    case 'v': OPTIONAL_REG (5, 16, GP);
+    case 'w': OPTIONAL_REG (5, 21, GP);
     case 'y': REG (5, 6, GP);
     case 'z': MAPPED_REG (0, 0, GP, reg_0_map);
     }
Index: opcodes/mips-opc.c
===================================================================
--- opcodes/mips-opc.c	2013-08-19 19:13:13.972190295 +0100
+++ opcodes/mips-opc.c	2013-08-19 19:13:19.011234962 +0100
@@ -134,8 +134,8 @@ decode_mips_operand (const char *p)
     case 'S': REG (5, 11, FP);
     case 'T': REG (5, 16, FP);
     case 'U': SPECIAL (10, 11, CLO_CLZ_DEST);
-    case 'V': REG (5, 11, FP);
-    case 'W': REG (5, 16, FP);
+    case 'V': OPTIONAL_REG (5, 11, FP);
+    case 'W': OPTIONAL_REG (5, 16, FP);
     case 'X': REG (5, 6, VEC);
     case 'Y': REG (5, 11, VEC);
     case 'Z': REG (5, 16, VEC);
@@ -153,12 +153,12 @@ decode_mips_operand (const char *p)
     case 'o': SINT (16, 0);
     case 'p': BRANCH (16, 0, 2);
     case 'q': HINT (10, 6);
-    case 'r': REG (5, 21, GP);
+    case 'r': OPTIONAL_REG (5, 21, GP);
     case 's': REG (5, 21, GP);
     case 't': REG (5, 16, GP);
     case 'u': HINT (16, 0);
-    case 'v': REG (5, 21, GP);
-    case 'w': REG (5, 16, GP);
+    case 'v': OPTIONAL_REG (5, 21, GP);
+    case 'w': OPTIONAL_REG (5, 16, GP);
     case 'x': REG (0, 0, GP);
     case 'z': MAPPED_REG (0, 0, GP, reg_0_map);
     }
Index: opcodes/mips16-opc.c
===================================================================
--- opcodes/mips16-opc.c	2013-08-19 19:13:13.972190295 +0100
+++ opcodes/mips16-opc.c	2013-08-19 19:13:19.012234971 +0100
@@ -66,8 +66,8 @@ decode_mips16_operand (char type, bfd_bo
     case 'i': JALX (26, 0, 2);
     case 'l': SPECIAL (6, 5, ENTRY_EXIT_LIST);
     case 'm': SPECIAL (7, 0, SAVE_RESTORE_LIST);
-    case 'v': MAPPED_REG (3, 8, GP, reg_m16_map);
-    case 'w': MAPPED_REG (3, 5, GP, reg_m16_map);
+    case 'v': OPTIONAL_MAPPED_REG (3, 8, GP, reg_m16_map);
+    case 'w': OPTIONAL_MAPPED_REG (3, 5, GP, reg_m16_map);
     case 'x': MAPPED_REG (3, 8, GP, reg_m16_map);
     case 'y': MAPPED_REG (3, 5, GP, reg_m16_map);
     case 'z': MAPPED_REG (3, 2, GP, reg_m16_map);
Index: opcodes/mips-dis.c
===================================================================
--- opcodes/mips-dis.c	2013-08-19 19:13:13.972190295 +0100
+++ opcodes/mips-dis.c	2013-08-19 19:13:19.010234953 +0100
@@ -1042,6 +1042,7 @@ print_insn_arg (struct disassemble_info
       break;
 
     case OP_REG:
+    case OP_OPTIONAL_REG:
       {
 	const struct mips_reg_operand *reg_op;
 
Index: gas/config/tc-mips.c
===================================================================
--- gas/config/tc-mips.c	2013-08-19 19:13:13.972190295 +0100
+++ gas/config/tc-mips.c	2013-08-19 19:13:19.007234927 +0100
@@ -3868,6 +3868,7 @@ operand_reg_mask (const struct mips_cl_i
       abort ();
 
     case OP_REG:
+    case OP_OPTIONAL_REG:
       {
 	const struct mips_reg_operand *reg_op;
 
@@ -5283,6 +5284,7 @@ match_operand (struct mips_arg_info *arg
       return match_msb_operand (arg, operand);
 
     case OP_REG:
+    case OP_OPTIONAL_REG:
       return match_reg_operand (arg, operand);
 
     case OP_REG_PAIR:
@@ -12393,7 +12395,6 @@ mips_ip (char *str, struct mips_cl_insn
   const struct mips_operand *operand;
   struct mips_arg_info arg;
   struct mips_operand_token *tokens;
-  bfd_boolean optional_reg;
   unsigned int opcode_extra;
 
   insn_error = NULL;
@@ -12522,17 +12523,17 @@ mips_ip (char *str, struct mips_cl_insn
 	      /* Handle unary instructions in which only one operand is given.
 		 The source is then the same as the destination.  */
 	      if (arg.opnum == 1 && *args == ',')
-		switch (args[1])
-		  {
-		  case 'r':
-		  case 'v':
-		  case 'w':
-		  case 'W':
-		  case 'V':
-		    arg.token = tokens;
-		    arg.argnum = 1;
-		    continue;
-		  }
+		{
+		  operand = (mips_opts.micromips
+			     ? decode_micromips_operand (args + 1)
+			     : decode_mips_operand (args + 1));
+		  if (operand && mips_optional_operand_p (operand))
+		    {
+		      arg.token = tokens;
+		      arg.argnum = 1;
+		      continue;
+		    }
+		}
 
 	      /* Treat elided base registers as $0.  */
 	      if (strcmp (args, "(b)") == 0)
@@ -12593,7 +12594,6 @@ mips_ip (char *str, struct mips_cl_insn
 	     other operands.  */
 	  arg.opnum += 1;
 	  arg.lax_max = FALSE;
-	  optional_reg = FALSE;
 	  switch (*args)
 	    {
 	    case '+':
@@ -12668,17 +12668,6 @@ mips_ip (char *str, struct mips_cl_insn
 		arg.soft_match = FALSE;
 	      break;
 
-	    case 'r':
-	    case 'v':
-	    case 'w':
-	    case 'W':
-	    case 'V':
-	      /* We have already matched a comma by this point, so the register
-		 is only optional if there is another operand to come.  */
-	      gas_assert (arg.opnum == 2);
-	      optional_reg = (args[1] == ',');
-	      break;
-
 	    case 'I':
 	      if (match_const_int (&arg, &imm_expr.X_add_number, 0))
 		imm_expr.X_op = O_constant;
@@ -12766,16 +12755,6 @@ mips_ip (char *str, struct mips_cl_insn
 	      c = args[1];
 	      switch (c)
 		{
-		case 't':
-		case 'c':
-		case 'e':
-		  /* We have already matched a comma by this point,
-		     so the register is only optional if there is another
-		     operand to come.  */
-		  gas_assert (arg.opnum == 2);
-		  optional_reg = (args[2] == ',');
-		  break;
-
 		case 'D':
 		case 'E':
 		  if (!forced_insn_length)
@@ -12795,7 +12774,12 @@ mips_ip (char *str, struct mips_cl_insn
 	  if (!operand)
 	    abort ();
 
-	  if (optional_reg
+	  /* Skip prefixes.  */
+	  if (*args == '+' || *args == 'm')
+	    args++;
+
+	  if (mips_optional_operand_p (operand)
+	      && args[1] == ','
 	      && (arg.token[0].type != OT_REG
 		  || arg.token[1].type == OT_END))
 	    {
@@ -12808,10 +12792,6 @@ mips_ip (char *str, struct mips_cl_insn
 	  if (!match_operand (&arg, operand))
 	    break;
 
-	  /* Skip prefixes.  */
-	  if (*args == '+' || *args == 'm')
-	    args++;
-
 	  continue;
 	}
       /* Args don't match.  */
@@ -12848,7 +12828,6 @@ mips16_ip (char *str, struct mips_cl_ins
   const struct mips_operand *ext_operand;
   struct mips_arg_info arg;
   struct mips_operand_token *tokens;
-  bfd_boolean optional_reg;
 
   insn_error = NULL;
 
@@ -12961,14 +12940,15 @@ mips16_ip (char *str, struct mips_cl_ins
 	      /* Handle unary instructions in which only one operand is given.
 		 The source is then the same as the destination.  */
 	      if (arg.opnum == 1 && *args == ',')
-		switch (args[1])
-		  {
-		  case 'v':
-		  case 'w':
-		    arg.token = tokens;
-		    arg.argnum = 1;
-		    continue;
-		  }
+		{
+		  operand = decode_mips16_operand (args[1], FALSE);
+		  if (operand && mips_optional_operand_p (operand))
+		    {
+		      arg.token = tokens;
+		      arg.argnum = 1;
+		      continue;
+		    }
+		}
 
 	      /* Fail the match if there were too few operands.  */
 	      if (*args)
@@ -13020,15 +13000,9 @@ mips16_ip (char *str, struct mips_cl_ins
 	    }
 
 	  arg.opnum += 1;
-	  optional_reg = FALSE;
 	  c = *args;
 	  switch (c)
 	    {
-	    case 'v':
-	    case 'w':
-	      optional_reg = (args[1] == ',');
-	      break;
-
 	    case 'p':
 	    case 'q':
 	    case 'A':
@@ -13094,7 +13068,8 @@ mips16_ip (char *str, struct mips_cl_ins
 		}
 	    }
 
-	  if (optional_reg
+	  if (mips_optional_operand_p (operand)
+	      && args[1] == ','
 	      && (arg.token[0].type != OT_REG
 		  || arg.token[1].type == OT_END))
 	    {


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]