Re: [PATCH v8 0/7] Recognize and fold longhand wide-multiplication idioms [PR107090]
Philipp Tomsich <[email protected]> Tue, 4 Aug 2026 19:01:09 +0200
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <CAAeLtUBJRovnmbFd9r3xhaZ_VHSgiG2YuLwXpxqpkNZ5s7T2BQ@mail.gmail.com> |
--000000000000a26aed06583b9ab9 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Reverted. On Tue, 4 Aug 2026 at 18:58, Andrea Pinski <[email protected]> wrote: > On Mon, Aug 3, 2026 at 8:57=E2=80=AFAM Konstantinos Eleftheriou > <[email protected]> wrote: > > > > > > This patch series teaches GCC to recognize longhand 64x64->128 > > wide-multiplication idioms and replace them with native multiply > > instructions: a widening multiply followed by a right shift for the > > high part, and a plain MULT_EXPR for the low part. > > > > Portable C/C++ code that needs a 128-bit product on a 64-bit target > > often resorts to a longhand decomposition: split operands into 32-bit > > halves, compute four partial products, and propagate carries manually. > > This pattern appears in a number of real-world codebases, including > > SPEC2026's 750.sealcrypto_r (seal/util/uintarith.h) and several > > examples from Hacker's Delight. Targets like AArch64 (mul/umulh) and > > x86-64 can compute the full 128-bit product in one or two instructions, > > but GCC does not currently fold the longhand sequence back to these. > > > > The recognizer emits the canonical widening shape > > > > (N)(((2N) a * (2N) b) >> N) > > > > for the high part and a plain MULT_EXPR for the low part. The high > > part is emitted as a shift of the wide product, not as a bare > > (2N) a * (2N) b: the `>> N' form is the MULT_HIGHPART idiom, so > > pass_optimize_widening_mul rewrites it to the target's native high-part > > multiply (e.g. umulh) without ever forming the full 2N product. A bare > > widening product would also compute the unwanted low half, and where 2N > > has no native multiply -- the 128x128 case, 2N =3D OImode, which the mo= de > > table carries but the target cannot expand -- there is no way to form > > it at all. For that case the final patch resynthesizes the longhand at > > narrow precision from (N/2)-wide partial products, so the fold never > > depends on a 2N multiply the target lacks. > > > > The series is split into seven patches: > > Two things, this breaks bootstrap on x86_64-linux-gnu. > I filed https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D126642 . > Can we revert this until that is fixed? > > Second please don't squash the patches into one commit next time. If > you do please send a full updated patch to the list of what you > committed. This has been GCC's policy. > > Thanks, > Andrea > > > > > > 1/7 forwprop: Match and fold the long-multiply carry form > > [PR107090] > > > > Adds the match.pd atom patterns and the forwprop framework: > > linearize the outer add/ior chain, classify each summand, and > > match the multiset against a table of decomposed variants. > > Carries the base carry form, a single overflow comparison on > > the cross-sum. Matching starts only at the end of a chain, and > > leaves that are not long-multiply summands are preserved and > > re-applied on top of the fold, so a chain that mixes the idiom > > with unrelated addends still folds. > > > > 2/7 - 5/7 Add the remaining recognized variants: carry-low-sum, > > two-carry, ladder, and low-plus. > > > > 6/7 match.pd, forwprop: Recognize long-multiply carries written as > > 2-arg PHI > > > > Hand-written code often writes the carry as a 2-arg PHI > > (`if (overflow) result +=3D pow2;`) with no top-level + at the > > result. Adds cond_carry_add / cond_carry_add_neg recognizers > > for that shape and a match_long_mul_phi entry that synthesizes > > the carry summand from the PHI bindings and reuses the table > > walk and emit path. > > > > 7/7 widening_mul: Lower long-multiply chains to inline longhand > > > > Lowers the high-part chain to a longhand high-part at narrow > > precision when the target has no expansion path for the 2N > > form, using (N/2)-by-(N/2)->N widening multiplies where the > > optab exists and plain N-bit multiplies otherwise. Operands > > that are themselves wider than N are split into N-bit halves > > rather than truncated, and a 2N product left with only low-half > > uses is narrowed to an N-bit multiply rather than reaching > > expansion. When an operand holds a product's high N bits, those > > come from the product's own operands instead of the 2N shift. > > Paired with pass_optimize_widening_mul so the chain is emitted > > only when the pass will run to rescue an unsupported 2N shape. > > > > On SPEC2026's 750.sealcrypto_r: > > > > - AArch64 Neoverse-N1: 25% improvement > > - x86-64 Zen4: 59% improvement > > > > Compile-time impact is negligible: recompiling gcc/*.cc > > (checking=3Dyes,extra) with the series compiler versus its base adds > > about 0.1% overall, confined to forwprop, where the recognizer runs > > and grows the pass by a few percent. > > > > Bootstrapped/regtested on AArch64, x86-64, ARM and PowerPC. > > > > Changes in v8: > > - 1/7 starts matching only at chain ends and sets aside leaves that are > > not long-multiply summands, re-applying them on top of the fold. The > > two go together. Either alone regresses a foldable chain. Now folds > > shapes such as `acc +=3D mulh (x, y)`. > > - 1/7 factors out build_mul_high_seq, long_mul_classify_match and > > long_mul_classify_chain for the PHI entry in 6/7 to reuse, and takes > > a gassign * in the matcher and the emitters. > > - The long_mul_high_chain atom binds each mult operand through > > `(convert? @X)`, so a PRE-hoisted cast into a PHI still matches. An > > operand wider than N is now split into N-bit halves rather than > > rejected, which previously left the 2N multiply for expansion. > > - optimize_widening_mul_active_p returns false for optimize_debug: -Og > > runs no widening_mul pass to lower the emitted chain, so the > > unexpandable multiply reached expand. > > - Add narrow_long_mul_low_half: when a 2N `res =3D a * b` has uses only= in > > its low N bits and the target cannot expand 2N, rewrite it to > > `res =3D (2N) ((N)a * (N)b)`. The split-based lowering covers a chain= ed > > 2N operand by recursing into it, but not a shared 2N product left wit= h > > only low-half uses, which is what ICEs libgo's p521_fiat64.go on ARM3= 2. > > - long_mul_split_operand resolves a 2N value shifted down by N to the > > high half of what was shifted, instead of truncating the shift. The > > truncation read the shift and so kept a chained product live past its > > own lowering, aborting expand_mult on a target without a 2N multiply. > > Reachable from Go on ARM32 as bits.Mul64 (bits.Mul64 (x, y), z), wher= e > > the unexpandable mode is TImode, and on aarch64 and x86-64 from a > > chained __int128 longhand, where it is OImode. > > - New coverage: near misses of the idiom, checked at runtime against > > their literal meaning, a signed narrow-cast operand on Thumb-1, and > > chained longhands through both halves on ARM32. > > > > Changes in v7: > > - Split the single long-multiply fold (was 1/2) into five patches: a > > base patch carrying the framework and the carry form, then one > > patch each for the carry-low-sum, two-carry, ladder and low-plus > > variants. Easier to review and to bisect a variant in isolation. > > The PHI-form recognition follows as 6/7, unchanged from v6's 2/2. > > - New 7/7: lower the emitted high-part chain to inline longhand at > > narrow precision when the target has no expansion path for the 2N > > form. v6 only emitted a HIGH_PART when the 2N scalar mode existed > > and skipped it otherwise; v7 emits it and pairs the recognizer with > > lower_long_mul_high_chain via optimize_widening_mul_active_p, so a > > 128x128 high part on a target whose mode table has OImode but no > > scalar OImode support is now built from (N/2)-wide partial products > > instead of a 2N multiply the target cannot expand. > > - Refuse the HIGH_PART emit and the chain lowering for BITINT_TYPE, > > keeping the recognizer and the lowering gate symmetric. > > - Add per-variant tree-ssa tests, 128-bit torture and runtime tests, > > arm thumb1 / umull inline tests, and a check_effective_target > > _oi_mode helper. > > > > Changes in v6: > > - Reorder so the long-multiply fold (was 2/2) is now 1/2 and a new > > PHI-form recognition pass is 2/2. Reverting 2/2 leaves a working > > long-multiply fold for the flat-shifted-compare carry form. > > - Drop v5's standalone flatten_cond_carry_add driver. The same > > cond_carry_add / cond_carry_add_neg match.pd recognizers now feed > > a match_long_mul_phi entry inside the long-multiply fold, so a > > PHI-shaped carry folds straight to the wide-multiply output. > > - Factor long_mul_classify_chain, long_mul_classify_match and > > build_mul_high_seq for sharing between match_long_mul and the new > > match_long_mul_phi. > > - cond_carry_add_neg uses le / ge instead of gt / lt to encode the > > carry condition strictly. v5 inverted the compare via > > invert_tree_comparison in the flatten driver; v6 synthesises the > > carry summand directly inside match_long_mul_phi and so requires > > the recogniser to encode the strict form. > > - Delete forwprop-44/45/46.c; add PHI-form coverage in > > long-mul-carry.c, long-mul-two-carry.c, long-mul-boundary.c > > and long-mul-boundary-64.c. > > - Add PHI-form near-miss tests in long-mul-partial.c and > > operand-swap polarity coverage in long-mul-boundary{,-64}.c. > > - Refresh stale long-mul comment references (check_hilo_and_ops, > > fold_mul_low_plus) and reword mul_carry_low's :c-on-gt note to > > the correct LT form (a + b < a). > > > > Changes in v5: > > - 1/2: > > - Replace the match.pd simplify on COND_EXPR with cond_carry_add > > / cond_carry_add_neg match recognizers (cond^), split by gcond > > polarity, plus a flatten_cond_carry_add driver in > > tree-ssa-forwprop.cc. The driver inverts the gcond's > > comparison for the _neg form. Modelled on match_saturation_add. > > - Remove fold_cond_carry_add_profitable_p and the tm_p.h / > > predict.h includes from gimple-match-head.cc. The width > > > MAX_FIXED_MODE_SIZE and width % 2 !=3D 0 guards were > > prerequisites for the can_mult_highpart_p fallback path, not > > soundness checks. type_has_mode_precision_p subsumes them. > > - Retarget the test scans from phiopt2 to forwprop1. Add > > forwprop-46.c covering all four arm/comparison polarities. > > - forwprop-45.c uses __UINT64_TYPE__ instead of unsigned long > > and drops the lp64 restriction, covering the type > word_mode > > regime on 32-bit targets. > > - 2/2: > > - Lower the high-part as (N)(((2N) op1 * (2N) op2) >> N). > > pass_optimize_widening_mul rewrites this to WIDEN_MULT_EXPR / > > MULT_HIGHPART_EXPR on supporting targets. Removes > > can_mult_highpart_p queries from forwprop. > > - Replace the can_mult_highpart_p prefilter in match_long_mul > > with a targetm.scalar_mode_supported_p check on the 2N mode. > > Test scans select on int128, mirroring the gate, instead of > > lp64. > > - Drop the m_long_mul_fold_p pass parameter and its passes.def > > arguments. The long-mul fold runs in every forwprop instance. > > Test scans retargeted from forwprop2 to forwprop1. > > - Stop restricting forwprop-44.c to lp64. With the > > can_mult_highpart_p gating gone, the fold is target-independent > > and the test passes on ilp32 targets too. > > > > Changes in v4: > > - 1/2: > > - Rebuild the guard with per-conjunct reasoning: require both > > operands to be SSA names (drops degenerate one-side-constant > > cases that fold trivially elsewhere), require the type to > > have_mode_precision_p (excludes BITINT_TYPE precision !=3D mode > > and similar oddities), drop the explicit MAX_FIXED_MODE_SIZE > > width cap (subsumed by have_mode_precision_p), and gate on the > > flat optab via can_mult_highpart_p of the 2N mode. > > - Retain BRANCH_COST >=3D 2: keep the flatten conditional on a > > target where the branchless form is generally cheaper. > > - Rewrite the cover letter to describe the gate as the > > composition of these conjuncts and to clarify that the > > transformation now only ever introduces a (mul_hi-like) > > can_mult_highpart_p shape, not a libgcc multi-precision call. > > - 2/2: > > - Convert per-variant fold_mul_* functions into a > > table-driven long_mul fold framework. > > - Migrate each variant into a row in long_mul_table (six > > HIGH_PART, six LOW_PART rows) keyed by (kind, extract). > > - Add cross-summand consistency checks > > (long_mul_check_consistency, long_mul_check_two_carries, > > long_mul_check_low_plus_defer) shared across rows. > > - Drop emission to a libgcc multi-precision call from RTL > > expansion; defer to pass_optimize_widening_mul / RTL > > expansion to pick native umul_highpart, a widening multiply, or > > a synthesised sequence. Emission is gated on > > can_mult_highpart_p. > > - Structural redesign: per-variant fold_mul_* functions > > consolidated into a single linearise + classify + table-lookup > > framework (long_mul_table, match_long_mul, > > long_mul_classify_summand, long_mul_check_consistency). Each > > variant is now a row in long_mul_table; consistency checks are > > shared across rows. > > - Fast-fail prefilters in match_long_mul: LHS-type prefilter at > > entry (no legitimate long-mul leaf has a signed / pointer / > > float / odd-width type) and a can_mult_highpart_p probe before > > the row loop to skip HIGH_PART rows on unsupported targets. > > - Bound long_mul_linearize_chain mid-walk by LONG_MUL_MAX_SUMMANDS > > so an overlong addition / BIT_IOR chain bails immediately rather > > than after a full traversal. > > - Emit a dump-file hint pointing at the shared inner addition when > > long-mul folding rejects a chain because of a multi-used > > intermediate (caching the partial sum into a single-use SSA > > name normally enables the fold). > > > > Changes in v3: > > - Moved carry-diamond flattening from forwprop to match.pd, > > replacing ~460 lines of C++ with a 17-line match.pd pattern. > > - Two-carry test scans forwprop3 (the first forwprop after phiopt2, > > since early phiopt restricts which tree codes are allowed). > > - Set location for new sequences. > > - Updated mul_carry_low pattern. > > - Added the `mul_low_plus` pattern. > > - Fixed formatting issues. > > > > Changes in v2: > > - Fixed the testcases by separating the high part's fold count for > > 32-bit and 64-bit targets. > > > > Konstantinos Eleftheriou (7): > > forwprop: Match and fold the long-multiply carry form [PR107090] > > forwprop: Add long-multiply carry-low-sum variant > > forwprop: Add long-multiply two-carry variant > > forwprop: Add long-multiply ladder variants > > forwprop: Add long-multiply low-plus variant > > match.pd, forwprop: Recognize long-multiply carries written as 2-arg > > PHI > > widening_mul: Lower long-multiply chains to inline longhand > > > > gcc/match.pd | 198 +++ > > gcc/testsuite/gcc.dg/long-mul-128-Og.c | 26 + > > gcc/testsuite/gcc.dg/torture/long-mul-128.c | 121 ++ > > .../gcc.dg/torture/long-mul-64-run.c | 180 +++ > > .../gcc.dg/tree-ssa/long-mul-boundary-64.c | 417 ++++++ > > .../gcc.dg/tree-ssa/long-mul-boundary.c | 394 ++++++ > > .../gcc.dg/tree-ssa/long-mul-carry.c | 385 ++++++ > > .../gcc.dg/tree-ssa/long-mul-chain-cse-128.c | 52 + > > .../tree-ssa/long-mul-chain-trunc-128.c | 80 ++ > > .../gcc.dg/tree-ssa/long-mul-extra-addend.c | 63 + > > .../gcc.dg/tree-ssa/long-mul-ladder.c | 333 +++++ > > .../gcc.dg/tree-ssa/long-mul-low-plus.c | 54 + > > .../gcc.dg/tree-ssa/long-mul-partial.c | 193 +++ > > .../gcc.dg/tree-ssa/long-mul-two-carry.c | 140 ++ > > gcc/testsuite/gcc.target/aarch64/long_mul.c | 100 ++ > > .../gcc.target/arm/long-mul-thumb1-inline.c | 47 + > > gcc/testsuite/gcc.target/arm/long-mul-umull.c | 73 + > > gcc/testsuite/gcc.target/i386/long_mul.c | 100 ++ > > .../gcc.target/i386/widen_mult_high_chain.c | 32 + > > gcc/testsuite/lib/target-supports.exp | 20 + > > gcc/tree-ssa-forwprop.cc | 1197 ++++++++++++++++- > > gcc/tree-ssa-math-opts.cc | 492 ++++++- > > gcc/tree-ssa-math-opts.h | 2 + > > 23 files changed, 4690 insertions(+), 9 deletions(-) > > create mode 100644 gcc/testsuite/gcc.dg/long-mul-128-Og.c > > create mode 100644 gcc/testsuite/gcc.dg/torture/long-mul-128.c > > create mode 100644 gcc/testsuite/gcc.dg/torture/long-mul-64-run.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-boundary-64.= c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-boundary.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-carry.c > > create mode 100644 > gcc/testsuite/gcc.dg/tree-ssa/long-mul-chain-cse-128.c > > create mode 100644 > gcc/testsuite/gcc.dg/tree-ssa/long-mul-chain-trunc-128.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-extra-addend= .c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-ladder.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-low-plus.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-partial.c > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-two-carry.c > > create mode 100644 gcc/testsuite/gcc.target/aarch64/long_mul.c > > create mode 100644 gcc/testsuite/gcc.target/arm/long-mul-thumb1-inline= .c > > create mode 100644 gcc/testsuite/gcc.target/arm/long-mul-umull.c > > create mode 100644 gcc/testsuite/gcc.target/i386/long_mul.c > > create mode 100644 gcc/testsuite/gcc.target/i386/widen_mult_high_chain= .c > > > > -- > > 2.55.0 > --000000000000a26aed06583b9ab9 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr">Reverted.</div><br><div class=3D"gmail_quote gmail_quote_c= ontainer"><div dir=3D"ltr" class=3D"gmail_attr">On Tue, 4 Aug 2026 at 18:58= , Andrea Pinski <<a href=3D"mailto:[email protected]">andre= [email protected]</a>> wrote:<br></div><blockquote class=3D"gmai= l_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,20= 4,204);padding-left:1ex">On Mon, Aug 3, 2026 at 8:57=E2=80=AFAM Konstantino= s Eleftheriou<br> <<a href=3D"mailto:[email protected]" target=3D"_blank">= [email protected]</a>> wrote:<br> ><br> ><br> > This patch series teaches GCC to recognize longhand 64x64->128<br> > wide-multiplication idioms and replace them with native multiply<br> > instructions: a widening multiply followed by a right shift for the<br= > > high part, and a plain MULT_EXPR for the low part.<br> ><br> > Portable C/C++ code that needs a 128-bit product on a 64-bit target<br= > > often resorts to a longhand decomposition: split operands into 32-bit<= br> > halves, compute four partial products, and propagate carries manually.= <br> > This pattern appears in a number of real-world codebases, including<br= > > SPEC2026's 750.sealcrypto_r (seal/util/uintarith.h) and several<br= > > examples from Hacker's Delight. Targets like AArch64 (mul/umulh) a= nd<br> > x86-64 can compute the full 128-bit product in one or two instructions= ,<br> > but GCC does not currently fold the longhand sequence back to these.<b= r> ><br> > The recognizer emits the canonical widening shape<br> ><br> >=C2=A0 =C2=A0(N)(((2N) a * (2N) b) >> N)<br> ><br> > for the high part and a plain MULT_EXPR for the low part. The high<br> > part is emitted as a shift of the wide product, not as a bare<br> > (2N) a * (2N) b: the `>> N' form is the MULT_HIGHPART idiom,= so<br> > pass_optimize_widening_mul rewrites it to the target's native high= -part<br> > multiply (e.g. umulh) without ever forming the full 2N product. A bare= <br> > widening product would also compute the unwanted low half, and where 2= N<br> > has no native multiply -- the 128x128 case, 2N =3D OImode, which the m= ode<br> > table carries but the target cannot expand -- there is no way to form<= br> > it at all. For that case the final patch resynthesizes the longhand at= <br> > narrow precision from (N/2)-wide partial products, so the fold never<b= r> > depends on a 2N multiply the target lacks.<br> ><br> > The series is split into seven patches:<br> <br> Two things, this breaks bootstrap on x86_64-linux-gnu.<br> I filed <a href=3D"https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D126642" r= el=3D"noreferrer" target=3D"_blank">https://gcc.gnu.org/bugzilla/show_bug.c= gi?id=3D126642</a> .<br> Can we revert this until that is fixed?<br> <br> Second please don't squash the patches into one commit next time. If<br= > you do please send a full updated patch to the list of what you<br> committed.=C2=A0 This has been GCC's policy.<br> <br> Thanks,<br> Andrea<br> <br> <br> ><br> >=C2=A0 =C2=A01/7=C2=A0 forwprop: Match and fold the long-multiply carry= form<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 [PR107090]<br> ><br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 Adds the match.pd atom patterns and the for= wprop framework:<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 linearize the outer add/ior chain, classify= each summand, and<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 match the multiset against a table of decom= posed variants.<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 Carries the base carry form, a single overf= low comparison on<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 the cross-sum. Matching starts only at the = end of a chain, and<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 leaves that are not long-multiply summands = are preserved and<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 re-applied on top of the fold, so a chain t= hat mixes the idiom<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 with unrelated addends still folds.<br> ><br> >=C2=A0 =C2=A02/7 - 5/7=C2=A0 Add the remaining recognized variants: car= ry-low-sum,<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 two-carry, ladder, and= low-plus.<br> ><br> >=C2=A0 =C2=A06/7=C2=A0 match.pd, forwprop: Recognize long-multiply carr= ies written as<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 2-arg PHI<br> ><br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 Hand-written code often writes the carry as= a 2-arg PHI<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 (`if (overflow) result +=3D pow2;`) with no= top-level + at the<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 result. Adds cond_carry_add / cond_carry_ad= d_neg recognizers<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 for that shape and a match_long_mul_phi ent= ry that synthesizes<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 the carry summand from the PHI bindings and= reuses the table<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 walk and emit path.<br> ><br> >=C2=A0 =C2=A07/7=C2=A0 widening_mul: Lower long-multiply chains to inli= ne longhand<br> ><br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 Lowers the high-part chain to a longhand hi= gh-part at narrow<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 precision when the target has no expansion = path for the 2N<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 form, using (N/2)-by-(N/2)->N widening m= ultiplies where the<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 optab exists and plain N-bit multiplies oth= erwise. Operands<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 that are themselves wider than N are split = into N-bit halves<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 rather than truncated, and a 2N product lef= t with only low-half<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 uses is narrowed to an N-bit multiply rathe= r than reaching<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 expansion. When an operand holds a product&= #39;s high N bits, those<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 come from the product's own operands in= stead of the 2N shift.<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 Paired with pass_optimize_widening_mul so t= he chain is emitted<br> >=C2=A0 =C2=A0 =C2=A0 =C2=A0 only when the pass will run to rescue an un= supported 2N shape.<br> ><br> > On SPEC2026's 750.sealcrypto_r:<br> ><br> >=C2=A0 =C2=A0- AArch64 Neoverse-N1: 25% improvement<br> >=C2=A0 =C2=A0- x86-64 Zen4:=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A059% improv= ement<br> ><br> > Compile-time impact is negligible: recompiling gcc/*.cc<br> > (checking=3Dyes,extra) with the series compiler versus its base adds<b= r> > about 0.1% overall, confined to forwprop, where the recognizer runs<br= > > and grows the pass by a few percent.<br> ><br> > Bootstrapped/regtested on AArch64, x86-64, ARM and PowerPC.<br> ><br> > Changes in v8:<br> > - 1/7 starts matching only at chain ends and sets aside leaves that ar= e<br> >=C2=A0 =C2=A0not long-multiply summands, re-applying them on top of the= fold. The<br> >=C2=A0 =C2=A0two go together. Either alone regresses a foldable chain. = Now folds<br> >=C2=A0 =C2=A0shapes such as `acc +=3D mulh (x, y)`.<br> > - 1/7 factors out build_mul_high_seq, long_mul_classify_match and<br> >=C2=A0 =C2=A0long_mul_classify_chain for the PHI entry in 6/7 to reuse,= and takes<br> >=C2=A0 =C2=A0a gassign * in the matcher and the emitters.<br> > - The long_mul_high_chain atom binds each mult operand through<br> >=C2=A0 =C2=A0`(convert? @X)`, so a PRE-hoisted cast into a PHI still ma= tches. An<br> >=C2=A0 =C2=A0operand wider than N is now split into N-bit halves rather= than<br> >=C2=A0 =C2=A0rejected, which previously left the 2N multiply for expans= ion.<br> > - optimize_widening_mul_active_p returns false for optimize_debug: -Og= <br> >=C2=A0 =C2=A0runs no widening_mul pass to lower the emitted chain, so t= he<br> >=C2=A0 =C2=A0unexpandable multiply reached expand.<br> > - Add narrow_long_mul_low_half: when a 2N `res =3D a * b` has uses onl= y in<br> >=C2=A0 =C2=A0its low N bits and the target cannot expand 2N, rewrite it= to<br> >=C2=A0 =C2=A0`res =3D (2N) ((N)a * (N)b)`. The split-based lowering cov= ers a chained<br> >=C2=A0 =C2=A02N operand by recursing into it, but not a shared 2N produ= ct left with<br> >=C2=A0 =C2=A0only low-half uses, which is what ICEs libgo's p521_fi= at64.go on ARM32.<br> > - long_mul_split_operand resolves a 2N value shifted down by N to the<= br> >=C2=A0 =C2=A0high half of what was shifted, instead of truncating the s= hift. The<br> >=C2=A0 =C2=A0truncation read the shift and so kept a chained product li= ve past its<br> >=C2=A0 =C2=A0own lowering, aborting expand_mult on a target without a 2= N multiply.<br> >=C2=A0 =C2=A0Reachable from Go on ARM32 as bits.Mul64 (bits.Mul64 (x, y= ), z), where<br> >=C2=A0 =C2=A0the unexpandable mode is TImode, and on aarch64 and x86-64= from a<br> >=C2=A0 =C2=A0chained __int128 longhand, where it is OImode.<br> > - New coverage: near misses of the idiom, checked at runtime against<b= r> >=C2=A0 =C2=A0their literal meaning, a signed narrow-cast operand on Thu= mb-1, and<br> >=C2=A0 =C2=A0chained longhands through both halves on ARM32.<br> ><br> > Changes in v7:<br> > - Split the single long-multiply fold (was 1/2) into five patches: a<b= r> >=C2=A0 =C2=A0base patch carrying the framework and the carry form, then= one<br> >=C2=A0 =C2=A0patch each for the carry-low-sum, two-carry, ladder and lo= w-plus<br> >=C2=A0 =C2=A0variants.=C2=A0 Easier to review and to bisect a variant i= n isolation.<br> >=C2=A0 =C2=A0The PHI-form recognition follows as 6/7, unchanged from v6= 's 2/2.<br> > - New 7/7: lower the emitted high-part chain to inline longhand at<br> >=C2=A0 =C2=A0narrow precision when the target has no expansion path for= the 2N<br> >=C2=A0 =C2=A0form.=C2=A0 v6 only emitted a HIGH_PART when the 2N scalar= mode existed<br> >=C2=A0 =C2=A0and skipped it otherwise; v7 emits it and pairs the recogn= izer with<br> >=C2=A0 =C2=A0lower_long_mul_high_chain via optimize_widening_mul_active= _p, so a<br> >=C2=A0 =C2=A0128x128 high part on a target whose mode table has OImode = but no<br> >=C2=A0 =C2=A0scalar OImode support is now built from (N/2)-wide partial= products<br> >=C2=A0 =C2=A0instead of a 2N multiply the target cannot expand.<br> > - Refuse the HIGH_PART emit and the chain lowering for BITINT_TYPE,<br= > >=C2=A0 =C2=A0keeping the recognizer and the lowering gate symmetric.<br= > > - Add per-variant tree-ssa tests, 128-bit torture and runtime tests,<b= r> >=C2=A0 =C2=A0arm thumb1 / umull inline tests, and a check_effective_tar= get<br> >=C2=A0 =C2=A0_oi_mode helper.<br> ><br> > Changes in v6:<br> > - Reorder so the long-multiply fold (was 2/2) is now 1/2 and a new<br> >=C2=A0 =C2=A0PHI-form recognition pass is 2/2.=C2=A0 Reverting 2/2 leav= es a working<br> >=C2=A0 =C2=A0long-multiply fold for the flat-shifted-compare carry form= .<br> > - Drop v5's standalone flatten_cond_carry_add driver.=C2=A0 The sa= me<br> >=C2=A0 =C2=A0cond_carry_add / cond_carry_add_neg match.pd recognizers n= ow feed<br> >=C2=A0 =C2=A0a match_long_mul_phi entry inside the long-multiply fold, = so a<br> >=C2=A0 =C2=A0PHI-shaped carry folds straight to the wide-multiply outpu= t.<br> > - Factor long_mul_classify_chain, long_mul_classify_match and<br> >=C2=A0 =C2=A0build_mul_high_seq for sharing between match_long_mul and = the new<br> >=C2=A0 =C2=A0match_long_mul_phi.<br> > - cond_carry_add_neg uses le / ge instead of gt / lt to encode the<br> >=C2=A0 =C2=A0carry condition strictly.=C2=A0 v5 inverted the compare vi= a<br> >=C2=A0 =C2=A0invert_tree_comparison in the flatten driver; v6 synthesis= es the<br> >=C2=A0 =C2=A0carry summand directly inside match_long_mul_phi and so re= quires<br> >=C2=A0 =C2=A0the recogniser to encode the strict form.<br> > - Delete forwprop-44/45/46.c; add PHI-form coverage in<br> >=C2=A0 =C2=A0long-mul-carry.c, long-mul-two-carry.c, long-mul-boundary.= c<br> >=C2=A0 =C2=A0and long-mul-boundary-64.c.<br> > - Add PHI-form near-miss tests in long-mul-partial.c and<br> >=C2=A0 =C2=A0operand-swap polarity coverage in long-mul-boundary{,-64}.= c.<br> > - Refresh stale long-mul comment references (check_hilo_and_ops,<br> >=C2=A0 =C2=A0fold_mul_low_plus) and reword mul_carry_low's :c-on-gt= note to<br> >=C2=A0 =C2=A0the correct LT form (a + b < a).<br> ><br> > Changes in v5:<br> > - 1/2:<br> >=C2=A0 =C2=A0- Replace the match.pd simplify on COND_EXPR with cond_car= ry_add<br> >=C2=A0 =C2=A0 =C2=A0/ cond_carry_add_neg match recognizers (cond^), spl= it by gcond<br> >=C2=A0 =C2=A0 =C2=A0polarity, plus a flatten_cond_carry_add driver in<b= r> >=C2=A0 =C2=A0 =C2=A0tree-ssa-forwprop.cc.=C2=A0 The driver inverts the = gcond's<br> >=C2=A0 =C2=A0 =C2=A0comparison for the _neg form.=C2=A0 Modelled on mat= ch_saturation_add.<br> >=C2=A0 =C2=A0- Remove fold_cond_carry_add_profitable_p and the tm_p.h /= <br> >=C2=A0 =C2=A0 =C2=A0predict.h includes from gimple-match-head.cc.=C2=A0= The width ><br> >=C2=A0 =C2=A0 =C2=A0MAX_FIXED_MODE_SIZE and width % 2 !=3D 0 guards wer= e<br> >=C2=A0 =C2=A0 =C2=A0prerequisites for the can_mult_highpart_p fallback = path, not<br> >=C2=A0 =C2=A0 =C2=A0soundness checks.=C2=A0 type_has_mode_precision_p s= ubsumes them.<br> >=C2=A0 =C2=A0- Retarget the test scans from phiopt2 to forwprop1.=C2=A0= Add<br> >=C2=A0 =C2=A0 =C2=A0forwprop-46.c covering all four arm/comparison pola= rities.<br> >=C2=A0 =C2=A0- forwprop-45.c uses __UINT64_TYPE__ instead of unsigned l= ong<br> >=C2=A0 =C2=A0 =C2=A0and drops the lp64 restriction, covering the type &= gt; word_mode<br> >=C2=A0 =C2=A0 =C2=A0regime on 32-bit targets.<br> > - 2/2:<br> >=C2=A0 =C2=A0- Lower the high-part as (N)(((2N) op1 * (2N) op2) >>= ; N).<br> >=C2=A0 =C2=A0 =C2=A0pass_optimize_widening_mul rewrites this to WIDEN_M= ULT_EXPR /<br> >=C2=A0 =C2=A0 =C2=A0MULT_HIGHPART_EXPR on supporting targets.=C2=A0 Rem= oves<br> >=C2=A0 =C2=A0 =C2=A0can_mult_highpart_p queries from forwprop.<br> >=C2=A0 =C2=A0- Replace the can_mult_highpart_p prefilter in match_long_= mul<br> >=C2=A0 =C2=A0 =C2=A0with a targetm.scalar_mode_supported_p check on the= 2N mode.<br> >=C2=A0 =C2=A0 =C2=A0Test scans select on int128, mirroring the gate, in= stead of<br> >=C2=A0 =C2=A0 =C2=A0lp64.<br> >=C2=A0 =C2=A0- Drop the m_long_mul_fold_p pass parameter and its passes= .def<br> >=C2=A0 =C2=A0 =C2=A0arguments.=C2=A0 The long-mul fold runs in every fo= rwprop instance.<br> >=C2=A0 =C2=A0 =C2=A0Test scans retargeted from forwprop2 to forwprop1.<= br> >=C2=A0 =C2=A0- Stop restricting forwprop-44.c to lp64.=C2=A0 With the<b= r> >=C2=A0 =C2=A0 =C2=A0can_mult_highpart_p gating gone, the fold is target= -independent<br> >=C2=A0 =C2=A0 =C2=A0and the test passes on ilp32 targets too.<br> ><br> > Changes in v4:<br> > - 1/2:<br> >=C2=A0 =C2=A0- Rebuild the guard with per-conjunct reasoning: require b= oth<br> >=C2=A0 =C2=A0 =C2=A0operands to be SSA names (drops degenerate one-side= -constant<br> >=C2=A0 =C2=A0 =C2=A0cases that fold trivially elsewhere), require the t= ype to<br> >=C2=A0 =C2=A0 =C2=A0have_mode_precision_p (excludes BITINT_TYPE precisi= on !=3D mode<br> >=C2=A0 =C2=A0 =C2=A0and similar oddities), drop the explicit MAX_FIXED_= MODE_SIZE<br> >=C2=A0 =C2=A0 =C2=A0width cap (subsumed by have_mode_precision_p), and = gate on the<br> >=C2=A0 =C2=A0 =C2=A0flat optab via can_mult_highpart_p of the 2N mode.<= br> >=C2=A0 =C2=A0- Retain BRANCH_COST >=3D 2: keep the flatten condition= al on a<br> >=C2=A0 =C2=A0 =C2=A0target where the branchless form is generally cheap= er.<br> >=C2=A0 =C2=A0- Rewrite the cover letter to describe the gate as the<br> >=C2=A0 =C2=A0 =C2=A0composition of these conjuncts and to clarify that = the<br> >=C2=A0 =C2=A0 =C2=A0transformation now only ever introduces a (mul_hi-l= ike)<br> >=C2=A0 =C2=A0 =C2=A0can_mult_highpart_p shape, not a libgcc multi-preci= sion call.<br> > - 2/2:<br> >=C2=A0 =C2=A0- Convert per-variant fold_mul_* functions into a<br> >=C2=A0 =C2=A0 =C2=A0table-driven long_mul fold framework.<br> >=C2=A0 =C2=A0- Migrate each variant into a row in long_mul_table (six<b= r> >=C2=A0 =C2=A0 =C2=A0HIGH_PART, six LOW_PART rows) keyed by (kind, extra= ct).<br> >=C2=A0 =C2=A0- Add cross-summand consistency checks<br> >=C2=A0 =C2=A0 =C2=A0(long_mul_check_consistency, long_mul_check_two_car= ries,<br> >=C2=A0 =C2=A0 =C2=A0long_mul_check_low_plus_defer) shared across rows.<= br> >=C2=A0 =C2=A0- Drop emission to a libgcc multi-precision call from RTL<= br> >=C2=A0 =C2=A0 =C2=A0expansion; defer to pass_optimize_widening_mul / RT= L<br> >=C2=A0 =C2=A0 =C2=A0expansion to pick native umul_highpart, a widening = multiply, or<br> >=C2=A0 =C2=A0 =C2=A0a synthesised sequence.=C2=A0 Emission is gated on<= br> >=C2=A0 =C2=A0 =C2=A0can_mult_highpart_p.<br> >=C2=A0 =C2=A0- Structural redesign: per-variant fold_mul_* functions<br= > >=C2=A0 =C2=A0 =C2=A0consolidated into a single linearise + classify + t= able-lookup<br> >=C2=A0 =C2=A0 =C2=A0framework (long_mul_table, match_long_mul,<br> >=C2=A0 =C2=A0 =C2=A0long_mul_classify_summand, long_mul_check_consisten= cy).=C2=A0 Each<br> >=C2=A0 =C2=A0 =C2=A0variant is now a row in long_mul_table; consistency= checks are<br> >=C2=A0 =C2=A0 =C2=A0shared across rows.<br> >=C2=A0 =C2=A0- Fast-fail prefilters in match_long_mul: LHS-type prefilt= er at<br> >=C2=A0 =C2=A0 =C2=A0entry (no legitimate long-mul leaf has a signed / p= ointer /<br> >=C2=A0 =C2=A0 =C2=A0float / odd-width type) and a can_mult_highpart_p p= robe before<br> >=C2=A0 =C2=A0 =C2=A0the row loop to skip HIGH_PART rows on unsupported = targets.<br> >=C2=A0 =C2=A0- Bound long_mul_linearize_chain mid-walk by LONG_MUL_MAX_= SUMMANDS<br> >=C2=A0 =C2=A0 =C2=A0so an overlong addition / BIT_IOR chain bails immed= iately rather<br> >=C2=A0 =C2=A0 =C2=A0than after a full traversal.<br> >=C2=A0 =C2=A0- Emit a dump-file hint pointing at the shared inner addit= ion when<br> >=C2=A0 =C2=A0 =C2=A0long-mul folding rejects a chain because of a multi= -used<br> >=C2=A0 =C2=A0 =C2=A0intermediate (caching the partial sum into a single= -use SSA<br> >=C2=A0 =C2=A0 =C2=A0name normally enables the fold).<br> ><br> > Changes in v3:<br> > - Moved carry-diamond flattening from forwprop to match.pd,<br> > replacing ~460 lines of C++ with a 17-line match.pd pattern.<br> > - Two-carry test scans forwprop3 (the first forwprop after phiopt2,<br= > > since early phiopt restricts which tree codes are allowed).<br> > - Set location for new sequences.<br> > - Updated mul_carry_low pattern.<br> > - Added the `mul_low_plus` pattern.<br> > - Fixed formatting issues.<br> ><br> > Changes in v2:<br> > - Fixed the testcases by separating the high part's fold count for= <br> > 32-bit and 64-bit targets.<br> ><br> > Konstantinos Eleftheriou (7):<br> >=C2=A0 =C2=A0forwprop: Match and fold the long-multiply carry form [PR1= 07090]<br> >=C2=A0 =C2=A0forwprop: Add long-multiply carry-low-sum variant<br> >=C2=A0 =C2=A0forwprop: Add long-multiply two-carry variant<br> >=C2=A0 =C2=A0forwprop: Add long-multiply ladder variants<br> >=C2=A0 =C2=A0forwprop: Add long-multiply low-plus variant<br> >=C2=A0 =C2=A0match.pd, forwprop: Recognize long-multiply carries writte= n as 2-arg<br> >=C2=A0 =C2=A0 =C2=A0PHI<br> >=C2=A0 =C2=A0widening_mul: Lower long-multiply chains to inline longhan= d<br> ><br> >=C2=A0 gcc/match.pd=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 = 198 +++<br> >=C2=A0 gcc/testsuite/gcc.dg/long-mul-128-Og.c=C2=A0 =C2=A0 =C2=A0 =C2= =A0 |=C2=A0 =C2=A026 +<br> >=C2=A0 gcc/testsuite/gcc.dg/torture/long-mul-128.c=C2=A0 =C2=A0|=C2=A0 = 121 ++<br> >=C2=A0 .../gcc.dg/torture/long-mul-64-run.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 |=C2=A0 180 +++<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-boundary-64.c=C2=A0 =C2=A0 |=C2=A0 = 417 ++++++<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-boundary.c=C2=A0 =C2=A0 =C2=A0 =C2= =A0|=C2=A0 394 ++++++<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-carry.c=C2=A0 =C2=A0 =C2=A0 =C2=A0 = =C2=A0 |=C2=A0 385 ++++++<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-chain-cse-128.c=C2=A0 |=C2=A0 =C2= =A052 +<br> >=C2=A0 .../tree-ssa/long-mul-chain-trunc-128.c=C2=A0 =C2=A0 =C2=A0 =C2= =A0|=C2=A0 =C2=A080 ++<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-extra-addend.c=C2=A0 =C2=A0|=C2=A0 = =C2=A063 +<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-ladder.c=C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0|=C2=A0 333 +++++<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-low-plus.c=C2=A0 =C2=A0 =C2=A0 =C2= =A0|=C2=A0 =C2=A054 +<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-partial.c=C2=A0 =C2=A0 =C2=A0 =C2= =A0 |=C2=A0 193 +++<br> >=C2=A0 .../gcc.dg/tree-ssa/long-mul-two-carry.c=C2=A0 =C2=A0 =C2=A0 |= =C2=A0 140 ++<br> >=C2=A0 gcc/testsuite/gcc.target/aarch64/long_mul.c=C2=A0 =C2=A0|=C2=A0 = 100 ++<br> >=C2=A0 .../gcc.target/arm/long-mul-thumb1-inline.c=C2=A0 =C2=A0|=C2=A0 = =C2=A047 +<br> >=C2=A0 gcc/testsuite/gcc.target/arm/long-mul-umull.c |=C2=A0 =C2=A073 += <br> >=C2=A0 gcc/testsuite/gcc.target/i386/long_mul.c=C2=A0 =C2=A0 =C2=A0 |= =C2=A0 100 ++<br> >=C2=A0 .../gcc.target/i386/widen_mult_high_chain.c=C2=A0 =C2=A0|=C2=A0 = =C2=A032 +<br> >=C2=A0 gcc/testsuite/lib/target-supports.exp=C2=A0 =C2=A0 =C2=A0 =C2=A0= =C2=A0|=C2=A0 =C2=A020 +<br> >=C2=A0 gcc/tree-ssa-forwprop.cc=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 | 1197 ++++++++++++++++-<br> >=C2=A0 gcc/tree-ssa-math-opts.cc=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0|=C2=A0 492 ++++++-<br> >=C2=A0 gcc/tree-ssa-math-opts.h=C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2= =A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 =C2=A0 |=C2=A0 =C2=A0 2 +<br> >=C2=A0 23 files changed, 4690 insertions(+), 9 deletions(-)<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/long-mul-128-Og.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/torture/long-mul-128.c<b= r> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/torture/long-mul-64-run.= c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-bounda= ry-64.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-bounda= ry.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-carry.= c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-chain-= cse-128.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-chain-= trunc-128.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-extra-= addend.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-ladder= .c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-low-pl= us.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-partia= l.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/long-mul-two-ca= rry.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.target/aarch64/long_mul.c<b= r> >=C2=A0 create mode 100644 gcc/testsuite/gcc.target/arm/long-mul-thumb1-= inline.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.target/arm/long-mul-umull.c= <br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.target/i386/long_mul.c<br> >=C2=A0 create mode 100644 gcc/testsuite/gcc.target/i386/widen_mult_high= _chain.c<br> ><br> > --<br> > 2.55.0<br> </blockquote></div> --000000000000a26aed06583b9ab9--