> -----Original Message-----
> From: Robin Dapp <[email protected]>
> Sent: 04 August 2026 20:21
> To: Tamar Christina <[email protected]>; Jeffrey Law
> <[email protected]>; [email protected]; gcc-
> [email protected]
> Cc: [email protected]; Robin Dapp <[email protected]>
> Subject: Re: [PATCH 1/2] genopinit: Distribute generated code across multiple
> files
>
> > But in my opinion we don't want to keep adding split code to every
> > gen* file individually
> > but concentrate that code in gensupport so ever gen* can use it.
>
> The individual splits are there because I started out with one
> (genemit), then later realized genrecog is large as well. It was
> certainly not the initial intention to split everything individually.
> But I agree that by now we have reached a tipping point.
>
> BTW I also touched opinit before, splitting one function into several,
> because it would trigger pathological compiler behavior.
> So we already perform some kind of splitting. Are we sure we're not
> actually seeing pathological behavior again that could be helped by
> something else than file-level splitting?
geninit is also one that I think can benefit from a different way of generating
the values.
If you look at the large arrays it generates for like init_all_optabs
These are huge, and also result in at runtime just a series of branches and
calls.
i.e.
static void
init_optabs_01 (struct target_optabs *optabs)
{
bool *ena = optabs->pat_enable;
ena[1000] = HAVE_udot_prodv2siv8qi;
ena[1001] = HAVE_udot_prodv4siv16qi;
ena[1002] = HAVE_udot_prodvnx8hivnx16qi;
ena[1003] = HAVE_udot_prodvnx4sivnx16qi;
ena[1004] = HAVE_udot_prodvnx4sivnx8hi;
ena[1005] = HAVE_udot_prodvnx2divnx8hi;
ena[1006] = HAVE_usdot_prodv2siv8qi;
ena[1007] = HAVE_usdot_prodv4siv16qi;
ena[1008] = HAVE_usdot_prodvnx4sivnx16qi;
ena[1009] = HAVE_while_ultsivnx16bi;
ena[1010] = HAVE_while_ultdivnx16bi;
ena[1011] = HAVE_while_ultsivnx8bi;
ena[1012] = HAVE_while_ultdivnx8bi;
ena[1013] = HAVE_while_ultsivnx4bi;
ena[1014] = HAVE_while_ultdivnx4bi;
ena[1015] = HAVE_while_ultsivnx2bi;
ena[1016] = HAVE_while_ultdivnx2bi;
ena[1017] = HAVE_addsi3;
ena[1018] = HAVE_adddi3;
ena[1019] = HAVE_addti3;
ena[1020] = HAVE_addhf3;
ena[1021] = HAVE_addsf3;
ena[1022] = HAVE_adddf3;
ena[1023] = HAVE_addv8qi3;
ena[1024] = HAVE_addv4hi3;
ena[1025] = HAVE_addv2si3;
ena[1026] = HAVE_addv16qi3;
ena[1027] = HAVE_addv8hi3;
ena[1028] = HAVE_addv4si3;
ena[1029] = HAVE_addv2di3;
ena[1030] = HAVE_addvnx16qi3;
ena[1031] = HAVE_addvnx8hi3;
ena[1032] = HAVE_addvnx4si3;
ena[1033] = HAVE_addvnx2di3;
ena[1034] = HAVE_addvnx2qi3;
ena[1035] = HAVE_addvnx4qi3;
ena[1036] = HAVE_addvnx2hi3;
ena[1037] = HAVE_addvnx8qi3;
ena[1038] = HAVE_addvnx4hi3;
ena[1039] = HAVE_addvnx2si3;
ena[1040] = HAVE_addv4hf3;
ena[1041] = HAVE_addv2sf3;
ena[1042] = HAVE_addv8hf3;
ena[1043] = HAVE_addv4sf3;
ena[1044] = HAVE_addv2df3;
etc.
However if you look at insn-flags.h you'll see that
These flags mostly have the same definitions
For instance
> grep "HAVE_while_ult" ../../build-aarch64-none-elf/obj/gcc2/gcc/insn-flags.h
#define HAVE_while_ultsivnx16bi (TARGET_SVE)
#define HAVE_while_ultdivnx16bi (TARGET_SVE)
#define HAVE_while_ultsivnx8bi (TARGET_SVE)
#define HAVE_while_ultdivnx8bi (TARGET_SVE)
#define HAVE_while_ultsivnx4bi (TARGET_SVE)
#define HAVE_while_ultdivnx4bi (TARGET_SVE)
#define HAVE_while_ultsivnx2bi (TARGET_SVE)
#define HAVE_while_ultdivnx2bi (TARGET_SVE)
#define HAVE_while_ultsivnx16bi_ptest (TARGET_SVE)
#define HAVE_while_ultdivnx16bi_ptest (TARGET_SVE)
#define HAVE_while_ultsivnx8bi_ptest (TARGET_SVE)
#define HAVE_while_ultdivnx8bi_ptest (TARGET_SVE)
#define HAVE_while_ultsivnx4bi_ptest (TARGET_SVE)
#define HAVE_while_ultdivnx4bi_ptest (TARGET_SVE)
#define HAVE_while_ultsivnx2bi_ptest (TARGET_SVE)
#define HAVE_while_ultdivnx2bi_ptest (TARGET_SVE)
So we're spending quite a bit of time reading and compiling these files to just
set
the same flag over and over again.
If we were to group the generated code by the defines instead, we wouldn't
need giant array assignments.
Init_all_optabs would just become:
bool *ena = optabs->pat_enable
memset (ena, 0, sizeof optabs->pat_enable);
if (TARGET_SVE)
memset (ena + offset, 1, count);
which I think would be both a runtime and a compile time win because
quite a few insn have either no condition, or are just TARGET_SVE or
TARGET_SVE2 etc. And I assume this is common across many other
targets.
Though I have only experimented with a small version years ago.
Other files have similar things that interesting to evaluate wrt to the
scale the generators are at now.
Thanks,
Tamar
>
> > Lastly I also don't think splitting on an iterative way is going to
> > give you the best compile
> > time increase. Because that tends to bias the output.
> >
> > In genmatch I use ftell to tell how big the files is so far an pick the
> > smallest
> file. This allows
> > me to balance out the compile time over all files. I believe Robin
> > eventually
> respun his
> > gen<something> match to do this a swell as that allows you to amortize the
> compile time
> > over cores.
>
> Yeah, I added your suggestion to the initial genemit split. And at
> least the "which file to write to next"/ftell functionality is not local
> to genemit but already in gensupport. The function is choose_output and
> Kyryll's patch even uses it already so it shouldn't do iterative
> splitting if I'm not mistaken.
>
> Each gen* still keep track of its own files, though, and that part could
> be unified still. I don't think it's terribly difficult but will
> require some plumbing.
>
>
> --
> Regards
> Robin