forked from illumos/gcc
-
Notifications
You must be signed in to change notification settings - Fork 1
/
gcse.c
6774 lines (5608 loc) · 192 KB
/
gcse.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* Global common subexpression elimination/Partial redundancy elimination
and global constant/copy propagation for GNU compiler.
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
2006, 2007 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
/* TODO
- reordering of memory allocation and freeing to be more space efficient
- do rough calc of how many regs are needed in each block, and a rough
calc of how many regs are available in each class and use that to
throttle back the code in cases where RTX_COST is minimal.
- a store to the same address as a load does not kill the load if the
source of the store is also the destination of the load. Handling this
allows more load motion, particularly out of loops.
- ability to realloc sbitmap vectors would allow one initial computation
of reg_set_in_block with only subsequent additions, rather than
recomputing it for each pass
*/
/* References searched while implementing this.
Compilers Principles, Techniques and Tools
Aho, Sethi, Ullman
Addison-Wesley, 1988
Global Optimization by Suppression of Partial Redundancies
E. Morel, C. Renvoise
communications of the acm, Vol. 22, Num. 2, Feb. 1979
A Portable Machine-Independent Global Optimizer - Design and Measurements
Frederick Chow
Stanford Ph.D. thesis, Dec. 1983
A Fast Algorithm for Code Movement Optimization
D.M. Dhamdhere
SIGPLAN Notices, Vol. 23, Num. 10, Oct. 1988
A Solution to a Problem with Morel and Renvoise's
Global Optimization by Suppression of Partial Redundancies
K-H Drechsler, M.P. Stadel
ACM TOPLAS, Vol. 10, Num. 4, Oct. 1988
Practical Adaptation of the Global Optimization
Algorithm of Morel and Renvoise
D.M. Dhamdhere
ACM TOPLAS, Vol. 13, Num. 2. Apr. 1991
Efficiently Computing Static Single Assignment Form and the Control
Dependence Graph
R. Cytron, J. Ferrante, B.K. Rosen, M.N. Wegman, and F.K. Zadeck
ACM TOPLAS, Vol. 13, Num. 4, Oct. 1991
Lazy Code Motion
J. Knoop, O. Ruthing, B. Steffen
ACM SIGPLAN Notices Vol. 27, Num. 7, Jul. 1992, '92 Conference on PLDI
What's In a Region? Or Computing Control Dependence Regions in Near-Linear
Time for Reducible Flow Control
Thomas Ball
ACM Letters on Programming Languages and Systems,
Vol. 2, Num. 1-4, Mar-Dec 1993
An Efficient Representation for Sparse Sets
Preston Briggs, Linda Torczon
ACM Letters on Programming Languages and Systems,
Vol. 2, Num. 1-4, Mar-Dec 1993
A Variation of Knoop, Ruthing, and Steffen's Lazy Code Motion
K-H Drechsler, M.P. Stadel
ACM SIGPLAN Notices, Vol. 28, Num. 5, May 1993
Partial Dead Code Elimination
J. Knoop, O. Ruthing, B. Steffen
ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
Effective Partial Redundancy Elimination
P. Briggs, K.D. Cooper
ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
The Program Structure Tree: Computing Control Regions in Linear Time
R. Johnson, D. Pearson, K. Pingali
ACM SIGPLAN Notices, Vol. 29, Num. 6, Jun. 1994
Optimal Code Motion: Theory and Practice
J. Knoop, O. Ruthing, B. Steffen
ACM TOPLAS, Vol. 16, Num. 4, Jul. 1994
The power of assignment motion
J. Knoop, O. Ruthing, B. Steffen
ACM SIGPLAN Notices Vol. 30, Num. 6, Jun. 1995, '95 Conference on PLDI
Global code motion / global value numbering
C. Click
ACM SIGPLAN Notices Vol. 30, Num. 6, Jun. 1995, '95 Conference on PLDI
Value Driven Redundancy Elimination
L.T. Simpson
Rice University Ph.D. thesis, Apr. 1996
Value Numbering
L.T. Simpson
Massively Scalar Compiler Project, Rice University, Sep. 1996
High Performance Compilers for Parallel Computing
Michael Wolfe
Addison-Wesley, 1996
Advanced Compiler Design and Implementation
Steven Muchnick
Morgan Kaufmann, 1997
Building an Optimizing Compiler
Robert Morgan
Digital Press, 1998
People wishing to speed up the code here should read:
Elimination Algorithms for Data Flow Analysis
B.G. Ryder, M.C. Paull
ACM Computing Surveys, Vol. 18, Num. 3, Sep. 1986
How to Analyze Large Programs Efficiently and Informatively
D.M. Dhamdhere, B.K. Rosen, F.K. Zadeck
ACM SIGPLAN Notices Vol. 27, Num. 7, Jul. 1992, '92 Conference on PLDI
People wishing to do something different can find various possibilities
in the above papers and elsewhere.
*/
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "toplev.h"
#include "rtl.h"
#include "tree.h"
#include "tm_p.h"
#include "regs.h"
#include "hard-reg-set.h"
#include "flags.h"
#include "real.h"
#include "insn-config.h"
#include "recog.h"
#include "basic-block.h"
#include "output.h"
#include "function.h"
#include "expr.h"
#include "except.h"
#include "ggc.h"
#include "params.h"
#include "cselib.h"
#include "intl.h"
#include "obstack.h"
#include "timevar.h"
#include "tree-pass.h"
#include "hashtab.h"
#include "df.h"
#include "dbgcnt.h"
/* Propagate flow information through back edges and thus enable PRE's
moving loop invariant calculations out of loops.
Originally this tended to create worse overall code, but several
improvements during the development of PRE seem to have made following
back edges generally a win.
Note much of the loop invariant code motion done here would normally
be done by loop.c, which has more heuristics for when to move invariants
out of loops. At some point we might need to move some of those
heuristics into gcse.c. */
/* We support GCSE via Partial Redundancy Elimination. PRE optimizations
are a superset of those done by GCSE.
We perform the following steps:
1) Compute basic block information.
2) Compute table of places where registers are set.
3) Perform copy/constant propagation.
4) Perform global cse using lazy code motion if not optimizing
for size, or code hoisting if we are.
5) Perform another pass of copy/constant propagation.
Two passes of copy/constant propagation are done because the first one
enables more GCSE and the second one helps to clean up the copies that
GCSE creates. This is needed more for PRE than for Classic because Classic
GCSE will try to use an existing register containing the common
subexpression rather than create a new one. This is harder to do for PRE
because of the code motion (which Classic GCSE doesn't do).
Expressions we are interested in GCSE-ing are of the form
(set (pseudo-reg) (expression)).
Function want_to_gcse_p says what these are.
PRE handles moving invariant expressions out of loops (by treating them as
partially redundant).
Eventually it would be nice to replace cse.c/gcse.c with SSA (static single
assignment) based GVN (global value numbering). L. T. Simpson's paper
(Rice University) on value numbering is a useful reference for this.
**********************
We used to support multiple passes but there are diminishing returns in
doing so. The first pass usually makes 90% of the changes that are doable.
A second pass can make a few more changes made possible by the first pass.
Experiments show any further passes don't make enough changes to justify
the expense.
A study of spec92 using an unlimited number of passes:
[1 pass] = 1208 substitutions, [2] = 577, [3] = 202, [4] = 192, [5] = 83,
[6] = 34, [7] = 17, [8] = 9, [9] = 4, [10] = 4, [11] = 2,
[12] = 2, [13] = 1, [15] = 1, [16] = 2, [41] = 1
It was found doing copy propagation between each pass enables further
substitutions.
PRE is quite expensive in complicated functions because the DFA can take
a while to converge. Hence we only perform one pass. The parameter
max-gcse-passes can be modified if one wants to experiment.
**********************
The steps for PRE are:
1) Build the hash table of expressions we wish to GCSE (expr_hash_table).
2) Perform the data flow analysis for PRE.
3) Delete the redundant instructions
4) Insert the required copies [if any] that make the partially
redundant instructions fully redundant.
5) For other reaching expressions, insert an instruction to copy the value
to a newly created pseudo that will reach the redundant instruction.
The deletion is done first so that when we do insertions we
know which pseudo reg to use.
Various papers have argued that PRE DFA is expensive (O(n^2)) and others
argue it is not. The number of iterations for the algorithm to converge
is typically 2-4 so I don't view it as that expensive (relatively speaking).
PRE GCSE depends heavily on the second CSE pass to clean up the copies
we create. To make an expression reach the place where it's redundant,
the result of the expression is copied to a new register, and the redundant
expression is deleted by replacing it with this new register. Classic GCSE
doesn't have this problem as much as it computes the reaching defs of
each register in each block and thus can try to use an existing
register. */
/* GCSE global vars. */
/* Note whether or not we should run jump optimization after gcse. We
want to do this for two cases.
* If we changed any jumps via cprop.
* If we added any labels via edge splitting. */
static int run_jump_opt_after_gcse;
/* An obstack for our working variables. */
static struct obstack gcse_obstack;
struct reg_use {rtx reg_rtx; };
/* Hash table of expressions. */
struct expr
{
/* The expression (SET_SRC for expressions, PATTERN for assignments). */
rtx expr;
/* Index in the available expression bitmaps. */
int bitmap_index;
/* Next entry with the same hash. */
struct expr *next_same_hash;
/* List of anticipatable occurrences in basic blocks in the function.
An "anticipatable occurrence" is one that is the first occurrence in the
basic block, the operands are not modified in the basic block prior
to the occurrence and the output is not used between the start of
the block and the occurrence. */
struct occr *antic_occr;
/* List of available occurrence in basic blocks in the function.
An "available occurrence" is one that is the last occurrence in the
basic block and the operands are not modified by following statements in
the basic block [including this insn]. */
struct occr *avail_occr;
/* Non-null if the computation is PRE redundant.
The value is the newly created pseudo-reg to record a copy of the
expression in all the places that reach the redundant copy. */
rtx reaching_reg;
};
/* Occurrence of an expression.
There is one per basic block. If a pattern appears more than once the
last appearance is used [or first for anticipatable expressions]. */
struct occr
{
/* Next occurrence of this expression. */
struct occr *next;
/* The insn that computes the expression. */
rtx insn;
/* Nonzero if this [anticipatable] occurrence has been deleted. */
char deleted_p;
/* Nonzero if this [available] occurrence has been copied to
reaching_reg. */
/* ??? This is mutually exclusive with deleted_p, so they could share
the same byte. */
char copied_p;
};
/* Expression and copy propagation hash tables.
Each hash table is an array of buckets.
??? It is known that if it were an array of entries, structure elements
`next_same_hash' and `bitmap_index' wouldn't be necessary. However, it is
not clear whether in the final analysis a sufficient amount of memory would
be saved as the size of the available expression bitmaps would be larger
[one could build a mapping table without holes afterwards though].
Someday I'll perform the computation and figure it out. */
struct hash_table
{
/* The table itself.
This is an array of `expr_hash_table_size' elements. */
struct expr **table;
/* Size of the hash table, in elements. */
unsigned int size;
/* Number of hash table elements. */
unsigned int n_elems;
/* Whether the table is expression of copy propagation one. */
int set_p;
};
/* Expression hash table. */
static struct hash_table expr_hash_table;
/* Copy propagation hash table. */
static struct hash_table set_hash_table;
/* Mapping of uids to cuids.
Only real insns get cuids. */
static int *uid_cuid;
/* Highest UID in UID_CUID. */
static int max_uid;
/* Get the cuid of an insn. */
#ifdef ENABLE_CHECKING
#define INSN_CUID(INSN) \
(gcc_assert (INSN_UID (INSN) <= max_uid), uid_cuid[INSN_UID (INSN)])
#else
#define INSN_CUID(INSN) (uid_cuid[INSN_UID (INSN)])
#endif
/* Number of cuids. */
static int max_cuid;
/* Maximum register number in function prior to doing gcse + 1.
Registers created during this pass have regno >= max_gcse_regno.
This is named with "gcse" to not collide with global of same name. */
static unsigned int max_gcse_regno;
/* Table of registers that are modified.
For each register, each element is a list of places where the pseudo-reg
is set.
For simplicity, GCSE is done on sets of pseudo-regs only. PRE GCSE only
requires knowledge of which blocks kill which regs [and thus could use
a bitmap instead of the lists `reg_set_table' uses].
`reg_set_table' and could be turned into an array of bitmaps (num-bbs x
num-regs) [however perhaps it may be useful to keep the data as is]. One
advantage of recording things this way is that `reg_set_table' is fairly
sparse with respect to pseudo regs but for hard regs could be fairly dense
[relatively speaking]. And recording sets of pseudo-regs in lists speeds
up functions like compute_transp since in the case of pseudo-regs we only
need to iterate over the number of times a pseudo-reg is set, not over the
number of basic blocks [clearly there is a bit of a slow down in the cases
where a pseudo is set more than once in a block, however it is believed
that the net effect is to speed things up]. This isn't done for hard-regs
because recording call-clobbered hard-regs in `reg_set_table' at each
function call can consume a fair bit of memory, and iterating over
hard-regs stored this way in compute_transp will be more expensive. */
typedef struct reg_set
{
/* The next setting of this register. */
struct reg_set *next;
/* The index of the block where it was set. */
int bb_index;
} reg_set;
static reg_set **reg_set_table;
/* Size of `reg_set_table'.
The table starts out at max_gcse_regno + slop, and is enlarged as
necessary. */
static int reg_set_table_size;
/* Amount to grow `reg_set_table' by when it's full. */
#define REG_SET_TABLE_SLOP 100
/* This is a list of expressions which are MEMs and will be used by load
or store motion.
Load motion tracks MEMs which aren't killed by
anything except itself. (i.e., loads and stores to a single location).
We can then allow movement of these MEM refs with a little special
allowance. (all stores copy the same value to the reaching reg used
for the loads). This means all values used to store into memory must have
no side effects so we can re-issue the setter value.
Store Motion uses this structure as an expression table to track stores
which look interesting, and might be moveable towards the exit block. */
struct ls_expr
{
struct expr * expr; /* Gcse expression reference for LM. */
rtx pattern; /* Pattern of this mem. */
rtx pattern_regs; /* List of registers mentioned by the mem. */
rtx loads; /* INSN list of loads seen. */
rtx stores; /* INSN list of stores seen. */
struct ls_expr * next; /* Next in the list. */
int invalid; /* Invalid for some reason. */
int index; /* If it maps to a bitmap index. */
unsigned int hash_index; /* Index when in a hash table. */
rtx reaching_reg; /* Register to use when re-writing. */
};
/* Array of implicit set patterns indexed by basic block index. */
static rtx *implicit_sets;
/* Head of the list of load/store memory refs. */
static struct ls_expr * pre_ldst_mems = NULL;
/* Hashtable for the load/store memory refs. */
static htab_t pre_ldst_table = NULL;
/* Bitmap containing one bit for each register in the program.
Used when performing GCSE to track which registers have been set since
the start of the basic block. */
static regset reg_set_bitmap;
/* For each block, a bitmap of registers set in the block.
This is used by compute_transp.
It is computed during hash table computation and not by compute_sets
as it includes registers added since the last pass (or between cprop and
gcse) and it's currently not easy to realloc sbitmap vectors. */
static sbitmap *reg_set_in_block;
/* Array, indexed by basic block number for a list of insns which modify
memory within that block. */
static rtx * modify_mem_list;
static bitmap modify_mem_list_set;
/* This array parallels modify_mem_list, but is kept canonicalized. */
static rtx * canon_modify_mem_list;
/* Bitmap indexed by block numbers to record which blocks contain
function calls. */
static bitmap blocks_with_calls;
/* Various variables for statistics gathering. */
/* Memory used in a pass.
This isn't intended to be absolutely precise. Its intent is only
to keep an eye on memory usage. */
static int bytes_used;
/* GCSE substitutions made. */
static int gcse_subst_count;
/* Number of copy instructions created. */
static int gcse_create_count;
/* Number of local constants propagated. */
static int local_const_prop_count;
/* Number of local copies propagated. */
static int local_copy_prop_count;
/* Number of global constants propagated. */
static int global_const_prop_count;
/* Number of global copies propagated. */
static int global_copy_prop_count;
/* For available exprs */
static sbitmap *ae_kill, *ae_gen;
static void compute_can_copy (void);
static void *gmalloc (size_t) ATTRIBUTE_MALLOC;
static void *gcalloc (size_t, size_t) ATTRIBUTE_MALLOC;
static void *grealloc (void *, size_t);
static void *gcse_alloc (unsigned long);
static void alloc_gcse_mem (void);
static void free_gcse_mem (void);
static void alloc_reg_set_mem (int);
static void free_reg_set_mem (void);
static void record_one_set (int, rtx);
static void record_set_info (rtx, const_rtx, void *);
static void compute_sets (void);
static void hash_scan_insn (rtx, struct hash_table *, int);
static void hash_scan_set (rtx, rtx, struct hash_table *);
static void hash_scan_clobber (rtx, rtx, struct hash_table *);
static void hash_scan_call (rtx, rtx, struct hash_table *);
static int want_to_gcse_p (rtx);
static bool can_assign_to_reg_p (rtx);
static bool gcse_constant_p (const_rtx);
static int oprs_unchanged_p (const_rtx, const_rtx, int);
static int oprs_anticipatable_p (const_rtx, const_rtx);
static int oprs_available_p (const_rtx, const_rtx);
static void insert_expr_in_table (rtx, enum machine_mode, rtx, int, int,
struct hash_table *);
static void insert_set_in_table (rtx, rtx, struct hash_table *);
static unsigned int hash_expr (const_rtx, enum machine_mode, int *, int);
static unsigned int hash_set (int, int);
static int expr_equiv_p (const_rtx, const_rtx);
static void record_last_reg_set_info (rtx, int);
static void record_last_mem_set_info (rtx);
static void record_last_set_info (rtx, const_rtx, void *);
static void compute_hash_table (struct hash_table *);
static void alloc_hash_table (int, struct hash_table *, int);
static void free_hash_table (struct hash_table *);
static void compute_hash_table_work (struct hash_table *);
static void dump_hash_table (FILE *, const char *, struct hash_table *);
static struct expr *lookup_set (unsigned int, struct hash_table *);
static struct expr *next_set (unsigned int, struct expr *);
static void reset_opr_set_tables (void);
static int oprs_not_set_p (const_rtx, const_rtx);
static void mark_call (rtx);
static void mark_set (rtx, rtx);
static void mark_clobber (rtx, rtx);
static void mark_oprs_set (rtx);
static void alloc_cprop_mem (int, int);
static void free_cprop_mem (void);
static void compute_transp (const_rtx, int, sbitmap *, int);
static void compute_transpout (void);
static void compute_local_properties (sbitmap *, sbitmap *, sbitmap *,
struct hash_table *);
static void compute_cprop_data (void);
static void find_used_regs (rtx *, void *);
static int try_replace_reg (rtx, rtx, rtx);
static struct expr *find_avail_set (int, rtx);
static int cprop_jump (basic_block, rtx, rtx, rtx, rtx);
static void mems_conflict_for_gcse_p (rtx, const_rtx, void *);
static int load_killed_in_block_p (const_basic_block, int, const_rtx, int);
static void canon_list_insert (rtx, const_rtx, void *);
static int cprop_insn (rtx, int);
static int cprop (int);
static void find_implicit_sets (void);
static int one_cprop_pass (int, bool, bool);
static bool constprop_register (rtx, rtx, rtx, bool);
static struct expr *find_bypass_set (int, int);
static bool reg_killed_on_edge (const_rtx, const_edge);
static int bypass_block (basic_block, rtx, rtx);
static int bypass_conditional_jumps (void);
static void alloc_pre_mem (int, int);
static void free_pre_mem (void);
static void compute_pre_data (void);
static int pre_expr_reaches_here_p (basic_block, struct expr *,
basic_block);
static void insert_insn_end_basic_block (struct expr *, basic_block, int);
static void pre_insert_copy_insn (struct expr *, rtx);
static void pre_insert_copies (void);
static int pre_delete (void);
static int pre_gcse (void);
static int one_pre_gcse_pass (int);
static void add_label_notes (rtx, rtx);
static void alloc_code_hoist_mem (int, int);
static void free_code_hoist_mem (void);
static void compute_code_hoist_vbeinout (void);
static void compute_code_hoist_data (void);
static int hoist_expr_reaches_here_p (basic_block, int, basic_block, char *);
static void hoist_code (void);
static int one_code_hoisting_pass (void);
static rtx process_insert_insn (struct expr *);
static int pre_edge_insert (struct edge_list *, struct expr **);
static int pre_expr_reaches_here_p_work (basic_block, struct expr *,
basic_block, char *);
static struct ls_expr * ldst_entry (rtx);
static void free_ldst_entry (struct ls_expr *);
static void free_ldst_mems (void);
static void print_ldst_list (FILE *);
static struct ls_expr * find_rtx_in_ldst (rtx);
static int enumerate_ldsts (void);
static inline struct ls_expr * first_ls_expr (void);
static inline struct ls_expr * next_ls_expr (struct ls_expr *);
static int simple_mem (const_rtx);
static void invalidate_any_buried_refs (rtx);
static void compute_ld_motion_mems (void);
static void trim_ld_motion_mems (void);
static void update_ld_motion_stores (struct expr *);
static void reg_set_info (rtx, const_rtx, void *);
static void reg_clear_last_set (rtx, const_rtx, void *);
static bool store_ops_ok (const_rtx, int *);
static rtx extract_mentioned_regs (rtx);
static rtx extract_mentioned_regs_helper (rtx, rtx);
static void find_moveable_store (rtx, int *, int *);
static int compute_store_table (void);
static bool load_kills_store (const_rtx, const_rtx, int);
static bool find_loads (const_rtx, const_rtx, int);
static bool store_killed_in_insn (const_rtx, const_rtx, const_rtx, int);
static bool store_killed_after (const_rtx, const_rtx, const_rtx, const_basic_block, int *, rtx *);
static bool store_killed_before (const_rtx, const_rtx, const_rtx, const_basic_block, int *);
static void build_store_vectors (void);
static void insert_insn_start_basic_block (rtx, basic_block);
static int insert_store (struct ls_expr *, edge);
static void remove_reachable_equiv_notes (basic_block, struct ls_expr *);
static void replace_store_insn (rtx, rtx, basic_block, struct ls_expr *);
static void delete_store (struct ls_expr *, basic_block);
static void free_store_memory (void);
static void store_motion (void);
static void free_insn_expr_list_list (rtx *);
static void clear_modify_mem_tables (void);
static void free_modify_mem_tables (void);
static rtx gcse_emit_move_after (rtx, rtx, rtx);
static void local_cprop_find_used_regs (rtx *, void *);
static bool do_local_cprop (rtx, rtx, bool, rtx*);
static bool adjust_libcall_notes (rtx, rtx, rtx, rtx*);
static void local_cprop_pass (bool);
static bool is_too_expensive (const char *);
/* Entry point for global common subexpression elimination.
F is the first instruction in the function. Return nonzero if a
change is mode. */
static int
gcse_main (rtx f ATTRIBUTE_UNUSED)
{
int changed, pass;
/* Bytes used at start of pass. */
int initial_bytes_used;
/* Maximum number of bytes used by a pass. */
int max_pass_bytes;
/* Point to release obstack data from for each pass. */
char *gcse_obstack_bottom;
/* We do not construct an accurate cfg in functions which call
setjmp, so just punt to be safe. */
if (current_function_calls_setjmp)
return 0;
/* Assume that we do not need to run jump optimizations after gcse. */
run_jump_opt_after_gcse = 0;
/* Identify the basic block information for this function, including
successors and predecessors. */
max_gcse_regno = max_reg_num ();
df_note_add_problem ();
df_analyze ();
if (dump_file)
dump_flow_info (dump_file, dump_flags);
/* Return if there's nothing to do, or it is too expensive. */
if (n_basic_blocks <= NUM_FIXED_BLOCKS + 1
|| is_too_expensive (_("GCSE disabled")))
return 0;
gcc_obstack_init (&gcse_obstack);
bytes_used = 0;
/* We need alias. */
init_alias_analysis ();
/* Record where pseudo-registers are set. This data is kept accurate
during each pass. ??? We could also record hard-reg information here
[since it's unchanging], however it is currently done during hash table
computation.
It may be tempting to compute MEM set information here too, but MEM sets
will be subject to code motion one day and thus we need to compute
information about memory sets when we build the hash tables. */
alloc_reg_set_mem (max_gcse_regno);
compute_sets ();
pass = 0;
initial_bytes_used = bytes_used;
max_pass_bytes = 0;
gcse_obstack_bottom = gcse_alloc (1);
changed = 1;
while (changed && pass < MAX_GCSE_PASSES)
{
changed = 0;
if (dump_file)
fprintf (dump_file, "GCSE pass %d\n\n", pass + 1);
/* Initialize bytes_used to the space for the pred/succ lists,
and the reg_set_table data. */
bytes_used = initial_bytes_used;
/* Each pass may create new registers, so recalculate each time. */
max_gcse_regno = max_reg_num ();
alloc_gcse_mem ();
/* Don't allow constant propagation to modify jumps
during this pass. */
timevar_push (TV_CPROP1);
changed = one_cprop_pass (pass + 1, false, false);
timevar_pop (TV_CPROP1);
if (optimize_size)
/* Do nothing. */ ;
else
{
timevar_push (TV_PRE);
changed |= one_pre_gcse_pass (pass + 1);
/* We may have just created new basic blocks. Release and
recompute various things which are sized on the number of
basic blocks. */
if (changed)
{
free_modify_mem_tables ();
modify_mem_list = gcalloc (last_basic_block, sizeof (rtx));
canon_modify_mem_list = gcalloc (last_basic_block, sizeof (rtx));
}
free_reg_set_mem ();
alloc_reg_set_mem (max_reg_num ());
compute_sets ();
run_jump_opt_after_gcse = 1;
timevar_pop (TV_PRE);
}
if (max_pass_bytes < bytes_used)
max_pass_bytes = bytes_used;
/* Free up memory, then reallocate for code hoisting. We can
not re-use the existing allocated memory because the tables
will not have info for the insns or registers created by
partial redundancy elimination. */
free_gcse_mem ();
/* It does not make sense to run code hoisting unless we are optimizing
for code size -- it rarely makes programs faster, and can make
them bigger if we did partial redundancy elimination (when optimizing
for space, we don't run the partial redundancy algorithms). */
if (optimize_size)
{
timevar_push (TV_HOIST);
max_gcse_regno = max_reg_num ();
alloc_gcse_mem ();
changed |= one_code_hoisting_pass ();
free_gcse_mem ();
if (max_pass_bytes < bytes_used)
max_pass_bytes = bytes_used;
timevar_pop (TV_HOIST);
}
if (dump_file)
{
fprintf (dump_file, "\n");
fflush (dump_file);
}
obstack_free (&gcse_obstack, gcse_obstack_bottom);
pass++;
}
/* Do one last pass of copy propagation, including cprop into
conditional jumps. */
max_gcse_regno = max_reg_num ();
alloc_gcse_mem ();
/* This time, go ahead and allow cprop to alter jumps. */
timevar_push (TV_CPROP2);
one_cprop_pass (pass + 1, true, true);
timevar_pop (TV_CPROP2);
free_gcse_mem ();
if (dump_file)
{
fprintf (dump_file, "GCSE of %s: %d basic blocks, ",
current_function_name (), n_basic_blocks);
fprintf (dump_file, "%d pass%s, %d bytes\n\n",
pass, pass > 1 ? "es" : "", max_pass_bytes);
}
obstack_free (&gcse_obstack, NULL);
free_reg_set_mem ();
/* We are finished with alias. */
end_alias_analysis ();
if (!optimize_size && flag_gcse_sm)
{
timevar_push (TV_LSM);
store_motion ();
timevar_pop (TV_LSM);
}
/* Record where pseudo-registers are set. */
return run_jump_opt_after_gcse;
}
/* Misc. utilities. */
/* Nonzero for each mode that supports (set (reg) (reg)).
This is trivially true for integer and floating point values.
It may or may not be true for condition codes. */
static char can_copy[(int) NUM_MACHINE_MODES];
/* Compute which modes support reg/reg copy operations. */
static void
compute_can_copy (void)
{
int i;
#ifndef AVOID_CCMODE_COPIES
rtx reg, insn;
#endif
memset (can_copy, 0, NUM_MACHINE_MODES);
start_sequence ();
for (i = 0; i < NUM_MACHINE_MODES; i++)
if (GET_MODE_CLASS (i) == MODE_CC)
{
#ifdef AVOID_CCMODE_COPIES
can_copy[i] = 0;
#else
reg = gen_rtx_REG ((enum machine_mode) i, LAST_VIRTUAL_REGISTER + 1);
insn = emit_insn (gen_rtx_SET (VOIDmode, reg, reg));
if (recog (PATTERN (insn), insn, NULL) >= 0)
can_copy[i] = 1;
#endif
}
else
can_copy[i] = 1;
end_sequence ();
}
/* Returns whether the mode supports reg/reg copy operations. */
bool
can_copy_p (enum machine_mode mode)
{
static bool can_copy_init_p = false;
if (! can_copy_init_p)
{
compute_can_copy ();
can_copy_init_p = true;
}
return can_copy[mode] != 0;
}
/* Cover function to xmalloc to record bytes allocated. */
static void *
gmalloc (size_t size)
{
bytes_used += size;
return xmalloc (size);
}
/* Cover function to xcalloc to record bytes allocated. */
static void *
gcalloc (size_t nelem, size_t elsize)
{
bytes_used += nelem * elsize;
return xcalloc (nelem, elsize);
}
/* Cover function to xrealloc.
We don't record the additional size since we don't know it.
It won't affect memory usage stats much anyway. */
static void *
grealloc (void *ptr, size_t size)
{
return xrealloc (ptr, size);
}
/* Cover function to obstack_alloc. */
static void *
gcse_alloc (unsigned long size)
{
bytes_used += size;
return obstack_alloc (&gcse_obstack, size);
}
/* Allocate memory for the cuid mapping array,
and reg/memory set tracking tables.
This is called at the start of each pass. */
static void
alloc_gcse_mem (void)
{
int i;
basic_block bb;
rtx insn;
/* Find the largest UID and create a mapping from UIDs to CUIDs.
CUIDs are like UIDs except they increase monotonically, have no gaps,
and only apply to real insns.
(Actually, there are gaps, for insn that are not inside a basic block.
but we should never see those anyway, so this is OK.) */
max_uid = get_max_uid ();
uid_cuid = gcalloc (max_uid + 1, sizeof (int));
i = 0;
FOR_EACH_BB (bb)
FOR_BB_INSNS (bb, insn)
{
if (INSN_P (insn))
uid_cuid[INSN_UID (insn)] = i++;
else
uid_cuid[INSN_UID (insn)] = i;
}
max_cuid = i;
/* Allocate vars to track sets of regs. */
reg_set_bitmap = BITMAP_ALLOC (NULL);
/* Allocate vars to track sets of regs, memory per block. */
reg_set_in_block = sbitmap_vector_alloc (last_basic_block, max_gcse_regno);
/* Allocate array to keep a list of insns which modify memory in each
basic block. */
modify_mem_list = gcalloc (last_basic_block, sizeof (rtx));
canon_modify_mem_list = gcalloc (last_basic_block, sizeof (rtx));
modify_mem_list_set = BITMAP_ALLOC (NULL);
blocks_with_calls = BITMAP_ALLOC (NULL);
}
/* Free memory allocated by alloc_gcse_mem. */
static void
free_gcse_mem (void)
{
free (uid_cuid);
BITMAP_FREE (reg_set_bitmap);
sbitmap_vector_free (reg_set_in_block);
free_modify_mem_tables ();
BITMAP_FREE (modify_mem_list_set);
BITMAP_FREE (blocks_with_calls);
}
/* Compute the local properties of each recorded expression.
Local properties are those that are defined by the block, irrespective of
other blocks.
An expression is transparent in a block if its operands are not modified
in the block.
An expression is computed (locally available) in a block if it is computed
at least once and expression would contain the same value if the
computation was moved to the end of the block.
An expression is locally anticipatable in a block if it is computed at
least once and expression would contain the same value if the computation
was moved to the beginning of the block.
We call this routine for cprop, pre and code hoisting. They all compute
basically the same information and thus can easily share this code.
TRANSP, COMP, and ANTLOC are destination sbitmaps for recording local
properties. If NULL, then it is not necessary to compute or record that
particular property.
TABLE controls which hash table to look at. If it is set hash table,
additionally, TRANSP is computed as ~TRANSP, since this is really cprop's
ABSALTERED. */
static void
compute_local_properties (sbitmap *transp, sbitmap *comp, sbitmap *antloc,
struct hash_table *table)
{
unsigned int i;