[binutils-gdb] x86: optimize XCHG to MOV for same-register forms

Jan Beulich via Binutils-cvs <[email protected]> Fri, 19 Jun 2026 07:49:26 +0000 (GMT)
Newsgroups gmane.comp.gnu.binutils.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=1c3c3e4b3c2ac2eed9abcbce0b9cba1be10ed3f0

commit 1c3c3e4b3c2ac2eed9abcbce0b9cba1be10ed3f0
Author: Jan Beulich <[email protected]>
Date:   Fri Jun 19 09:47:21 2026 +0200

    x86: optimize XCHG to MOV for same-register forms
    
    MOV requires far less execution resources.

Diff:
---
 gas/config/tc-i386.c                        | 11 +++++++++++
 gas/testsuite/gas/i386/optimize-2.d         |  3 +++
 gas/testsuite/gas/i386/optimize-2.s         |  4 ++++
 gas/testsuite/gas/i386/optimize-2b.d        |  3 +++
 gas/testsuite/gas/i386/x86-64-optimize-3.d  |  4 ++++
 gas/testsuite/gas/i386/x86-64-optimize-3.s  |  5 +++++
 gas/testsuite/gas/i386/x86-64-optimize-3b.d |  4 ++++
 opcodes/i386-opc.tbl                        |  2 +-
 opcodes/i386-tbl.h                          |  2 +-
 9 files changed, 36 insertions(+), 2 deletions(-)

diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 365c2ee95f5..d58c375c8d8 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -5176,6 +5176,17 @@ optimize_encoding (void)
       i.seg[0] = NULL;
     }
 
