OSDN Git Service

* obj-c++.dg/stubify-1.mm: Only run on powerpc.
[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       addr = fold_convert (type, addr_off);
294       if (addr_base)
295         addr = fold_build2 (PLUS_EXPR, type, addr_base, addr);
296     }
297   else if (addr_base)
298     addr = addr_base;
299   else
300     addr = build_int_cst (type, 0);
301
302   return addr;
303 }
304
305 /* Returns true if a memory reference in MODE and with parameters given by
306    ADDR is valid on the current target.  */
307
308 static bool
309 valid_mem_ref_p (enum machine_mode mode, struct mem_address *addr)
310 {
311   rtx address;
312
313   address = addr_for_mem_ref (addr, false);
314   if (!address)
315     return false;
316
317   return memory_address_p (mode, address);
318 }
319
320 /* Checks whether a TARGET_MEM_REF with type TYPE and parameters given by ADDR
321    is valid on the current target and if so, creates and returns the
322    TARGET_MEM_REF.  */
323
324 static tree
325 create_mem_ref_raw (tree type, struct mem_address *addr)
326 {
327   if (!valid_mem_ref_p (TYPE_MODE (type), addr))
328     return NULL_TREE;
329
330   if (addr->step && integer_onep (addr->step))
331     addr->step = NULL_TREE;
332
333   if (addr->offset && integer_zerop (addr->offset))
334     addr->offset = NULL_TREE;
335
336   return build7 (TARGET_MEM_REF, type,
337                  addr->symbol, addr->base, addr->index,
338                  addr->step, addr->offset, NULL, NULL);
339 }
340
341 /* Returns true if OBJ is an object whose address is a link time constant.  */
342
343 static bool
344 fixed_address_object_p (tree obj)
345 {
346   return (TREE_CODE (obj) == VAR_DECL
347           && (TREE_STATIC (obj)
348               || DECL_EXTERNAL (obj)));
349 }
350
351 /* If ADDR contains an address of object that is a link time constant,
352    move it to PARTS->symbol.  */
353
354 static void
355 move_fixed_address_to_symbol (struct mem_address *parts, aff_tree *addr)
356 {
357   unsigned i;
358   tree val = NULL_TREE;
359
360   for (i = 0; i < addr->n; i++)
361     {
362       if (!double_int_one_p (addr->elts[i].coef))
363         continue;
364
365       val = addr->elts[i].val;
366       if (TREE_CODE (val) == ADDR_EXPR
367           && fixed_address_object_p (TREE_OPERAND (val, 0)))
368         break;
369     }
370
371   if (i == addr->n)
372     return;
373
374   parts->symbol = TREE_OPERAND (val, 0);
375   aff_combination_remove_elt (addr, i);
376 }
377
378 /* If ADDR contains an address of a dereferenced pointer, move it to
379    PARTS->base.  */
380
381 static void
382 move_pointer_to_base (struct mem_address *parts, aff_tree *addr)
383 {
384   unsigned i;
385   tree val = NULL_TREE;
386
387   for (i = 0; i < addr->n; i++)
388     {
389       if (!double_int_one_p (addr->elts[i].coef))
390         continue;
391
392       val = addr->elts[i].val;
393       if (POINTER_TYPE_P (TREE_TYPE (val)))
394         break;
395     }
396
397   if (i == addr->n)
398     return;
399
400   parts->base = val;
401   aff_combination_remove_elt (addr, i);
402 }
403
404 /* Adds ELT to PARTS.  */
405
406 static void
407 add_to_parts (struct mem_address *parts, tree elt)
408 {
409   tree type;
410
411   if (!parts->index)
412     {
413       parts->index = elt;
414       return;
415     }
416
417   if (!parts->base)
418     {
419       parts->base = elt;
420       return;
421     }
422
423   /* Add ELT to base.  */
424   type = TREE_TYPE (parts->base);
425   parts->base = fold_build2 (PLUS_EXPR, type,
426                              parts->base,
427                              fold_convert (type, elt));
428 }
429
430 /* Finds the most expensive multiplication in ADDR that can be
431    expressed in an addressing mode and move the corresponding
432    element(s) to PARTS.  */
433
434 static void
435 most_expensive_mult_to_index (struct mem_address *parts, aff_tree *addr)
436 {
437   HOST_WIDE_INT coef;
438   double_int best_mult, amult, amult_neg;
439   unsigned best_mult_cost = 0, acost;
440   tree mult_elt = NULL_TREE, elt;
441   unsigned i, j;
442   enum tree_code op_code;
443
444   best_mult = double_int_zero;
445   for (i = 0; i < addr->n; i++)
446     {
447       if (!double_int_fits_in_shwi_p (addr->elts[i].coef))
448         continue;
449
450       /* FIXME: Should use the correct memory mode rather than Pmode.  */
451
452       coef = double_int_to_shwi (addr->elts[i].coef);
453       if (coef == 1
454           || !multiplier_allowed_in_address_p (coef, Pmode))
455         continue;
456
457       acost = multiply_by_cost (coef, Pmode);
458
459       if (acost > best_mult_cost)
460         {
461           best_mult_cost = acost;
462           best_mult = addr->elts[i].coef;
463         }
464     }
465
466   if (!best_mult_cost)
467     return;
468
469   /* Collect elements multiplied by best_mult.  */
470   for (i = j = 0; i < addr->n; i++)
471     {
472       amult = addr->elts[i].coef;
473       amult_neg = double_int_ext_for_comb (double_int_neg (amult), addr);
474  
475       if (double_int_equal_p (amult, best_mult))
476         op_code = PLUS_EXPR;
477       else if (double_int_equal_p (amult_neg, best_mult))
478         op_code = MINUS_EXPR;
479       else
480         {
481           addr->elts[j] = addr->elts[i];
482           j++;
483           continue;
484         }
485   
486       elt = fold_convert (sizetype, addr->elts[i].val);
487       if (mult_elt)
488         mult_elt = fold_build2 (op_code, sizetype, mult_elt, elt);
489       else if (op_code == PLUS_EXPR)
490         mult_elt = elt;
491       else
492         mult_elt = fold_build1 (NEGATE_EXPR, sizetype, elt);
493     }
494   addr->n = j;
495   
496   parts->index = mult_elt;
497   parts->step = double_int_to_tree (sizetype, best_mult);
498 }
499
500 /* Splits address ADDR into PARTS.
501    
502    TODO -- be more clever about the distribution of the elements of ADDR
503    to PARTS.  Some architectures do not support anything but single
504    register in address, possibly with a small integer offset; while
505    create_mem_ref will simplify the address to an acceptable shape
506    later, it would be more efficient to know that asking for complicated
507    addressing modes is useless.  */
508
509 static void
510 addr_to_parts (aff_tree *addr, struct mem_address *parts)
511 {
512   tree part;
513   unsigned i;
514
515   parts->symbol = NULL_TREE;
516   parts->base = NULL_TREE;
517   parts->index = NULL_TREE;
518   parts->step = NULL_TREE;
519
520   if (!double_int_zero_p (addr->offset))
521     parts->offset = double_int_to_tree (sizetype, addr->offset);
522   else
523     parts->offset = NULL_TREE;
524
525   /* Try to find a symbol.  */
526   move_fixed_address_to_symbol (parts, addr);
527
528   /* First move the most expensive feasible multiplication
529      to index.  */
530   most_expensive_mult_to_index (parts, addr);
531
532   /* Try to find a base of the reference.  Since at the moment
533      there is no reliable way how to distinguish between pointer and its
534      offset, this is just a guess.  */
535   if (!parts->symbol)
536     move_pointer_to_base (parts, addr);
537
538   /* Then try to process the remaining elements.  */
539   for (i = 0; i < addr->n; i++)
540     {
541       part = fold_convert (sizetype, addr->elts[i].val);
542       if (!double_int_one_p (addr->elts[i].coef))
543         part = fold_build2 (MULT_EXPR, sizetype, part,
544                             double_int_to_tree (sizetype, addr->elts[i].coef));
545       add_to_parts (parts, part);
546     }
547   if (addr->rest)
548     add_to_parts (parts, fold_convert (sizetype, addr->rest));
549 }
550
551 /* Force the PARTS to register.  */
552
553 static void
554 gimplify_mem_ref_parts (block_stmt_iterator *bsi, struct mem_address *parts)
555 {
556   if (parts->base)
557     parts->base = force_gimple_operand_bsi (bsi, parts->base,
558                                             true, NULL_TREE);
559   if (parts->index)
560     parts->index = force_gimple_operand_bsi (bsi, parts->index,
561                                              true, NULL_TREE);
562 }
563
564 /* Creates and returns a TARGET_MEM_REF for address ADDR.  If necessary
565    computations are emitted in front of BSI.  TYPE is the mode
566    of created memory reference.  */
567
568 tree
569 create_mem_ref (block_stmt_iterator *bsi, tree type, aff_tree *addr)
570 {
571   tree mem_ref, tmp;
572   tree atype;
573   struct mem_address parts;
574
575   addr_to_parts (addr, &parts);
576   gimplify_mem_ref_parts (bsi, &parts);
577   mem_ref = create_mem_ref_raw (type, &parts);
578   if (mem_ref)
579     return mem_ref;
580
581   /* The expression is too complicated.  Try making it simpler.  */
582
583   if (parts.step && !integer_onep (parts.step))
584     {
585       /* Move the multiplication to index.  */
586       gcc_assert (parts.index);
587       parts.index = force_gimple_operand_bsi (bsi,
588                                 fold_build2 (MULT_EXPR, sizetype,
589                                              parts.index, parts.step),
590                                 true, NULL_TREE);
591       parts.step = NULL_TREE;
592   
593       mem_ref = create_mem_ref_raw (type, &parts);
594       if (mem_ref)
595         return mem_ref;
596     }
597
598   if (parts.symbol)
599     {
600       tmp = build_addr (parts.symbol, current_function_decl);
601       gcc_assert (is_gimple_val (tmp));
602     
603       /* Add the symbol to base, eventually forcing it to register.  */
604       if (parts.base)
605         {
606           gcc_assert (tree_ssa_useless_type_conversion_1
607                                 (sizetype, TREE_TYPE (parts.base)));
608
609           if (parts.index)
610             {
611               atype = TREE_TYPE (tmp);
612               parts.base = force_gimple_operand_bsi (bsi,
613                         fold_build2 (PLUS_EXPR, atype,
614                                      fold_convert (atype, parts.base),
615                                      tmp),
616                         true, NULL_TREE);
617             }
618           else
619             {
620               parts.index = parts.base;
621               parts.base = tmp;
622             }
623         }
624       else
625         parts.base = tmp;
626       parts.symbol = NULL_TREE;
627
628       mem_ref = create_mem_ref_raw (type, &parts);
629       if (mem_ref)
630         return mem_ref;
631     }
632
633   if (parts.index)
634     {
635       /* Add index to base.  */
636       if (parts.base)
637         {
638           atype = TREE_TYPE (parts.base);
639           parts.base = force_gimple_operand_bsi (bsi,
640                         fold_build2 (PLUS_EXPR, atype,
641                                      parts.base,
642                                      fold_convert (atype, parts.index)),
643                         true, NULL_TREE);
644         }
645       else
646         parts.base = parts.index;
647       parts.index = NULL_TREE;
648
649       mem_ref = create_mem_ref_raw (type, &parts);
650       if (mem_ref)
651         return mem_ref;
652     }
653
654   if (parts.offset && !integer_zerop (parts.offset))
655     {
656       /* Try adding offset to base.  */
657       if (parts.base)
658         {
659           atype = TREE_TYPE (parts.base);
660           parts.base = force_gimple_operand_bsi (bsi, 
661                         fold_build2 (PLUS_EXPR, atype,
662                                      parts.base,
663                                      fold_convert (atype, parts.offset)),
664                         true, NULL_TREE);
665         }
666       else
667         parts.base = parts.offset;
668
669       parts.offset = NULL_TREE;
670
671       mem_ref = create_mem_ref_raw (type, &parts);
672       if (mem_ref)
673         return mem_ref;
674     }
675
676   /* Verify that the address is in the simplest possible shape
677      (only a register).  If we cannot create such a memory reference,
678      something is really wrong.  */
679   gcc_assert (parts.symbol == NULL_TREE);
680   gcc_assert (parts.index == NULL_TREE);
681   gcc_assert (!parts.step || integer_onep (parts.step));
682   gcc_assert (!parts.offset || integer_zerop (parts.offset));
683   gcc_unreachable ();
684 }
685
686 /* Copies components of the address from OP to ADDR.  */
687
688 void
689 get_address_description (tree op, struct mem_address *addr)
690 {
691   addr->symbol = TMR_SYMBOL (op);
692   addr->base = TMR_BASE (op);
693   addr->index = TMR_INDEX (op);
694   addr->step = TMR_STEP (op);
695   addr->offset = TMR_OFFSET (op);
696 }
697
698 /* Copies the additional information attached to target_mem_ref FROM to TO.  */
699
700 void
701 copy_mem_ref_info (tree to, tree from)
702 {
703   /* Copy the annotation, to preserve the aliasing information.  */
704   TMR_TAG (to) = TMR_TAG (from);
705
706   /* And the info about the original reference.  */
707   TMR_ORIGINAL (to) = TMR_ORIGINAL (from);
708 }
709
710 /* Move constants in target_mem_ref REF to offset.  Returns the new target
711    mem ref if anything changes, NULL_TREE otherwise.  */
712
713 tree
714 maybe_fold_tmr (tree ref)
715 {
716   struct mem_address addr;
717   bool changed = false;
718   tree ret, off;
719
720   get_address_description (ref, &addr);
721
722   if (addr.base && TREE_CODE (addr.base) == INTEGER_CST)
723     {
724       if (addr.offset)
725         addr.offset = fold_binary_to_constant (PLUS_EXPR, sizetype,
726                         addr.offset,
727                         fold_convert (sizetype, addr.base));
728       else
729         addr.offset = addr.base;
730
731       addr.base = NULL_TREE;
732       changed = true;
733     }
734
735   if (addr.index && TREE_CODE (addr.index) == INTEGER_CST)
736     {
737       off = addr.index;
738       if (addr.step)
739         {
740           off = fold_binary_to_constant (MULT_EXPR, sizetype,
741                                          off, addr.step);
742           addr.step = NULL_TREE;
743         }
744
745       if (addr.offset)
746         {
747           addr.offset = fold_binary_to_constant (PLUS_EXPR, sizetype,
748                                                  addr.offset, off);
749         }
750       else
751         addr.offset = off;
752
753       addr.index = NULL_TREE;
754       changed = true;
755     }
756
757   if (!changed)
758     return NULL_TREE;
759   
760   ret = create_mem_ref_raw (TREE_TYPE (ref), &addr);
761   if (!ret)
762     return NULL_TREE;
763
764   copy_mem_ref_info (ret, ref);
765   return ret;
766 }
767
768 /* Dump PARTS to FILE.  */
769
770 extern void dump_mem_address (FILE *, struct mem_address *);
771 void
772 dump_mem_address (FILE *file, struct mem_address *parts)
773 {
774   if (parts->symbol)
775     {
776       fprintf (file, "symbol: ");
777       print_generic_expr (file, parts->symbol, TDF_SLIM);
778       fprintf (file, "\n");
779     }
780   if (parts->base)
781     {
782       fprintf (file, "base: ");
783       print_generic_expr (file, parts->base, TDF_SLIM);
784       fprintf (file, "\n");
785     }
786   if (parts->index)
787     {
788       fprintf (file, "index: ");
789       print_generic_expr (file, parts->index, TDF_SLIM);
790       fprintf (file, "\n");
791     }
792   if (parts->step)
793     {
794       fprintf (file, "step: ");
795       print_generic_expr (file, parts->step, TDF_SLIM);
796       fprintf (file, "\n");
797     }
798   if (parts->offset)
799     {
800       fprintf (file, "offset: ");
801       print_generic_expr (file, parts->offset, TDF_SLIM);
802       fprintf (file, "\n");
803     }
804 }
805
806 #include "gt-tree-ssa-address.h"