[llvm] [X86] Optimize (a & 0xff) | ((b & 0xff) << 8) into high-byte register insertions (PR #214645)

via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 7 08:01:21 PDT 2026


https://github.com/AZero13 updated https://github.com/llvm/llvm-project/pull/214645

>From eb4a27eed1b0a16043273371fa28374ad86dfa9d Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 7 Aug 2026 01:24:04 -0400
Subject: [PATCH 1/2] Pre-commit test (NFC)

---
 llvm/test/CodeGen/X86/h-register-insert.ll | 73 ++++++++++++++++++++++
 1 file changed, 73 insertions(+)
 create mode 100644 llvm/test/CodeGen/X86/h-register-insert.ll

diff --git a/llvm/test/CodeGen/X86/h-register-insert.ll b/llvm/test/CodeGen/X86/h-register-insert.ll
new file mode 100644
index 0000000000000..77c1d8d6ffa24
--- /dev/null
+++ b/llvm/test/CodeGen/X86/h-register-insert.ll
@@ -0,0 +1,73 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc < %s -mtriple=i686-unknown-unknown | FileCheck %s --check-prefix=X86
+; RUN: llc < %s -mtriple=x86_64-unknown-unknown | FileCheck %s --check-prefix=X64
+
+define i32 @test_insert_32(i8 inreg %A, i8 inreg %B) {
+; X86-LABEL: test_insert_32:
+; X86:       # %bb.0:
+; X86-NEXT:    movzbl %al, %ecx
+; X86-NEXT:    movzbl %dl, %eax
+; X86-NEXT:    shll $8, %eax
+; X86-NEXT:    orl %ecx, %eax
+; X86-NEXT:    retl
+;
+; X64-LABEL: test_insert_32:
+; X64:       # %bb.0:
+; X64-NEXT:    movzbl %dil, %ecx
+; X64-NEXT:    movzbl %sil, %eax
+; X64-NEXT:    shll $8, %eax
+; X64-NEXT:    orl %ecx, %eax
+; X64-NEXT:    retq
+  %conv = zext i8 %A to i32
+  %conv1 = zext i8 %B to i32
+  %shl = shl nuw nsw i32 %conv1, 8
+  %or = or disjoint i32 %shl, %conv
+  ret i32 %or
+}
+
+define i16 @test_insert_16(i8 inreg %A, i8 inreg %B) {
+; X86-LABEL: test_insert_16:
+; X86:       # %bb.0:
+; X86-NEXT:    movzbl %al, %eax
+; X86-NEXT:    shll $8, %edx
+; X86-NEXT:    orl %edx, %eax
+; X86-NEXT:    # kill: def $ax killed $ax killed $eax
+; X86-NEXT:    retl
+;
+; X64-LABEL: test_insert_16:
+; X64:       # %bb.0:
+; X64-NEXT:    movzbl %dil, %eax
+; X64-NEXT:    shll $8, %esi
+; X64-NEXT:    orl %esi, %eax
+; X64-NEXT:    # kill: def $ax killed $ax killed $eax
+; X64-NEXT:    retq
+  %conv = zext i8 %A to i16
+  %conv1 = zext i8 %B to i16
+  %shl = shl nuw nsw i16 %conv1, 8
+  %or = or disjoint i16 %shl, %conv
+  ret i16 %or
+}
+
+define i64 @test_insert_64(i8 inreg %A, i8 inreg %B) {
+; X86-LABEL: test_insert_64:
+; X86:       # %bb.0:
+; X86-NEXT:    movzbl %al, %ecx
+; X86-NEXT:    movzbl %dl, %eax
+; X86-NEXT:    shll $8, %eax
+; X86-NEXT:    orl %ecx, %eax
+; X86-NEXT:    xorl %edx, %edx
+; X86-NEXT:    retl
+;
+; X64-LABEL: test_insert_64:
+; X64:       # %bb.0:
+; X64-NEXT:    movzbl %dil, %ecx
+; X64-NEXT:    movzbl %sil, %eax
+; X64-NEXT:    shll $8, %eax
+; X64-NEXT:    orl %ecx, %eax
+; X64-NEXT:    retq
+  %conv = zext i8 %A to i64
+  %conv1 = zext i8 %B to i64
+  %shl = shl nuw nsw i64 %conv1, 8
+  %or = or disjoint i64 %shl, %conv
+  ret i64 %or
+}

>From 01ed66ecae742df86851f8b1fcc3e083426130c2 Mon Sep 17 00:00:00 2001
From: AZero13 <gfunni234 at gmail.com>
Date: Fri, 7 Aug 2026 02:05:48 -0400
Subject: [PATCH 2/2] [X86] Optimize (a & 0xff) | ((b & 0xff) << 8) into
 high-byte register insertions

This had to be done in dagtodag because tablegen would not work.
---
 llvm/lib/Target/X86/X86ISelDAGToDAG.cpp    | 121 +++++++++++++++++++++
 llvm/test/CodeGen/X86/h-register-insert.ll |  37 +++++--
 2 files changed, 148 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
index d078117061677..072275ad3f5aa 100644
--- a/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
+++ b/llvm/lib/Target/X86/X86ISelDAGToDAG.cpp
@@ -599,6 +599,7 @@ namespace {
                         uint8_t Imm);
     bool tryVPTESTM(SDNode *Root, SDValue Setcc, SDValue Mask);
     bool tryMatchBitSelect(SDNode *N);
+    bool tryMatchHRegisterInsert(SDNode *N);
 
     MachineSDNode *emitPCMPISTR(unsigned ROpc, unsigned MOpc, bool MayFoldLoad,
                                 const SDLoc &dl, MVT VT, SDNode *Node);
@@ -5403,6 +5404,124 @@ bool X86DAGToDAGISel::tryMatchBitSelect(SDNode *N) {
                         Ternlog.getNode(), A, B, C, 0xCA);
 }
 
