[PATCH 6/6] x86: improve matching diagnostics when %st is involved

Jan Beulich jbeulich@suse.com
Wed Apr 30 13:31:22 GMT 2025


Diagnosing operand size vs operand type mismatches doesn't work very
well when GPRs and FPRs are in the same register class, distinguished
just by size. Introduce a separate RegFP class.

--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -2510,8 +2510,8 @@ operand_type_check (i386_operand_type t,
   return 0;
 }
 
-/* Return 1 if there is no conflict in 8bit/16bit/32bit/64bit/80bit size
-   between operand GIVEN and opeand WANTED for instruction template T.  */
+/* Return 1 if there is no conflict in 8bit/16bit/32bit/64bit size
+   between operand GIVEN and operand WANTED for instruction template T.  */
 
 static INLINE int
 match_operand_size (const insn_template *t, unsigned int wanted,
@@ -2531,13 +2531,22 @@ match_operand_size (const insn_template
 		   || (intel_syntax
 		       && flag_code != CODE_64BIT
 		       && (t->operand_types[wanted].bitfield.class == Reg
-			   || t->opcode_modifier.isstring))))
-	   || (i.types[given].bitfield.tbyte
-	       && !t->operand_types[wanted].bitfield.tbyte));
+			   || t->opcode_modifier.isstring)))));
+}
+
+/* Return 1 if there is no conflict in 80bit size
+   between operand GIVEN and operand WANTED for instruction template T.  */
+
+static INLINE int
+match_fp_size (const insn_template *t, unsigned int wanted,
+		    unsigned int given)
+{
+  return !i.types[given].bitfield.tbyte
+	 || t->operand_types[wanted].bitfield.tbyte;
 }
 
 /* Return 1 if there is no conflict in SIMD register between operand
-   GIVEN and opeand WANTED for instruction template T.  */
+   GIVEN and operand WANTED for instruction template T.  */
 
 static INLINE int
 match_simd_size (const insn_template *t, unsigned int wanted,
@@ -2554,13 +2563,15 @@ match_simd_size (const insn_template *t,
 }
 
 /* Return 1 if there is no conflict in any size between operand GIVEN
-   and opeand WANTED for instruction template T.  */
+   and operand WANTED for instruction template T.  */
 
 static INLINE int
 match_mem_size (const insn_template *t, unsigned int wanted,
 		unsigned int given)
 {
   return (match_operand_size (t, wanted, given)
+	  && (!i.types[given].bitfield.tbyte
+	      || t->operand_types[wanted].bitfield.tbyte)
 	  && !((i.types[given].bitfield.unspecified
 		&& !i.broadcast.type
 		&& !i.broadcast.bytes
@@ -2610,14 +2621,23 @@ operand_size_match (const insn_template
 		  && (t->operand_types[j].bitfield.byte
 		      || t->operand_types[j].bitfield.word
 		      || t->operand_types[j].bitfield.dword
-		      || t->operand_types[j].bitfield.qword
-		      || t->operand_types[j].bitfield.tbyte)))
+		      || t->operand_types[j].bitfield.qword)))
 	  && !match_operand_size (t, j, j))
 	{
 	  match = 0;
 	  break;
 	}
 
+      if (i.types[j].bitfield.class == RegFP
+	  && (t->operand_types[j].bitfield.class == RegFP
+	      || (t->operand_types[j].bitfield.instance == Accum
+		  && t->operand_types[j].bitfield.tbyte))
+	  && !match_fp_size (t, j, j))
+	{
+	  match = 0;
+	  break;
+	}
+
       if (i.types[j].bitfield.class == RegSIMD
 	  && (t->operand_types[j].bitfield.class == RegSIMD
 	      || (t->operand_types[j].bitfield.instance == Accum
@@ -2667,6 +2687,13 @@ operand_size_match (const insn_template
 	  && !match_operand_size (t, j, given))
 	return match;
 
+      if (i.types[given].bitfield.class == RegFP
+	  && (t->operand_types[j].bitfield.class == RegFP
+	      || (t->operand_types[j].bitfield.instance == Accum
+		  && t->operand_types[j].bitfield.tbyte))
+	  && !match_fp_size (t, j, given))
+	return match;
+
       /* No need to check for Accum here: There are no such templates with D
 	 set.  */
       if (i.types[given].bitfield.class == RegSIMD
@@ -3602,14 +3629,14 @@ md_begin (void)
 		if (regtab->reg_type.bitfield.instance == Accum)
 		  reg_eax = regtab;
 	      }
-	    else if (regtab->reg_type.bitfield.tbyte)
-	      {
-		/* There's no point inserting st(<N>) in the hash table, as
-		   parentheses aren't included in register_chars[] anyway.  */
-		if (regtab->reg_type.bitfield.instance != Accum)
-		  continue;
-		reg_st0 = regtab;
-	      }
+	    break;
+
+	  case RegFP:
+	    /* There's no point inserting st(<N>) in the hash table, as
+	       parentheses aren't included in register_chars[] anyway.  */
+	    if (regtab->reg_type.bitfield.instance != Accum)
+	      continue;
+	    reg_st0 = regtab;
 	    break;
 
 	  case SReg:
@@ -3744,6 +3771,7 @@ pi (const char *line, i386_insn *x)
       pt (x->types[j]);
       fprintf (stdout, "\n");
       if (x->types[j].bitfield.class == Reg
+	  || x->types[j].bitfield.class == RegFP
 	  || x->types[j].bitfield.class == RegMMX
 	  || x->types[j].bitfield.class == RegSIMD
 	  || x->types[j].bitfield.class == RegMask
@@ -3852,7 +3880,7 @@ const type_names[] =
   { { .bitfield = { .class = RegCR } }, "control reg" },
   { { .bitfield = { .class = RegTR } }, "test reg" },
   { { .bitfield = { .class = RegDR } }, "debug reg" },
-  { { .bitfield = { .class = Reg, .tbyte = 1 } }, "FReg" },
+  { { .bitfield = { .class = RegFP, .tbyte = 1 } }, "FReg" },
   { { .bitfield = { .instance = Accum, .tbyte = 1 } }, "FAcc" },
   { { .bitfield = { .class = SReg } }, "SReg" },
   { { .bitfield = { .class = RegMMX } }, "rMMX" },
--- a/gas/testsuite/gas/i386/inval-type.l
+++ b/gas/testsuite/gas/i386/inval-type.l
@@ -4,7 +4,10 @@
 .*: Error: operand type mismatch for .bsf.
 .*: Error: operand type mismatch for .bswap.
 .*: Error: operand type mismatch for .bswap.
+.*: Error: operand type mismatch for .bswap.
+.*: Error: operand type mismatch for .fcomi.
 .*: Error: operand type mismatch for .fcomi.
 .*: Error: operand type mismatch for .in.
+.*: Error: operand type mismatch for .in.
 .*: Error: operand type mismatch for .movntdqa.
 .*: Error: operand type mismatch for .movntdq.
--- a/gas/testsuite/gas/i386/inval-type.s
+++ b/gas/testsuite/gas/i386/inval-type.s
@@ -6,9 +6,12 @@
 	blendvps %eax, %xmm1, %xmm2
 	blendvps %st, %xmm1, %xmm2
 	bsf	%eax, (%eax)
+	bswap	%st
 	bswap	%mm0
 	bswapl	%xmm0
+	fcomi	%st(1), %eax
 	fcomi	%st(1), %xmm0
+	inl	$0, %st
 	inl	$0, %xmm0
 	movntdqa %xmm0, (%eax)
 	movntdq	(%eax), %xmm0
--- a/opcodes/i386-gen.c
+++ b/opcodes/i386-gen.c
@@ -540,6 +540,7 @@ static const struct {
 } operand_classes[] = {
   CLASS (Reg),
   CLASS (SReg),
+  CLASS (RegFP),
   CLASS (RegCR),
   CLASS (RegDR),
   CLASS (RegTR),
--- a/opcodes/i386-opc.h
+++ b/opcodes/i386-opc.h
@@ -863,8 +863,9 @@ typedef struct i386_opcode_modifier
 enum operand_class
 {
   ClassNone,
-  Reg, /* GPRs and FP regs, distinguished by operand size */
+  Reg, /* GPRs, distinguished by operand size */
   SReg, /* Segment register */
+  RegFP, /* FP regs */
   RegCR, /* Control register */
   RegDR, /* Debug register */
   RegTR, /* Test register */
--- a/opcodes/i386-opc.tbl
+++ b/opcodes/i386-opc.tbl
@@ -44,7 +44,7 @@
 #define InOutPortReg RegD|Word
 
 #define FloatAcc Acc|Tbyte
-#define FloatReg Class=Reg|Tbyte
+#define FloatReg Class=RegFP|Tbyte
 
 #define SReg  Class=SReg
 
--- a/opcodes/i386-reg.tbl
+++ b/opcodes/i386-reg.tbl
@@ -367,14 +367,14 @@ eip, Dword, RegRex64, RegIP, 8, Dw2Inval
 riz, Qword|BaseIndex, RegRex64, RegIZ, Dw2Inval, Dw2Inval
 eiz, Dword|BaseIndex, 0, RegIZ, Dw2Inval, Dw2Inval
 // fp regs. No need for an explicit st(0) here.
-st, Class=Reg|Instance=Accum|Tbyte, 0, 0, 11, 33
-st(1), Class=Reg|Tbyte, 0, 1, 12, 34
-st(2), Class=Reg|Tbyte, 0, 2, 13, 35
-st(3), Class=Reg|Tbyte, 0, 3, 14, 36
-st(4), Class=Reg|Tbyte, 0, 4, 15, 37
-st(5), Class=Reg|Tbyte, 0, 5, 16, 38
-st(6), Class=Reg|Tbyte, 0, 6, 17, 39
-st(7), Class=Reg|Tbyte, 0, 7, 18, 40
+st, Class=RegFP|Instance=Accum|Tbyte, 0, 0, 11, 33
+st(1), Class=RegFP|Tbyte, 0, 1, 12, 34
+st(2), Class=RegFP|Tbyte, 0, 2, 13, 35
+st(3), Class=RegFP|Tbyte, 0, 3, 14, 36
+st(4), Class=RegFP|Tbyte, 0, 4, 15, 37
+st(5), Class=RegFP|Tbyte, 0, 5, 16, 38
+st(6), Class=RegFP|Tbyte, 0, 6, 17, 39
+st(7), Class=RegFP|Tbyte, 0, 7, 18, 40
 // Pseudo-register names only used in .cfi_* directives
 eflags, 0, 0, 0, 9, 49
 rflags, 0, 0, 0, Dw2Inval, 49



More information about the Binutils mailing list