OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-address.c
1 /* Memory address lowering and addressing mode selection.
2    Copyright (C) 2004 Free Software Foundation, Inc.
3    
4 This file is part of GCC.
5    
6 GCC is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10    
11 GCC is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 for more details.
15    
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING.  If not, write to the Free
18 Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.  */
20
21 /* Utility functions for manipulation with TARGET_MEM_REFs -- tree expressions
22    that directly map to addressing modes of the target.  */
23
24 #include "config.h"
25 #include "system.h"
26 #include "coretypes.h"
27 #include "tm.h"
28 #include "tree.h"
29 #include "rtl.h"
30 #include "tm_p.h"
31 #include "hard-reg-set.h"
32 #include "basic-block.h"
33 #include "output.h"
34 #include "diagnostic.h"
35 #include "tree-flow.h"
36 #include "tree-dump.h"
37 #include "tree-pass.h"
38 #include "timevar.h"
39 #include "flags.h"
40 #include "tree-inline.h"
41 #include "insn-config.h"
42 #include "recog.h"
43 #include "expr.h"
44 #include "ggc.h"
45 #include "tree-affine.h"
46
47 /* TODO -- handling of symbols (according to Richard Hendersons
48    comments, http://gcc.gnu.org/ml/gcc-patches/2005-04/msg00949.html):
49    
50    There are at least 5 different kinds of symbols that we can run up against:
51
52      (1) binds_local_p, small data area.
53      (2) binds_local_p, eg local statics
54      (3) !binds_local_p, eg global variables
55      (4) thread local, local_exec
56      (5) thread local, !local_exec
57
58    Now, (1) won't appear often in an array context, but it certainly can.
59    All you have to do is set -GN high enough, or explicitly mark any
60    random object __attribute__((section (".sdata"))).
61
62    All of these affect whether or not a symbol is in fact a valid address.
63    The only one tested here is (3).  And that result may very well
64    be incorrect for (4) or (5).
65
66    An incorrect result here does not cause incorrect results out the
67    back end, because the expander in expr.c validizes the address.  However
68    it would be nice to improve the handling here in order to produce more
69    precise results.  */
70
71 /* A "template" for memory address, used to determine whether the address is
72    valid for mode.  */
73
74 struct mem_addr_template GTY (())
75 {
76   rtx ref;                      /* The template.  */
77   rtx * GTY ((skip)) step_p;    /* The point in template where the step should be
78                                    filled in.  */
79   rtx * GTY ((skip)) off_p;     /* The point in template where the offset should
80                                    be filled in.  */
81 };
82
83 /* The templates.  Each of the five bits of the index corresponds to one
84    component of TARGET_MEM_REF being present, see TEMPL_IDX.  */
85
86 static GTY (()) struct mem_addr_template templates[32];
87
88 #define TEMPL_IDX(SYMBOL, BASE, INDEX, STEP, OFFSET) \
89   (((SYMBOL != 0) << 4) \
90    | ((BASE != 0) << 3) \
91    | ((INDEX != 0) << 2) \
92    | ((STEP != 0) << 1) \
93    | (OFFSET != 0))
94
95 /* Stores address for memory reference with parameters SYMBOL, BASE, INDEX,
96    STEP and OFFSET to *ADDR.  Stores pointers to where step is placed to
97    *STEP_P and offset to *OFFSET_P.  */
98
99 static void
100 gen_addr_rtx (rtx symbol, rtx base, rtx index, rtx step, rtx offset,
101               rtx *addr, rtx **step_p, rtx **offset_p)
102 {
103   rtx act_elem;
104
105   *addr = NULL_RTX;
106   if (step_p)
107     *step_p = NULL;
108   if (offset_p)
109     *offset_p = NULL;
110
111   if (index)
112     {
113       act_elem = index;
114       if (step)
115         {
116           act_elem = gen_rtx_MULT (Pmode, act_elem, step);
117
118           if (step_p)
119             *step_p = &XEXP (act_elem, 1);
120         }
121
122       *addr = act_elem;
123     }
124
125   if (base)
126     {
127       if (*addr)
128         *addr = gen_rtx_PLUS (Pmode, *addr, base);
129       else
130         *addr = base;
131     }
132
133   if (symbol)
134     {
135       act_elem = symbol;
136       if (offset)
137         {
138           act_elem = gen_rtx_CONST (Pmode,
139                                     gen_rtx_PLUS (Pmode, act_elem, offset));
140           if (offset_p)
141             *offset_p = &XEXP (XEXP (act_elem, 0), 1);
142         }
143
144       if (*addr)
145         *addr = gen_rtx_PLUS (Pmode, *addr, act_elem);
146       else
147         *addr = act_elem;
148     }
149   else if (offset)
150     {
151       if (*addr)
152         {
153           *addr = gen_rtx_PLUS (Pmode, *addr, offset);
154           if (offset_p)
155             *offset_p = &XEXP (*addr, 1);
156         }
157       else
158         {
159           *addr = offset;
160           if (offset_p)
161             *offset_p = addr;
162         }
163     }
164
165   if (!*addr)
166     *addr = const0_rtx;
167 }
168
169 /* Returns address for TARGET_MEM_REF with parameters given by ADDR.
170    If REALLY_EXPAND is false, just make fake registers instead 
171    of really expanding the operands, and perform the expansion in-place
172    by using one of the "templates".  */
173
174 rtx
175 addr_for_mem_ref (struct mem_address *addr, bool really_expand)
176 {
177   rtx address, sym, bse, idx, st, off;
178   static bool templates_initialized = false;
179   struct mem_addr_template *templ;
180
181   if (addr->step && !integer_onep (addr->step))
182     st = immed_double_const (TREE_INT_CST_LOW (addr->step),
183                              TREE_INT_CST_HIGH (addr->step), Pmode);
184   else
185     st = NULL_RTX;
186
187   if (addr->offset && !integer_zerop (addr->offset))
188     off = immed_double_const (TREE_INT_CST_LOW (addr->offset),
189                               TREE_INT_CST_HIGH (addr->offset), Pmode);
190   else
191     off = NULL_RTX;
192
193   if (!really_expand)
194     {
195       /* Reuse the templates for addresses, so that we do not waste memory.  */
196       if (!templates_initialized)
197         {
198           unsigned i;
199
200           templates_initialized = true;
201           sym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup ("test_symbol"));
202           bse = gen_raw_REG (Pmode, LAST_VIRTUAL_REGISTER + 1);
203           idx = gen_raw_REG (Pmode, LAST_VIRTUAL_REGISTER + 2);
204
205           for (i = 0; i < 32; i++)
206             gen_addr_rtx ((i & 16 ? sym : NULL_RTX),
207                           (i & 8 ? bse : NULL_RTX),
208                           (i & 4 ? idx : NULL_RTX),
209                           (i & 2 ? const0_rtx : NULL_RTX),
210                           (i & 1 ? const0_rtx : NULL_RTX),
211                           &templates[i].ref,
212                           &templates[i].step_p,
213                           &templates[i].off_p);
214         }
215
216       templ = templates + TEMPL_IDX (addr->symbol, addr->base, addr->index,
217                                      st, off);
218       if (st)
219         *templ->step_p = st;
220       if (off)
221         *templ->off_p = off;
222
223       return templ->ref;
224     }
225
226   /* Otherwise really expand the expressions.  */
227   sym = (addr->symbol
228          ? expand_expr (build_addr (addr->symbol, current_function_decl),
229                         NULL_RTX, Pmode, EXPAND_NORMAL)
230          : NULL_RTX);
231   bse = (addr->base
232          ? expand_expr (addr->base, NULL_RTX, Pmode, EXPAND_NORMAL)
233          : NULL_RTX);
234   idx = (addr->index
235          ? expand_expr (addr->index, NULL_RTX, Pmode, EXPAND_NORMAL)
236          : NULL_RTX);
237
238   gen_addr_rtx (sym, bse, idx, st, off, &address, NULL, NULL);
239   return address;
240 }
241
242 /* Returns address of MEM_REF in TYPE.  */
243
244 tree
245 tree_mem_ref_addr (tree type, tree mem_ref)
246 {
247   tree addr;
248   tree act_elem;
249   tree step = TMR_STEP (mem_ref), offset = TMR_OFFSET (mem_ref);
250   tree sym = TMR_SYMBOL (mem_ref), base = TMR_BASE (mem_ref);
251   tree addr_base = NULL_TREE, addr_off = NULL_TREE;
252
253   if (sym)
254     addr_base = fold_convert (type, build_addr (sym, current_function_decl));
255   else if (base && POINTER_TYPE_P (TREE_TYPE (base)))
256     {
257       addr_base = fold_convert (type, base);
258       base = NULL_TREE;
259     }
260
261   act_elem = TMR_INDEX (mem_ref);
262   if (act_elem)
263     {
264       if (step)
265         act_elem = fold_build2 (MULT_EXPR, sizetype, act_elem, step);
266       addr_off = act_elem;
267     }
268
269   act_elem = base;
270   if (act_elem)
271     {
272       if (addr_off)
273         addr_off = fold_build2 (PLUS_EXPR, sizetype, addr_off, act_elem);
274       else
275         addr_off = act_elem;
276     }
277
278   if (offset && !integer_zerop (offset))
279     {
280       if (addr_off)
281         addr_off = fold_build2 (PLUS_EXPR, sizetype, addr_off, offset);
282       else
283         addr_off = offset;
284     }
285
286   if (addr_off)
287     {
288       addr = fold_convert (type, addr_off);
289       if (addr_base)
290         addr = fold_build2 (PLUS_EXPR, type, addr_base, addr);
291     }
292   else if (addr_base)
293     addr = addr_base;
294   else
295     addr = build_int_cst (type, 0);
296
297   return addr;
298 }
299
300 /* Returns true if a memory reference in MODE and with parameters given by
301    ADDR is valid on the current target.  */
302
303 static bool
304 valid_mem_ref_p (enum machine_mode mode, struct mem_address *addr)
305 {
306   rtx address;
307
308   address = addr_for_mem_ref (addr, false);
309   if (!address)
310     return false;
311
312   return memory_address_p (mode, address);
313 }
314
315 /* Checks whether a TARGET_MEM_REF with type TYPE and parameters given by ADDR
316    is valid on the current target and if so, creates and returns the
317    TARGET_MEM_REF.  */
318
319 static tree
320 create_mem_ref_raw (tree type, struct mem_address *addr)
321 {
322   if (!valid_mem_ref_p (TYPE_MODE (type), addr))
323     return NULL_TREE;
324
325   if (addr->step && integer_onep (addr->step))
326     addr->step = NULL_TREE;
327
328   if (addr->offset && integer_zerop (addr->offset))
329     addr->offset = NULL_TREE;
330
331   return build7 (TARGET_MEM_REF, type,
332                  addr->symbol, addr->base, addr->index,
333                  addr->step, addr->offset, NULL, NULL);
334 }
335
336 /* Returns true if OBJ is an object whose address is a link time constant.  */
337
338 static bool
339 fixed_address_object_p (tree obj)
340 {
341   return (TREE_CODE (obj) == VAR_DECL
342           && (TREE_STATIC (obj)
343               || DECL_EXTERNAL (obj)));
344 }
345
346 /* If ADDR contains an address of object that is a link time constant,
347    move it to PARTS->symbol.  */
348
349 static void
350 move_fixed_address_to_symbol (struct mem_address *parts, aff_tree *addr)
351 {
352   unsigned i;
353   tree val = NULL_TREE;
354
355   for (i = 0; i < addr->n; i++)
356     {
357       if (!double_int_one_p (addr->elts[i].coef))
358         continue;
359
360       val = addr->elts[i].val;
361       if (TREE_CODE (val) == ADDR_EXPR
362           && fixed_address_object_p (TREE_OPERAND (val, 0)))
363         break;
364     }
365
366   if (i == addr->n)
367     return;
368
369   parts->symbol = TREE_OPERAND (val, 0);
370   aff_combination_remove_elt (addr, i);
371 }
372
373 /* If ADDR contains an address of a dereferenced pointer, move it to
374    PARTS->base.  */
375
376 static void
377 move_pointer_to_base (struct mem_address *parts, aff_tree *addr)
378 {
379   unsigned i;
380   tree val = NULL_TREE;
381
382   for (i = 0; i < addr->n; i++)
383     {
384       if (!double_int_one_p (addr->elts[i].coef))
385         continue;
386
387       val = addr->elts[i].val;
388       if (POINTER_TYPE_P (TREE_TYPE (val)))
389         break;
390     }
391
392   if (i == addr->n)
393     return;
394
395   parts->base = val;
396   aff_combination_remove_elt (addr, i);
397 }
398
399 /* Adds ELT to PARTS.  */
400
401 static void
402 add_to_parts (struct mem_address *parts, tree elt)
403 {
404   tree type;
405
406   if (!parts->index)
407     {
408       parts->index = elt;
409       return;
410     }
411
412   if (!parts->base)
413     {
414       parts->base = elt;
415       return;
416     }
417
418   /* Add ELT to base.  */
419   type = TREE_TYPE (parts->base);
420   parts->base = fold_build2 (PLUS_EXPR, type,
421                              parts->base,
422                              fold_convert (type, elt));
423 }
424
425 /* Finds the most expensive multiplication in ADDR that can be
426    expressed in an addressing mode and move the corresponding
427    element(s) to PARTS.  */
428
429 static void
430 most_expensive_mult_to_index (struct mem_address *parts, aff_tree *addr)
431 {
432   HOST_WIDE_INT coef;
433   double_int best_mult, amult, amult_neg;
434   unsigned best_mult_cost = 0, acost;
435   tree mult_elt = NULL_TREE, elt;
436   unsigned i, j;
437   enum tree_code op_code;
438
439   best_mult = double_int_zero;
440   for (i = 0; i < addr->n; i++)
441     {
442       if (!double_int_fits_in_shwi_p (addr->elts[i].coef))
443         continue;
444
445       /* FIXME: Should use the correct memory mode rather than Pmode.  */
446
447       coef = double_int_to_shwi (addr->elts[i].coef);
448       if (coef == 1
449           || !multiplier_allowed_in_address_p (coef, Pmode))
450         continue;
451
452       acost = multiply_by_cost (coef, Pmode);
453
454       if (acost > best_mult_cost)
455         {
456           best_mult_cost = acost;
457           best_mult = addr->elts[i].coef;
458         }
459     }
460
461   if (!best_mult_cost)
462     return;
463
464   /* Collect elements multiplied by best_mult.  */
465   for (i = j = 0; i < addr->n; i++)
466     {
467       amult = addr->elts[i].coef;
468       amult_neg = double_int_ext_for_comb (double_int_neg (amult), addr);
469  
470       if (double_int_equal_p (amult, best_mult))
471         op_code = PLUS_EXPR;
472       else if (double_int_equal_p (amult_neg, best_mult))
473         op_code = MINUS_EXPR;
474       else
475         {
476           addr->elts[j] = addr->elts[i];
477           j++;
478           continue;
479         }
480   
481       elt = fold_convert (sizetype, addr->elts[i].val);
482       if (mult_elt)
483         mult_elt = fold_build2 (op_code, sizetype, mult_elt, elt);
484       else if (op_code == PLUS_EXPR)
485         mult_elt = elt;
486       else
487         mult_elt = fold_build1 (NEGATE_EXPR, sizetype, elt);
488     }
489   addr->n = j;
490   
491   parts->index = mult_elt;
492   parts->step = double_int_to_tree (sizetype, best_mult);
493 }
494
495 /* Splits address ADDR into PARTS.
496    
497    TODO -- be more clever about the distribution of the elements of ADDR
498    to PARTS.  Some architectures do not support anything but single
499    register in address, possibly with a small integer offset; while
500    create_mem_ref will simplify the address to an acceptable shape
501    later, it would be more efficient to know that asking for complicated
502    addressing modes is useless.  */
503
504 static void
505 addr_to_parts (aff_tree *addr, struct mem_address *parts)
506 {
507   tree part;
508   unsigned i;
509
510   parts->symbol = NULL_TREE;
511   parts->base = NULL_TREE;
512   parts->index = NULL_TREE;
513   parts->step = NULL_TREE;
514
515   if (!double_int_zero_p (addr->offset))
516     parts->offset = double_int_to_tree (sizetype, addr->offset);
517   else
518     parts->offset = NULL_TREE;
519
520   /* Try to find a symbol.  */
521   move_fixed_address_to_symbol (parts, addr);
522
523   /* First move the most expensive feasible multiplication
524      to index.  */
525   most_expensive_mult_to_index (parts, addr);
526
527   /* Try to find a base of the reference.  Since at the moment
528      there is no reliable way how to distinguish between pointer and its
529      offset, this is just a guess.  */
530   if (!parts->symbol)
531     move_pointer_to_base (parts, addr);
532
533   /* Then try to process the remaining elements.  */
534   for (i = 0; i < addr->n; i++)
535     {
536       part = fold_convert (sizetype, addr->elts[i].val);
537       if (!double_int_one_p (addr->elts[i].coef))
538         part = fold_build2 (MULT_EXPR, sizetype, part,
539                             double_int_to_tree (sizetype, addr->elts[i].coef));
540       add_to_parts (parts, part);
541     }
542   if (addr->rest)
543     add_to_parts (parts, fold_convert (sizetype, addr->rest));
544 }
545
546 /* Force the PARTS to register.  */
547
548 static void
549 gimplify_mem_ref_parts (block_stmt_iterator *bsi, struct mem_address *parts)
550 {
551   if (parts->base)
552     parts->base = force_gimple_operand_bsi (bsi, parts->base,
553                                             true, NULL_TREE);
554   if (parts->index)
555     parts->index = force_gimple_operand_bsi (bsi, parts->index,
556                                              true, NULL_TREE);
557 }
558
559 /* Creates and returns a TARGET_MEM_REF for address ADDR.  If necessary
560    computations are emitted in front of BSI.  TYPE is the mode
561    of created memory reference.  */
562
563 tree
564 create_mem_ref (block_stmt_iterator *bsi, tree type, aff_tree *addr)
565 {
566   tree mem_ref, tmp;
567   tree addr_type = build_pointer_type (type), atype;
568   struct mem_address parts;
569
570   addr_to_parts (addr, &parts);
571   gimplify_mem_ref_parts (bsi, &parts);
572   mem_ref = create_mem_ref_raw (type, &parts);
573   if (mem_ref)
574     return mem_ref;
575
576   /* The expression is too complicated.  Try making it simpler.  */
577
578   if (parts.step && !integer_onep (parts.step))
579     {
580       /* Move the multiplication to index.  */
581       gcc_assert (parts.index);
582       parts.index = force_gimple_operand_bsi (bsi,
583                                 fold_build2 (MULT_EXPR, sizetype,
584                                              parts.index, parts.step),
585                                 true, NULL_TREE);
586       parts.step = NULL_TREE;
587   
588       mem_ref = create_mem_ref_raw (type, &parts);
589       if (mem_ref)
590         return mem_ref;
591     }
592
593   if (parts.symbol)
594     {
595       tmp = fold_convert (addr_type,
596                           build_addr (parts.symbol, current_function_decl));
597     
598       /* Add the symbol to base, eventually forcing it to register.  */
599       if (parts.base)
600         {
601           if (parts.index)
602             parts.base = force_gimple_operand_bsi (bsi,
603                         fold_build2 (PLUS_EXPR, addr_type,
604                                      fold_convert (addr_type, parts.base),
605                                      tmp),
606                         true, NULL_TREE);
607           else
608             {
609               parts.index = parts.base;
610               parts.base = tmp;
611             }
612         }
613       else
614         parts.base = tmp;
615       parts.symbol = NULL_TREE;
616
617       mem_ref = create_mem_ref_raw (type, &parts);
618       if (mem_ref)
619         return mem_ref;
620     }
621
622   if (parts.index)
623     {
624       /* Add index to base.  */
625       if (parts.base)
626         {
627           atype = TREE_TYPE (parts.base);
628           parts.base = force_gimple_operand_bsi (bsi,
629                         fold_build2 (PLUS_EXPR, atype,
630                                      parts.base,
631                                      fold_convert (atype, parts.index)),
632                         true, NULL_TREE);
633         }
634       else
635         parts.base = parts.index;
636       parts.index = NULL_TREE;
637
638       mem_ref = create_mem_ref_raw (type, &parts);
639       if (mem_ref)
640         return mem_ref;
641     }
642
643   if (parts.offset && !integer_zerop (parts.offset))
644     {
645       /* Try adding offset to base.  */
646       if (parts.base)
647         {
648           atype = TREE_TYPE (parts.base);
649           parts.base = force_gimple_operand_bsi (bsi, 
650                         fold_build2 (PLUS_EXPR, atype,
651                                      parts.base,
652                                      fold_convert (atype, parts.offset)),
653                         true, NULL_TREE);
654         }
655       else
656         parts.base = parts.offset, bsi;
657
658       parts.offset = NULL_TREE;
659
660       mem_ref = create_mem_ref_raw (type, &parts);
661       if (mem_ref)
662         return mem_ref;
663     }
664
665   /* Verify that the address is in the simplest possible shape
666      (only a register).  If we cannot create such a memory reference,
667      something is really wrong.  */
668   gcc_assert (parts.symbol == NULL_TREE);
669   gcc_assert (parts.index == NULL_TREE);
670   gcc_assert (!parts.step || integer_onep (parts.step));
671   gcc_assert (!parts.offset || integer_zerop (parts.offset));
672   gcc_unreachable ();
673 }
674
675 /* Copies components of the address from OP to ADDR.  */
676
677 void
678 get_address_description (tree op, struct mem_address *addr)
679 {
680   addr->symbol = TMR_SYMBOL (op);
681   addr->base = TMR_BASE (op);
682   addr->index = TMR_INDEX (op);
683   addr->step = TMR_STEP (op);
684   addr->offset = TMR_OFFSET (op);
685 }
686
687 /* Copies the additional information attached to target_mem_ref FROM to TO.  */
688
689 void
690 copy_mem_ref_info (tree to, tree from)
691 {
692   /* Copy the annotation, to preserve the aliasing information.  */
693   TMR_TAG (to) = TMR_TAG (from);
694
695   /* And the info about the original reference.  */
696   TMR_ORIGINAL (to) = TMR_ORIGINAL (from);
697 }
698
699 /* Move constants in target_mem_ref REF to offset.  Returns the new target
700    mem ref if anything changes, NULL_TREE otherwise.  */
701
702 tree
703 maybe_fold_tmr (tree ref)
704 {
705   struct mem_address addr;
706   bool changed = false;
707   tree ret, off;
708
709   get_address_description (ref, &addr);
710
711   if (addr.base && TREE_CODE (addr.base) == INTEGER_CST)
712     {
713       if (addr.offset)
714         addr.offset = fold_binary_to_constant (PLUS_EXPR, sizetype,
715                         addr.offset,
716                         fold_convert (sizetype, addr.base));
717       else
718         addr.offset = addr.base;
719
720       addr.base = NULL_TREE;
721       changed = true;
722     }
723
724   if (addr.index && TREE_CODE (addr.index) == INTEGER_CST)
725     {
726       off = addr.index;
727       if (addr.step)
728         {
729           off = fold_binary_to_constant (MULT_EXPR, sizetype,
730                                          off, addr.step);
731           addr.step = NULL_TREE;
732         }
733
734       if (addr.offset)
735         {
736           addr.offset = fold_binary_to_constant (PLUS_EXPR, sizetype,
737                                                  addr.offset, off);
738         }
739       else
740         addr.offset = off;
741
742       addr.index = NULL_TREE;
743       changed = true;
744     }
745
746   if (!changed)
747     return NULL_TREE;
748   
749   ret = create_mem_ref_raw (TREE_TYPE (ref), &addr);
750   if (!ret)
751     return NULL_TREE;
752
753   copy_mem_ref_info (ret, ref);
754   return ret;
755 }
756
757 /* Dump PARTS to FILE.  */
758
759 extern void dump_mem_address (FILE *, struct mem_address *);
760 void
761 dump_mem_address (FILE *file, struct mem_address *parts)
762 {
763   if (parts->symbol)
764     {
765       fprintf (file, "symbol: ");
766       print_generic_expr (file, parts->symbol, TDF_SLIM);
767       fprintf (file, "\n");
768     }
769   if (parts->base)
770     {
771       fprintf (file, "base: ");
772       print_generic_expr (file, parts->base, TDF_SLIM);
773       fprintf (file, "\n");
774     }
775   if (parts->index)
776     {
777       fprintf (file, "index: ");
778       print_generic_expr (file, parts->index, TDF_SLIM);
779       fprintf (file, "\n");
780     }
781   if (parts->step)
782     {
783       fprintf (file, "step: ");
784       print_generic_expr (file, parts->step, TDF_SLIM);
785       fprintf (file, "\n");
786     }
787   if (parts->offset)
788     {
789       fprintf (file, "offset: ");
790       print_generic_expr (file, parts->offset, TDF_SLIM);
791       fprintf (file, "\n");
792     }
793 }
794
795 #include "gt-tree-ssa-address.h"