[llvm] [X86][CostModel] Free a clean narrow zext used as a GEP index (PR #216256)

Simon Pilgrim via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 14 00:40:33 PDT 2026


================
@@ -2429,6 +2431,39 @@ InstructionCost X86TTIImpl::getCastInstrCost(unsigned Opcode, Type *Dst,
   int ISD = TLI->InstructionOpcodeToISD(Opcode);
   assert(ISD && "Invalid opcode");
 
+  // A narrow (i8/i16) zero-extension used as a GEP *index* can be folded into
+  // the addressing mode of the consuming memory op, but only if the source is
+  // already materialised zero-extended in a full register. X86's SIB form
+  // [base + index*scale + disp] reads the index at full width and does NOT
+  // zero-extend a narrow index (unlike AArch64's uxtw-extended addressing), so
+  // a "dirty" narrow source (e.g. an i16 add result used only as an index)
+  // still needs a dedicated movzx and is not free. Price it as free only with
+  // positive evidence that no movzx is required.
+  if (ISD == ISD::ZERO_EXTEND && I && I->hasOneUse() && Src->isIntegerTy() &&
+      Src->getScalarSizeInBits() < 32) {
+    const Use &U = *I->use_begin();
+    if (isa<GetElementPtrInst>(U.getUser()) &&
+        U.getOperandNo() != GetElementPtrInst::getPointerOperandIndex()) {
+      const Value *Op = I->getOperand(0);
+      // Clean sources: an extending load, a zeroext argument, or a value whose
+      // high bits are provably zero (e.g. from a shift/mask). These mirror the
+      // proof-based reasoning the middle end uses elsewhere (ValueTracking and
+      // InstCombine's canEvaluateZExtd); we intentionally do NOT treat a merely
+      // multiply-used operand as clean, since that is a guess rather than
+      // proof.
+      bool CleanSource = isa<LoadInst>(Op);
+      if (!CleanSource)
+        if (const auto *A = dyn_cast<Argument>(Op))
+          CleanSource = A->hasAttribute(Attribute::ZExt);
+      if (!CleanSource)
+        CleanSource =
+            computeKnownBits(Op, I->getDataLayout(), /*AC=*/nullptr, I)
+                .countMinLeadingZeros() > 0;
+      if (CleanSource)
+        return 0;
----------------
RKSimon wrote:

(style) you're better off doing early returns:
```
if (isa<LoadInst>(Op))
  return TTI::TCC_Free;
if (const auto *A = dyn_cast<Argument>(Op))
   if (A->hasAttribute(Attribute::ZExt))
     return TTI::TCC_Free;
......
```

https://github.com/llvm/llvm-project/pull/216256


More information about the llvm-commits mailing list