OSDN Git Service

2007-06-29 Andrew Pinski <andrew_pinski@playstation.sony.com>
[pf3gnuchains/gcc-fork.git] / gcc / tree-ssa-address.c
1 /* Memory address lowering and addressing mode selection.
2    Copyright (C) 2004, 2006 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_PLUS (Pmode, act_elem, offset);
139
140           if (offset_p)
141             *offset_p = &XEXP (act_elem, 1);
142
143           if (GET_CODE (symbol) == SYMBOL_REF
144               || GET_CODE (symbol) == LABEL_REF
145               || GET_CODE (symbol) == CONST)
146             act_elem = gen_rtx_CONST (Pmode, act_elem);
147         }
148
149       if (*addr)
150         *addr = gen_rtx_PLUS (Pmode, *addr, act_elem);
151       else
152         *addr = act_elem;
153     }
154   else if (offset)
155     {
156       if (*addr)
157         {
158           *addr = gen_rtx_PLUS (Pmode, *addr, offset);
159           if (offset_p)
160             *offset_p = &XEXP (*addr, 1);
161         }
162       else
163         {
164           *addr = offset;
165           if (offset_p)
166             *offset_p = addr;
167         }
168     }
169
170   if (!*addr)
171     *addr = const0_rtx;
172 }
173
174 /* Returns address for TARGET_MEM_REF with parameters given by ADDR.
175    If REALLY_EXPAND is false, just make fake registers instead 
176    of really expanding the operands, and perform the expansion in-place
177    by using one of the "templates".  */
178
179 rtx
180 addr_for_mem_ref (struct mem_address *addr, bool really_expand)
181 {
182   rtx address, sym, bse, idx, st, off;
183   static bool templates_initialized = false;
184   struct mem_addr_template *templ;
185
186   if (addr->step && !integer_onep (addr->step))
187     st = immed_double_const (TREE_INT_CST_LOW (addr->step),
188                              TREE_INT_CST_HIGH (addr->step), Pmode);
189   else
190     st = NULL_RTX;
191
192   if (addr->offset && !integer_zerop (addr->offset))
193     off = immed_double_const (TREE_INT_CST_LOW (addr->offset),
194                               TREE_INT_CST_HIGH (addr->offset), Pmode);
195   else
196     off = NULL_RTX;
197
198   if (!really_expand)
199     {
200       /* Reuse the templates for addresses, so that we do not waste memory.  */
201       if (!templates_initialized)
202         {
203           unsigned i;
204
205           templates_initialized = true;
206           sym = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup ("test_symbol"));
207           bse = gen_raw_REG (Pmode, LAST_VIRTUAL_REGISTER + 1);
208           idx = gen_raw_REG (Pmode, LAST_VIRTUAL_REGISTER + 2);
209
210           for (i = 0; i < 32; i++)
211             gen_addr_rtx ((i & 16 ? sym : NULL_RTX),
212                           (i & 8 ? bse : NULL_RTX),
213                           (i & 4 ? idx : NULL_RTX),
214                           (i & 2 ? const0_rtx : NULL_RTX),
215                           (i & 1 ? const0_rtx : NULL_RTX),
216                           &templates[i].ref,
217                           &templates[i].step_p,
218                           &templates[i].off_p);
219         }
220
221       templ = templates + TEMPL_IDX (addr->symbol, addr->base, addr->index,
222                                      st, off);
223       if (st)
224         *templ->step_p = st;
225       if (off)
226         *templ->off_p = off;
227
228       return templ->ref;
229     }
230
231   /* Otherwise really expand the expressions.  */
232   sym = (addr->symbol
233          ? expand_expr (build_addr (addr->symbol, current_function_decl),
234                         NULL_RTX, Pmode, EXPAND_NORMAL)
235          : NULL_RTX);
236   bse = (addr->base
237          ? expand_expr (addr->base, NULL_RTX, Pmode, EXPAND_NORMAL)
238          : NULL_RTX);
239   idx = (addr->index
240          ? expand_expr (addr->index, NULL_RTX, Pmode, EXPAND_NORMAL)
241          : NULL_RTX);
242
243   gen_addr_rtx (sym, bse, idx, st, off, &address, NULL, NULL);
244   return address;
245 }
246
247 /* Returns address of MEM_REF in TYPE.  */
248
249 tree
250 tree_mem_ref_addr (tree type, tree mem_ref)
251 {
252   tree addr;
253   tree act_elem;
254   tree step = TMR_STEP (mem_ref), offset = TMR_OFFSET (mem_ref);
255   tree sym = TMR_SYMBOL (mem_ref), base = TMR_BASE (mem_ref);
256   tree addr_base = NULL_TREE, addr_off = NULL_TREE;
257
258   if (sym)
259     addr_base = fold_convert (type, build_addr (sym, current_function_decl));
260   else if (base && POINTER_TYPE_P (TREE_TYPE (base)))
261     {
262       addr_base = fold_convert (type, base);
263       base = NULL_TREE;
264     }
265
266   act_elem = TMR_INDEX (mem_ref);
267   if (act_elem)
268     {
269       if (step)
270         act_elem = fold_build2 (MULT_EXPR, sizetype, act_elem, step);
271       addr_off = act_elem;
272     }
273
274   act_elem = base;
275   if (act_elem)
276     {
277       if (addr_off)
278         addr_off = fold_build2 (PLUS_EXPR, sizetype, addr_off, act_elem);
279       else
280         addr_off = act_elem;
281     }
282
283   if (offset && !integer_zerop (offset))
284     {
285       if (addr_off)
286         addr_off = fold_build2 (PLUS_EXPR, sizetype, addr_off, offset);
287       else
288         addr_off = offset;
289     }
290
291   if (addr_off)
292     {
293       if (addr_base)
294         addr = fold_build2 (POINTER_PLUS_EXPR, type, addr_base, addr_off);
295       else
296         addr = fold_convert (type, addr_off);
297     }
298   else if (addr_base)
299     addr = addr_base;
300   else
301     addr = build_int_cst (type, 0);
302
303   return addr;
304 }
305
306 /* Returns true if a memory reference in MODE and with parameters given by
307    ADDR is valid on the current target.  */
308
309 static bool
310 valid_mem_ref_p (enum machine_mode mode, struct mem_address *addr)
311 {
312   rtx address;
313
314   address = addr_for_mem_ref (addr, false);
315   if (!address)
316     return false;
317
318   return memory_address_p (mode, address);
319 }
320
321 /* Checks whether a TARGET_MEM_REF with type TYPE and parameters given by ADDR
322    is valid on the current target and if so, creates and returns the
323    TARGET_MEM_REF.  */
324
325 static tree
326 create_mem_ref_raw (tree type, struct mem_address *addr)
327 {
328   if (!valid_mem_ref_p (TYPE_MODE (type), addr))
329     return NULL_TREE;
330
331   if (addr->step && integer_onep (addr->step))
332     addr->step = NULL_TREE;
333
334   if (addr->offset && integer_zerop (addr->offset))
335     addr->offset = NULL_TREE;
336
337   return build7 (TARGET_MEM_REF, type,
338                  addr->symbol, addr->base, addr->index,
339                  addr->step, addr->offset, NULL, NULL);
340 }
341
342 /* Returns true if OBJ is an object whose address is a link time constant.  */
343
344 static bool
345 fixed_address_object_p (tree obj)
346 {
347   return (TREE_CODE (obj) == VAR_DECL
348           && (TREE_STATIC (obj)
349               || DECL_EXTERNAL (obj)));
350 }
351
352 /* If ADDR contains an address of object that is a link time constant,
353    move it to PARTS->symbol.  */
354
355 static void
356 move_fixed_address_to_symbol (struct mem_address *parts, aff_tree *addr)
357 {
358   unsigned i;
359   tree val = NULL_TREE;
360
361   for (i = 0; i < addr->n; i++)
362     {
363       if (!double_int_one_p (addr->elts[i].coef))
364         continue;
365
366       val = addr->elts[i].val;
367       if (TREE_CODE (val) == ADDR_EXPR
368           && fixed_address_object_p (TREE_OPERAND (val, 0)))
369         break;
370     }
371
372   if (i == addr->n)
373     return;
374
375   parts->symbol = TREE_OPERAND (val, 0);
376   aff_combination_remove_elt (addr, i);
377 }
378
379 /* If ADDR contains an address of a dereferenced pointer, move it to
380    PARTS->base.  */
381
382 static void
383 move_pointer_to_base (struct mem_address *parts, aff_tree *addr)
384 {
385   unsigned i;
386   tree val = NULL_TREE;
387
388   for (i = 0; i < addr->n; i++)
389     {
390       if (!double_int_one_p (addr->elts[i].coef))
391         continue;
392
393       val = addr->elts[i].val;
394       if (POINTER_TYPE_P (TREE_TYPE (val)))
395         break;
396     }
397
398   if (i == addr->n)
399     return;
400
401   parts->base = val;
402   aff_combination_remove_elt (addr, i);
403 }
404
405 /* Adds ELT to PARTS.  */
406
407 static void
408 add_to_parts (struct mem_address *parts, tree elt)
409 {
410   tree type;
411
412   if (!parts->index)
413     {
414       parts->index = fold_convert (sizetype, elt);
415       return;
416     }
417
418   if (!parts->base)
419     {
420       parts->base = elt;
421       return;
422     }
423
424   /* Add ELT to base.  */
425   type = TREE_TYPE (parts->base);
426   parts->base = fold_build2 (PLUS_EXPR, type,
427                              parts->base,
428                              fold_convert (type, elt));
429 }
430
431 /* Finds the most expensive multiplication in ADDR that can be
432    expressed in an addressing mode and move the corresponding
433    element(s) to PARTS.  */
434
435 static void
436 most_expensive_mult_to_index (struct mem_address *parts, aff_tree *addr)
437 {
438   HOST_WIDE_INT coef;
439   double_int best_mult, amult, amult_neg;
440   unsigned best_mult_cost = 0, acost;
441   tree mult_elt = NULL_TREE, elt;
442   unsigned i, j;
443   enum tree_code op_code;
444
445   best_mult = double_int_zero;
446   for (i = 0; i < addr->n; i++)
447     {
448       if (!double_int_fits_in_shwi_p (addr->elts[i].coef))
449         continue;
450
451       /* FIXME: Should use the correct memory mode rather than Pmode.  */
452
453       coef = double_int_to_shwi (addr->elts[i].coef);
454       if (coef == 1
455           || !multiplier_allowed_in_address_p (coef, Pmode))
456         continue;
457
458       acost = multiply_by_cost (coef, Pmode);
459
460       if (acost > best_mult_cost)
461         {
462           best_mult_cost = acost;
463           best_mult = addr->elts[i].coef;
464         }
465     }
466
467   if (!best_mult_cost)
468     return;
469
470   /* Collect elements multiplied by best_mult.  */
471   for (i = j = 0; i < addr->n; i++)
472     {
473       amult = addr->elts[i].coef;
474       amult_neg = double_int_ext_for_comb (double_int_neg (amult), addr);
475  
476       if (double_int_equal_p (amult, best_mult))
477         op_code = PLUS_EXPR;
478       else if (double_int_equal_p (amult_neg, best_mult))
479         op_code = MINUS_EXPR;
480       else
481         {
482           addr->elts[j] = addr->elts[i];
483           j++;
484           continue;
485         }
486
487       elt = fold_convert (sizetype, addr->elts[i].val);
488       if (mult_elt)
489         mult_elt = fold_build2 (op_code, sizetype, mult_elt, elt);
490       else if (op_code == PLUS_EXPR)
491         mult_elt = elt;
492       else
493         mult_elt = fold_build1 (NEGATE_EXPR, sizetype, elt);
494     }
495   addr->n = j;
496   
497   parts->index = mult_elt;
498   parts->step = double_int_to_tree (sizetype, best_mult);
499 }
500
501 /* Splits address ADDR into PARTS.
502    
503    TODO -- be more clever about the distribution of the elements of ADDR
504    to PARTS.  Some architectures do not support anything but single
505    register in address, possibly with a small integer offset; while
506    create_mem_ref will simplify the address to an acceptable shape
507    later, it would be more efficient to know that asking for complicated
508    addressing modes is useless.  */
509
510 static void
511 addr_to_parts (aff_tree *addr, struct mem_address *parts)
512 {
513   tree part;
514   unsigned i;
515
516   parts->symbol = NULL_TREE;
517   parts->base = NULL_TREE;
518   parts->index = NULL_TREE;
519   parts->step = NULL_TREE;
520
521   if (!double_int_zero_p (addr->offset))
522     parts->offset = double_int_to_tree (sizetype, addr->offset);
523   else
524     parts->offset = NULL_TREE;
525
526   /* Try to find a symbol.  */
527   move_fixed_address_to_symbol (parts, addr);
528
529   /* First move the most expensive feasible multiplication
530      to index.  */
531   most_expensive_mult_to_index (parts, addr);
532
533   /* Try to find a base of the reference.  Since at the moment
534      there is no reliable way how to distinguish between pointer and its
535      offset, this is just a guess.  */
536   if (!parts->symbol)
537     move_pointer_to_base (parts, addr);
538
539   /* Then try to process the remaining elements.  */
540   for (i = 0; i < addr->n; i++)
541     {
542       part = fold_convert (sizetype, addr->elts[i].val);
543       if (!double_int_one_p (addr->elts[i].coef))
544         part = fold_build2 (MULT_EXPR, sizetype, part,
545                             double_int_to_tree (sizetype, addr->elts[i].coef));
546       add_to_parts (parts, part);
547     }
548   if (addr->rest)
549     add_to_parts (parts, fold_convert (sizetype, addr->rest));
550 }
551
552 /* Force the PARTS to register.  */
553
554 static void
555 gimplify_mem_ref_parts (block_stmt_iterator *bsi, struct mem_address *parts)
556 {
557   if (parts->base)
558     parts->base = force_gimple_operand_bsi (bsi, parts->base,
559                                             true, NULL_TREE);
560   if (parts->index)
561     parts->index = force_gimple_operand_bsi (bsi, parts->index,
562                                              true, NULL_TREE);
563 }
564
565 /* Creates and returns a TARGET_MEM_REF for address ADDR.  If necessary
566    computations are emitted in front of BSI.  TYPE is the mode
567    of created memory reference.  */
568
569 tree
570 create_mem_ref (block_stmt_iterator *bsi, tree type, aff_tree *addr)
571 {
572   tree mem_ref, tmp;
573   tree atype;
574   struct mem_address parts;
575
576   addr_to_parts (addr, &parts);
577   gimplify_mem_ref_parts (bsi, &parts);
578   mem_ref = create_mem_ref_raw (type, &parts);
579   if (mem_ref)
580     return mem_ref;
581
582   /* The expression is too complicated.  Try making it simpler.  */
583
584   if (parts.step && !integer_onep (parts.step))
585     {
586       /* Move the multiplication to index.  */
587       gcc_assert (parts.index);
588       parts.index = force_gimple_operand_bsi (bsi,
589                                 fold_build2 (MULT_EXPR, sizetype,
590                                              parts.index, parts.step),
591                                 true, NULL_TREE);
592       parts.step = NULL_TREE;
593   
594       mem_ref = create_mem_ref_raw (type, &parts);
595       if (mem_ref)
596         return mem_ref;
597     }
598
599   if (parts.symbol)
600     {
601       tmp = build_addr (parts.symbol, current_function_decl);
602       gcc_assert (is_gimple_val (tmp));
603     
604       /* Add the symbol to base, eventually forcing it to register.  */
605       if (parts.base)
606         {
607           gcc_assert (tree_ssa_useless_type_conversion_1
608                                 (sizetype, TREE_TYPE (parts.base)));
609
610           if (parts.index)
611             {
612               atype = TREE_TYPE (tmp);
613               parts.base = force_gimple_operand_bsi (bsi,
614                         fold_build2 (PLUS_EXPR, atype,
615                                      fold_convert (atype, parts.base),
616                                      tmp),
617                         true, NULL_TREE);
618             }
619           else
620             {
621               parts.index = parts.base;
622               parts.base = tmp;
623             }
624         }
625       else
626         parts.base = tmp;
627       parts.symbol = NULL_TREE;
628
629       mem_ref = create_mem_ref_raw (type, &parts);
630       if (mem_ref)
631         return mem_ref;
632     }
633
634   if (parts.index)
635     {
636       /* Add index to base.  */
637       if (parts.base)
638         {
639           atype = TREE_TYPE (parts.base);
640           parts.base = force_gimple_operand_bsi (bsi,
641                         fold_build2 (PLUS_EXPR, atype,
642                                      parts.base,
643                                      fold_convert (atype, parts.index)),
644                         true, NULL_TREE);
645         }
646       else
647         parts.base = parts.index;
648       parts.index = NULL_TREE;
649
650       mem_ref = create_mem_ref_raw (type, &parts);
651       if (mem_ref)
652         return mem_ref;
653     }
654
655   if (parts.offset && !integer_zerop (parts.offset))
656     {
657       /* Try adding offset to base.  */
658       if (parts.base)
659         {
660           atype = TREE_TYPE (parts.base);
661           parts.base = force_gimple_operand_bsi (bsi, 
662                         fold_build2 (POINTER_PLUS_EXPR, atype,
663                                      parts.base,
664                                      fold_convert (sizetype, parts.offset)),
665                         true, NULL_TREE);
666         }
667       else
668         parts.base = parts.offset;
669
670       parts.offset = NULL_TREE;
671
672       mem_ref = create_mem_ref_raw (type, &parts);
673       if (mem_ref)
674         return mem_ref;
675     }
676
677   /* Verify that the address is in the simplest possible shape
678      (only a register).  If we cannot create such a memory reference,
679      something is really wrong.  */
680   gcc_assert (parts.symbol == NULL_TREE);
681   gcc_assert (parts.index == NULL_TREE);
682   gcc_assert (!parts.step || integer_onep (parts.step));
683   gcc_assert (!parts.offset || integer_zerop (parts.offset));
684   gcc_unreachable ();
685 }
686
687 /* Copies components of the address from OP to ADDR.  */
688
689 void
690 get_address_description (tree op, struct mem_address *addr)
691 {
692   addr->symbol = TMR_SYMBOL (op);
693   addr->base = TMR_BASE (op);
694   addr->index = TMR_INDEX (op);
695   addr->step = TMR_STEP (op);
696   addr->offset = TMR_OFFSET (op);
697 }
698
699 /* Copies the additional information attached to target_mem_ref FROM to TO.  */
700
701 void
702 copy_mem_ref_info (tree to, tree from)
703 {
704   /* Copy the annotation, to preserve the aliasing information.  */
705   TMR_TAG (to) = TMR_TAG (from);
706
707   /* And the info about the original reference.  */
708   TMR_ORIGINAL (to) = TMR_ORIGINAL (from);
709 }
710
711 /* Move constants in target_mem_ref REF to offset.  Returns the new target
712    mem ref if anything changes, NULL_TREE otherwise.  */
713
714 tree
715 maybe_fold_tmr (tree ref)
716 {
717   struct mem_address addr;
718   bool changed = false;
719   tree ret, off;
720
721   get_address_description (ref, &addr);
722
723   if (addr.base && TREE_CODE (addr.base) == INTEGER_CST)
724     {
725       if (addr.offset)
726         addr.offset = fold_binary_to_constant (PLUS_EXPR, sizetype,
727                         addr.offset,
728                         fold_convert (sizetype, addr.base));
729       else
730         addr.offset = addr.base;
731
732       addr.base = NULL_TREE;
733       changed = true;
734     }
735
736   if (addr.index && TREE_CODE (addr.index) == INTEGER_CST)
737     {
738       off = addr.index;
739       if (addr.step)
740         {
741           off = fold_binary_to_constant (MULT_EXPR, sizetype,
742                                          off, addr.step);
743           addr.step = NULL_TREE;
744         }
745
746       if (addr.offset)
747         {
748           addr.offset = fold_binary_to_constant (PLUS_EXPR, sizetype,
749                                                  addr.offset, off);
750         }
751       else
752         addr.offset = off;
753
754       addr.index = NULL_TREE;
755       changed = true;
756     }
757
758   if (!changed)
759     return NULL_TREE;
760   
761   ret = create_mem_ref_raw (TREE_TYPE (ref), &addr);
762   if (!ret)
763     return NULL_TREE;
764
765   copy_mem_ref_info (ret, ref);
766   return ret;
767 }
768
769 /* Dump PARTS to FILE.  */
770
771 extern void dump_mem_address (FILE *, struct mem_address *);
772 void
773 dump_mem_address (FILE *file, struct mem_address *parts)
774 {
775   if (parts->symbol)
776     {
777       fprintf (file, "symbol: ");
778       print_generic_expr (file, parts->symbol, TDF_SLIM);
779       fprintf (file, "\n");
780     }
781   if (parts->base)
782     {
783       fprintf (file, "base: ");
784       print_generic_expr (file, parts->base, TDF_SLIM);
785       fprintf (file, "\n");
786     }
787   if (parts->index)
788     {
789       fprintf (file, "index: ");
790       print_generic_expr (file, parts->index, TDF_SLIM);
791       fprintf (file, "\n");
792     }
793   if (parts->step)
794     {
795       fprintf (file, "step: ");
796       print_generic_expr (file, parts->step, TDF_SLIM);
797       fprintf (file, "\n");
798     }
799   if (parts->offset)
800     {
801       fprintf (file, "offset: ");
802       print_generic_expr (file, parts->offset, TDF_SLIM);
803       fprintf (file, "\n");
804     }
805 }
806
807 #include "gt-tree-ssa-address.h"