+bool X86DAGToDAGISel::tryMatchHRegisterInsert(SDNode *N) {
+  if (Subtarget->is64Bit())
+    return false;
+
+  if (N->getOpcode() != ISD::OR)
+    return false;
+  MVT VT = N->getSimpleValueType(0);
+  // On 32-bit x86, i64 is expanded to two i32 halves and i16 is promoted to i32
+  // during type legalization. So we only ever see i32 here.
+  if (VT != MVT::i32)
+    return false;
+
+  SDValue LHS = N->getOperand(0);
+  SDValue RHS = N->getOperand(1);
+
+  bool ZeroExtendLo = false;
+  SDValue LoBase;
+  auto isLoBase = [&](SDValue V) {
+    if (V.getOpcode() == ISD::AND) {
+      auto *C = dyn_cast<ConstantSDNode>(V.getOperand(1));
+      if (!C)
+        return false;
+      uint64_t Mask = C->getZExtValue();
+      if (Mask == 0xFF) {
+        ZeroExtendLo = true;
+        LoBase = V.getOperand(0);
+        return true;
+      }
+      unsigned BitWidth = V.getValueSizeInBits();
+      uint64_t NotFF00 = (~0xFF00ULL) & maskTrailingOnes<uint64_t>(BitWidth);
+      if (Mask == NotFF00) {
+        ZeroExtendLo = false;
+        LoBase = V.getOperand(0);
+        return true;
+      }
+    } else if (V.getOpcode() == ISD::ZERO_EXTEND && V.getOperand(0).getValueType() == MVT::i8) {
+      ZeroExtendLo = false;
+      LoBase = V;
+      return true;
+    } else if (V.getOpcode() == ISD::ANY_EXTEND && V.getOperand(0).getValueType() == MVT::i8) {
+      ZeroExtendLo = true;
+      LoBase = V.getOperand(0);
+      return true;
+    }
+    return false;
+  };
+
+  SDValue HiBase;
+  auto isShl8 = [&](SDValue V) {
+    if (V.getOpcode() != ISD::SHL)
+      return false;
+    auto *C = dyn_cast<ConstantSDNode>(V.getOperand(1));
+    if (!C || C->getZExtValue() != 8)
+      return false;
+    SDValue Src = V.getOperand(0);
+    if (Src.getOpcode() == ISD::AND) {
+      if (auto *Mask = dyn_cast<ConstantSDNode>(Src.getOperand(1))) {
+        if (Mask->getZExtValue() == 255)
+          Src = Src.getOperand(0);
+      }
+    } else if (Src.getOpcode() == ISD::ZERO_EXTEND || Src.getOpcode() == ISD::ANY_EXTEND) {
+      if (Src.getOperand(0).getValueType() == MVT::i8)
+        Src = Src.getOperand(0);
+    }
+    // Make sure the shifted value has only one use, so we don't duplicate work.
+    if (!V.hasOneUse())
+      return false;
+    HiBase = Src;
+    return true;
+  };
+
+  if (isLoBase(LHS) && isShl8(RHS)) {
+  } else if (isLoBase(RHS) && isShl8(LHS)) {
+  } else {
+    return false;
+  }
+
+  SDLoc dl(N);
+  SDValue Sub8 = CurDAG->getTargetConstant(X86::sub_8bit, dl, MVT::i32);
+  SDValue RC_ABCD = CurDAG->getTargetConstant(X86::GR32_ABCDRegClassID, dl, MVT::i32);
+
+  SDValue DestBase;
+  if (ZeroExtendLo) {
+    SDValue LoExtr;
+    if (LoBase.getValueType() == MVT::i8) {
+      LoExtr = LoBase;
+    } else {
+      LoExtr = SDValue(CurDAG->getMachineNode(TargetOpcode::EXTRACT_SUBREG, dl,
+                                              MVT::i8, LoBase, Sub8), 0);
+    }
+    DestBase = SDValue(
+        CurDAG->getMachineNode(X86::MOVZX32rr8, dl, MVT::i32, LoExtr), 0);
+    DestBase =
+        SDValue(CurDAG->getMachineNode(TargetOpcode::COPY_TO_REGCLASS, dl,
+                                       MVT::i32, DestBase, RC_ABCD),
+                0);
+  } else {
+    DestBase = SDValue(CurDAG->getMachineNode(TargetOpcode::COPY_TO_REGCLASS,
+                                              dl, MVT::i32, LoBase, RC_ABCD),
+                       0);
+  }
+
+  SDValue HiExtr1;
+  if (HiBase.getValueType() == MVT::i8) {
+    HiExtr1 = HiBase;
+  } else {
+    HiExtr1 = SDValue(CurDAG->getMachineNode(TargetOpcode::EXTRACT_SUBREG,
+                                             dl, MVT::i8, HiBase, Sub8), 0);
+  }
+
+  SDValue Sub8Hi = CurDAG->getTargetConstant(X86::sub_8bit_hi, dl, MVT::i32);
+  SDNode *Insert = CurDAG->getMachineNode(TargetOpcode::INSERT_SUBREG, dl, VT,
+                                          DestBase, HiExtr1, Sub8Hi);
+
+  ReplaceNode(N, Insert);
+  return true;
+}
+
 void X86DAGToDAGISel::Select(SDNode *Node) {
   MVT NVT = Node->getSimpleValueType(0);
   unsigned Opcode = Node->getOpcode();
@@ -5715,6 +5834,8 @@ void X86DAGToDAGISel::Select(SDNode *Node) {
       return;
     if (Opcode == ISD::OR && tryMatchBitSelect(Node))
       return;
+    if (Opcode == ISD::OR && tryMatchHRegisterInsert(Node))
+      return;
     if (tryVPTERNLOG(Node))
       return;
 
diff --git a/llvm/test/CodeGen/X86/h-register-insert.ll b/llvm/test/CodeGen/X86/h-register-insert.ll
index 77c1d8d6ffa24..a5d8d825d1560 100644
--- a/llvm/test/CodeGen/X86/h-register-insert.ll
+++ b/llvm/test/CodeGen/X86/h-register-insert.ll
@@ -5,10 +5,8 @@
 define i32 @test_insert_32(i8 inreg %A, i8 inreg %B) {
 ; X86-LABEL: test_insert_32:
 ; X86:       # %bb.0:
-; X86-NEXT:    movzbl %al, %ecx
-; X86-NEXT:    movzbl %dl, %eax
-; X86-NEXT:    shll $8, %eax
-; X86-NEXT:    orl %ecx, %eax
+; X86-NEXT:    movzbl %al, %eax
+; X86-NEXT:    movb %dl, %ah
 ; X86-NEXT:    retl
 ;
 ; X64-LABEL: test_insert_32:
@@ -29,8 +27,7 @@ define i16 @test_insert_16(i8 inreg %A, i8 inreg %B) {
 ; X86-LABEL: test_insert_16:
 ; X86:       # %bb.0:
 ; X86-NEXT:    movzbl %al, %eax
-; X86-NEXT:    shll $8, %edx
-; X86-NEXT:    orl %edx, %eax
+; X86-NEXT:    movb %dl, %ah
 ; X86-NEXT:    # kill: def $ax killed $ax killed $eax
 ; X86-NEXT:    retl
 ;
@@ -51,10 +48,8 @@ define i16 @test_insert_16(i8 inreg %A, i8 inreg %B) {
 define i64 @test_insert_64(i8 inreg %A, i8 inreg %B) {
 ; X86-LABEL: test_insert_64:
 ; X86:       # %bb.0:
-; X86-NEXT:    movzbl %al, %ecx
-; X86-NEXT:    movzbl %dl, %eax
-; X86-NEXT:    shll $8, %eax
-; X86-NEXT:    orl %ecx, %eax
+; X86-NEXT:    movzbl %al, %eax
+; X86-NEXT:    movb %dl, %ah
 ; X86-NEXT:    xorl %edx, %edx
 ; X86-NEXT:    retl
 ;
@@ -71,3 +66,25 @@ define i64 @test_insert_64(i8 inreg %A, i8 inreg %B) {
   %or = or disjoint i64 %shl, %conv
   ret i64 %or
 }
+
+define i32 @test_insert_mask_32(i32 %a, i8 inreg %b) {
+; X86-LABEL: test_insert_mask_32:
+; X86:       # %bb.0:
+; X86-NEXT:    movl {{[0-9]+}}(%esp), %ecx
+; X86-NEXT:    movb %al, %ch
+; X86-NEXT:    movl %ecx, %eax
+; X86-NEXT:    retl
+;
+; X64-LABEL: test_insert_mask_32:
+; X64:       # %bb.0:
+; X64-NEXT:    andl $-65281, %edi # imm = 0xFFFF00FF
+; X64-NEXT:    movzbl %sil, %eax
+; X64-NEXT:    shll $8, %eax
+; X64-NEXT:    orl %edi, %eax
+; X64-NEXT:    retq
+  %mask = and i32 %a, 4294902015 ; 0xFFFF00FF
+  %conv = zext i8 %b to i32
+  %shl = shl nuw nsw i32 %conv, 8
+  %or = or disjoint i32 %mask, %shl
+  ret i32 %or
+}



More information about the llvm-commits mailing list