+  if (!optimize_for_space
+      && i.tm.mnem_off == MN_xchg
+      && i.reg_operands == 2
+      && i.op[0].regs == i.op[1].regs)
+    {
+      /* Optimize: -O:
+	   xchg %rN, %rN     -> mov %rN, %rN
+       */
+      i.tm.base_opcode = pp.dir_encoding == dir_encoding_load ? 0x8a : 0x88;
+    }
+
   if (((i.tm.opcode_space == SPACE_0F
         && (i.tm.base_opcode | 1) == 0xbf
         && (i.types[0].bitfield.byte
diff --git a/gas/testsuite/gas/i386/optimize-2.d b/gas/testsuite/gas/i386/optimize-2.d
index 3c90cc9b178..51f117966be 100644
--- a/gas/testsuite/gas/i386/optimize-2.d
+++ b/gas/testsuite/gas/i386/optimize-2.d
@@ -24,6 +24,9 @@ Disassembly of section .text:
  +[a-f0-9]+:	09 f6                	or     %esi,%esi
  +[a-f0-9]+:	87 0a                	xchg   %ecx,\(%edx\)
  +[a-f0-9]+:	87 11                	xchg   %edx,\(%ecx\)
+ +[a-f0-9]+:	86 c9                	xchg   %cl,%cl
+ +[a-f0-9]+:	66 87 d2             	xchg   %dx,%dx
+ +[a-f0-9]+:	87 ff                	xchg   %edi,%edi
  +[a-f0-9]+:	66 98                	cbtw
  +[a-f0-9]+:	66 98                	cbtw
  +[a-f0-9]+:	98                   	cwtl
diff --git a/gas/testsuite/gas/i386/optimize-2.s b/gas/testsuite/gas/i386/optimize-2.s
index 80a46eab485..102144592b1 100644
--- a/gas/testsuite/gas/i386/optimize-2.s
+++ b/gas/testsuite/gas/i386/optimize-2.s
@@ -22,6 +22,10 @@ _start:
 	lock xchg %ecx, (%edx)
 	lock xchg (%ecx), %edx
 
+	xchg	%cl, %cl
+	{load} xchg %dx, %dx
+	xchg	%edi, %edi
+
 	movsb	%al, %ax
 	movsbw	%al, %ax
 
diff --git a/gas/testsuite/gas/i386/optimize-2b.d b/gas/testsuite/gas/i386/optimize-2b.d
index 3e92acfd869..359d4a097a0 100644
--- a/gas/testsuite/gas/i386/optimize-2b.d
+++ b/gas/testsuite/gas/i386/optimize-2b.d
@@ -25,6 +25,9 @@ Disassembly of section .text:
  +[a-f0-9]+:	85 f6                	test   %esi,%esi
  +[a-f0-9]+:	87 0a                	xchg   %ecx,\(%edx\)
  +[a-f0-9]+:	87 11                	xchg   %edx,\(%ecx\)
+ +[a-f0-9]+:	88 c9                	mov    %cl,%cl
+ +[a-f0-9]+:	66 8b d2             	mov    %dx,%dx
+ +[a-f0-9]+:	89 ff                	mov    %edi,%edi
  +[a-f0-9]+:	66 98                	cbtw
  +[a-f0-9]+:	66 98                	cbtw
  +[a-f0-9]+:	98                   	cwtl
diff --git a/gas/testsuite/gas/i386/x86-64-optimize-3.d b/gas/testsuite/gas/i386/x86-64-optimize-3.d
index c91503aa8f9..20d3d550041 100644
--- a/gas/testsuite/gas/i386/x86-64-optimize-3.d
+++ b/gas/testsuite/gas/i386/x86-64-optimize-3.d
@@ -80,6 +80,10 @@ Disassembly of section .text:
  +[a-f0-9]+:	66 09 f6             	or     %si,%si
  +[a-f0-9]+:	09 ff                	or     %edi,%edi
  +[a-f0-9]+:	4d 09 c0             	or     %r8,%r8
+ +[a-f0-9]+:	d5 50 86 c9          	xchg   %r17b,%r17b
+ +[a-f0-9]+:	66 45 87 c0          	xchg   %r8w,%r8w
+ +[a-f0-9]+:	87 c0                	xchg   %eax,%eax
+ +[a-f0-9]+:	4d 87 ff             	xchg   %r15,%r15
  +[a-f0-9]+:	66 98                	cbtw
  +[a-f0-9]+:	66 98                	cbtw
  +[a-f0-9]+:	98                   	cwtl
diff --git a/gas/testsuite/gas/i386/x86-64-optimize-3.s b/gas/testsuite/gas/i386/x86-64-optimize-3.s
index 4fd29aeec98..1b61d017e7b 100644
--- a/gas/testsuite/gas/i386/x86-64-optimize-3.s
+++ b/gas/testsuite/gas/i386/x86-64-optimize-3.s
@@ -39,6 +39,11 @@ _start:
 	or	%edi, %edi
 	or	%r8, %r8
 
+	xchg	%r17b, %r17b
+	{load} xchg %r8w, %r8w
+	xchg	%eax, %eax
+	xchg	%r15, %r15
+
 	movsb	%al, %ax
 	movsbw	%al, %ax
 
diff --git a/gas/testsuite/gas/i386/x86-64-optimize-3b.d b/gas/testsuite/gas/i386/x86-64-optimize-3b.d
index 2a8fd3a0f41..e7d211ed74c 100644
--- a/gas/testsuite/gas/i386/x86-64-optimize-3b.d
+++ b/gas/testsuite/gas/i386/x86-64-optimize-3b.d
@@ -81,6 +81,10 @@ Disassembly of section .text:
  +[a-f0-9]+:	66 85 f6             	test   %si,%si
  +[a-f0-9]+:	09 ff                	or     %edi,%edi
  +[a-f0-9]+:	4d 85 c0             	test   %r8,%r8
+ +[a-f0-9]+:	d5 50 88 c9          	mov    %r17b,%r17b
+ +[a-f0-9]+:	66 45 8b c0          	mov    %r8w,%r8w
+ +[a-f0-9]+:	89 c0                	mov    %eax,%eax
+ +[a-f0-9]+:	4d 89 ff             	mov    %r15,%r15
  +[a-f0-9]+:	66 98                	cbtw
  +[a-f0-9]+:	66 98                	cbtw
  +[a-f0-9]+:	98                   	cwtl
diff --git a/opcodes/i386-opc.tbl b/opcodes/i386-opc.tbl
index 5797a634de3..bb547bd2ee7 100644
--- a/opcodes/i386-opc.tbl
+++ b/opcodes/i386-opc.tbl
@@ -269,7 +269,7 @@ popa, 0x61, i186&No64, ImplicitStackOp|DefaultSize|No_bSuf|No_sSuf|No_qSuf|Intel
 
 // In the 64bit code, xchg rax, rax is reused for new nop instruction.
 xchg, 0x90, 0, D|C|CheckOperandSize|No_bSuf|No_sSuf, { Reg16|Reg32|Reg64, Acc|Word|Dword|Qword }
-xchg, 0x86, 0, D|W|C|CheckOperandSize|Modrm|No_sSuf|HLEPrefixAny, { Reg8|Reg16|Reg32|Reg64, Reg8|Reg16|Reg32|Reg64|Unspecified|BaseIndex }
+xchg, 0x86, 0, D|W|C|CheckOperandSize|Modrm|No_sSuf|HLEPrefixAny|Optimize, { Reg8|Reg16|Reg32|Reg64, Reg8|Reg16|Reg32|Reg64|Unspecified|BaseIndex }
 
 // In/out from ports.
 in, 0xe4, 0, W|No_sSuf|No_qSuf, { Imm8, Acc|Byte|Word|Dword }
diff --git a/opcodes/i386-tbl.h b/opcodes/i386-tbl.h
index da96efc6fda..f1ecc6498af 100644
--- a/opcodes/i386-tbl.h
+++ b/opcodes/i386-tbl.h
@@ -494,7 +494,7 @@ static const insn_template i386_optab[] =
 	  0, 0, 0, 0, 0, 0 } } } },
   { MN_xchg, 0x86, 2, SPACE_BASE, None,
     { 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 7,
-      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,
       0, 0 },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
     { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },