@@ -49,13 +49,74 @@ static std::optional<Instruction *> foldSignExtendToConst(InstCombiner &IC,
4949
5050static std::optional<Instruction *> instCombineSignExtend (InstCombiner &IC,
5151 IntrinsicInst &II) {
52+ constexpr unsigned BitWidth = 256 ;
53+ if (!II.getType ()->isIntegerTy (BitWidth))
54+ return std::nullopt ;
55+
5256 // Fold signextend(b, signextend(b, x)) -> signextend(b, x)
5357 Value *B = nullptr , *X = nullptr ;
5458 if (match (&II, m_Intrinsic<Intrinsic::evm_signextend>(
5559 m_Value (B), m_Intrinsic<Intrinsic::evm_signextend>(
5660 m_Deferred (B), m_Value (X)))))
5761 return IC.replaceInstUsesWith (II, II.getArgOperand (1 ));
5862
63+ // From now on, we only handle signextend with constant byte index.
64+ const auto *ByteIdxC = dyn_cast<ConstantInt>(II.getArgOperand (0 ));
65+ if (!ByteIdxC)
66+ return std::nullopt ;
67+
68+ // ByteIdx must be in range [0, 31].
69+ uint64_t ByteIdx = ByteIdxC->getZExtValue ();
70+ if (ByteIdx >= BitWidth / 8 )
71+ return std::nullopt ;
72+
73+ unsigned Width = (ByteIdx + 1 ) * 8 ;
74+
75+ // Fold signextend into shifts, if second operand or the result is shift
76+ // with constant. LLVM will fuse those shifts, and will replace signextend
77+ // with a shift, which is cheaper.
78+ if (match (II.getArgOperand (1 ),
79+ m_OneUse (m_Shift (m_Value (), m_ConstantInt ()))) ||
80+ (II.hasOneUse () &&
81+ match (II.user_back (), m_Shift (m_Specific (&II), m_ConstantInt ())))) {
82+ unsigned ShiftAmt = BitWidth - Width;
83+ auto *Shl = IC.Builder .CreateShl (II.getArgOperand (1 ), ShiftAmt);
84+ auto *Ashr = IC.Builder .CreateAShr (Shl, ShiftAmt);
85+ return IC.replaceInstUsesWith (II, Ashr);
86+ }
87+
88+ const APInt *AndVal = nullptr ;
89+ // Match signextend(b, and(x, C))
90+ if (match (II.getArgOperand (1 ), m_And (m_Value (X), m_APInt (AndVal)))) {
91+ APInt LowMask = APInt::getLowBitsSet (BitWidth, Width);
92+
93+ // signextend(b, x & C) -> signextend(b, x)
94+ // If and fully preservs low bits, we can drop it.
95+ if ((*AndVal & LowMask) == LowMask)
96+ return IC.replaceOperand (II, 1 , X);
97+
98+ // signextend(b, x & C) -> (x & C)
99+ // If and doesn't touch upper bits, and clears sign bit, we can drop
100+ // signextend.
101+ APInt SignBit = APInt (BitWidth, 1 ).shl (Width - 1 );
102+ if ((*AndVal & ~LowMask).isZero () && (*AndVal & SignBit).isZero ())
103+ return IC.replaceInstUsesWith (II, II.getArgOperand (1 ));
104+
105+ // signextend(b, x & C) -> 0
106+ // If and clears all low bits, result is always 0.
107+ if ((*AndVal & LowMask).isZero ())
108+ return IC.replaceInstUsesWith (II,
109+ ConstantInt::getNullValue (II.getType ()));
110+ }
111+
112+ // and(signextend(b, x), C) -> and(x, C)
113+ // If and doesn't touch upper bits, we can drop signextend.
114+ if (II.hasOneUse () &&
115+ match (II.user_back (), m_And (m_Specific (&II), m_APInt (AndVal)))) {
116+ APInt LowMask = APInt::getLowBitsSet (BitWidth, Width);
117+ if ((*AndVal & ~LowMask).isZero ())
118+ return IC.replaceInstUsesWith (II, II.getArgOperand (1 ));
119+ }
59120 return foldSignExtendToConst (IC, II);
60121}
61122
0 commit comments