Commit | Line | Data |
---|---|---|
9bf86ebb PR |
1 | /* Convert language-specific tree expression to rtl instructions, |
2 | for GNU compiler. | |
3 | Copyright (C) 1988, 1992, 1993 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GNU CC. | |
6 | ||
7 | GNU CC is free software; you can redistribute it and/or modify | |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation; either version 2, or (at your option) | |
10 | any later version. | |
11 | ||
12 | GNU CC is distributed in the hope that it will be useful, | |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GNU CC; see the file COPYING. If not, write to | |
19 | the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ | |
20 | ||
21 | ||
22 | #include "config.h" | |
23 | #include "rtl.h" | |
24 | #include "tree.h" | |
25 | #include "flags.h" | |
26 | #include "expr.h" | |
27 | #include "cp-tree.h" | |
28 | ||
29 | #undef NULL | |
30 | #define NULL 0 | |
31 | ||
32 | /* Hook used by expand_expr to expand language-specific tree codes. */ | |
33 | ||
34 | rtx | |
35 | cplus_expand_expr (exp, target, tmode, modifier) | |
36 | tree exp; | |
37 | rtx target; | |
38 | enum machine_mode tmode; | |
39 | enum expand_modifier modifier; | |
40 | { | |
41 | tree type = TREE_TYPE (exp); | |
42 | register enum machine_mode mode = TYPE_MODE (type); | |
43 | register enum tree_code code = TREE_CODE (exp); | |
44 | rtx original_target = target; | |
45 | int ignore = target == const0_rtx; | |
46 | ||
47 | if (ignore) target = 0, original_target = 0; | |
48 | ||
49 | /* No sense saving up arithmetic to be done | |
50 | if it's all in the wrong mode to form part of an address. | |
51 | And force_operand won't know whether to sign-extend or zero-extend. */ | |
52 | ||
53 | if (mode != Pmode && modifier == EXPAND_SUM) | |
54 | modifier = EXPAND_NORMAL; | |
55 | ||
56 | switch (code) | |
57 | { | |
58 | case NEW_EXPR: | |
59 | { | |
60 | /* Something needs to be initialized, but we didn't know | |
61 | where that thing was when building the tree. For example, | |
62 | it could be the return value of a function, or a parameter | |
63 | to a function which lays down in the stack, or a temporary | |
64 | variable which must be passed by reference. | |
65 | ||
66 | Cleanups are handled in a language-specific way: they | |
67 | might be run by the called function (true in GNU C++ | |
68 | for parameters with cleanups), or they might be | |
69 | run by the caller, after the call (true in GNU C++ | |
70 | for other cleanup needs). */ | |
71 | ||
72 | tree func = TREE_OPERAND (exp, 0); | |
73 | tree args = TREE_OPERAND (exp, 1); | |
74 | tree type = TREE_TYPE (exp), slot; | |
75 | tree fn_type = TREE_TYPE (TREE_TYPE (func)); | |
76 | tree return_type = TREE_TYPE (fn_type); | |
77 | rtx call_target, return_target; | |
78 | ||
79 | /* The expression `init' wants to initialize what | |
80 | `target' represents. SLOT holds the slot for TARGET. */ | |
81 | slot = TREE_OPERAND (exp, 2); | |
82 | ||
83 | if (target == 0) | |
84 | { | |
85 | /* Should always be called with a target in BLKmode case. */ | |
86 | my_friendly_assert (mode != BLKmode, 205); | |
87 | my_friendly_assert (DECL_RTL (slot) != 0, 206); | |
88 | ||
89 | target = gen_reg_rtx (mode); | |
90 | } | |
91 | ||
92 | /* The target the initializer will initialize (CALL_TARGET) | |
93 | must now be directed to initialize the target we are | |
94 | supposed to initialize (TARGET). The semantics for | |
95 | choosing what CALL_TARGET is is language-specific, | |
96 | as is building the call which will perform the | |
97 | initialization. It is left here to show the choices that | |
98 | exist for C++. */ | |
99 | ||
100 | if (TREE_CODE (func) == ADDR_EXPR | |
101 | && TREE_CODE (TREE_OPERAND (func, 0)) == FUNCTION_DECL | |
102 | && DECL_CONSTRUCTOR_P (TREE_OPERAND (func, 0))) | |
103 | { | |
104 | type = TYPE_POINTER_TO (type); | |
105 | /* Don't clobber a value that might be part of a default | |
106 | parameter value. */ | |
107 | if (TREE_PERMANENT (args)) | |
108 | args = tree_cons (0, build1 (ADDR_EXPR, type, slot), | |
109 | TREE_CHAIN (args)); | |
110 | else | |
111 | TREE_VALUE (args) = build1 (ADDR_EXPR, type, slot); | |
112 | call_target = 0; | |
113 | } | |
114 | else if (TREE_CODE (return_type) == REFERENCE_TYPE) | |
115 | { | |
116 | type = return_type; | |
117 | call_target = 0; | |
118 | } | |
119 | else | |
120 | { | |
121 | call_target = target; | |
122 | } | |
123 | if (call_target) | |
124 | preserve_temp_slots (call_target); | |
125 | preserve_temp_slots (DECL_RTL (slot)); | |
126 | return_target = expand_expr (build (CALL_EXPR, type, func, args, 0), call_target, mode, 0); | |
127 | free_temp_slots (); | |
128 | if (call_target == 0) | |
129 | call_target = return_target; | |
130 | else if (call_target != return_target) | |
131 | { | |
132 | if (GET_MODE (return_target) == BLKmode) | |
133 | emit_block_move (call_target, return_target, expr_size (exp), | |
134 | TYPE_ALIGN (type) / BITS_PER_UNIT); | |
135 | else | |
136 | emit_move_insn (call_target, return_target); | |
137 | } | |
138 | ||
139 | if (TREE_CODE (return_type) == REFERENCE_TYPE) | |
140 | { | |
141 | tree init; | |
142 | ||
143 | if (GET_CODE (call_target) == REG | |
144 | && REGNO (call_target) < FIRST_PSEUDO_REGISTER) | |
145 | my_friendly_abort (39); | |
146 | ||
147 | type = TREE_TYPE (exp); | |
148 | ||
149 | init = build (RTL_EXPR, return_type, 0, call_target); | |
150 | /* We got back a reference to the type we want. Now initialize | |
151 | target with that. */ | |
152 | expand_aggr_init (slot, init, 0); | |
153 | } | |
154 | ||
155 | if (DECL_RTL (slot) != target) | |
156 | emit_move_insn (DECL_RTL (slot), target); | |
157 | return DECL_RTL (slot); | |
158 | } | |
159 | ||
160 | case OFFSET_REF: | |
161 | { | |
162 | tree base = build_unary_op (ADDR_EXPR, TREE_OPERAND (exp, 0), 0); | |
163 | tree offset = build_unary_op (ADDR_EXPR, TREE_OPERAND (exp, 1), 0); | |
164 | return expand_expr (build (PLUS_EXPR, TREE_TYPE (exp), base, offset), | |
165 | target, tmode, EXPAND_NORMAL); | |
166 | } | |
167 | ||
168 | default: | |
169 | break; | |
170 | } | |
171 | my_friendly_abort (40); | |
172 | /* NOTREACHED */ | |
173 | return NULL; | |
174 | } | |
175 | ||
176 | void | |
177 | init_cplus_expand () | |
178 | { | |
179 | lang_expand_expr = cplus_expand_expr; | |
180 | } | |
181 | ||
182 | /* If DECL had its rtl moved from where callers expect it | |
183 | to be, fix it up. RESULT is the nominal rtl for the RESULT_DECL, | |
184 | which may be a pseudo instead of a hard register. */ | |
185 | ||
186 | void | |
187 | fixup_result_decl (decl, result) | |
188 | tree decl; | |
189 | rtx result; | |
190 | { | |
191 | if (REG_P (result)) | |
192 | { | |
193 | if (REGNO (result) >= FIRST_PSEUDO_REGISTER) | |
194 | { | |
195 | rtx real_decl_result; | |
196 | ||
197 | #ifdef FUNCTION_OUTGOING_VALUE | |
198 | real_decl_result | |
199 | = FUNCTION_OUTGOING_VALUE (TREE_TYPE (decl), current_function_decl); | |
200 | #else | |
201 | real_decl_result | |
202 | = FUNCTION_VALUE (TREE_TYPE (decl), current_function_decl); | |
203 | #endif | |
204 | REG_FUNCTION_VALUE_P (real_decl_result) = 1; | |
205 | result = real_decl_result; | |
206 | } | |
207 | emit_move_insn (result, DECL_RTL (decl)); | |
208 | emit_insn (gen_rtx (USE, VOIDmode, result)); | |
209 | } | |
210 | } | |
211 | ||
212 | /* Return nonzero iff DECL is memory-based. The DECL_RTL of | |
213 | certain const variables might be a CONST_INT, or a REG | |
214 | in some cases. We cannot use `memory_operand' as a test | |
215 | here because on most RISC machines, a variable's address | |
216 | is not, by itself, a legitimate address. */ | |
217 | int | |
218 | decl_in_memory_p (decl) | |
219 | tree decl; | |
220 | { | |
221 | return DECL_RTL (decl) != 0 && GET_CODE (DECL_RTL (decl)) == MEM; | |
222 | } |