]>
Commit | Line | Data |
---|---|---|
f2423e19 | 1 | /* Subroutines for insn-output.c for System/370. |
701a2601 JL |
2 | Copyright (C) 1989, 1993, 1995, 1997, 1998, 1999, 2000 |
3 | Free Software Foundation, Inc. | |
f2423e19 | 4 | Contributed by Jan Stein (jan@cd.chalmers.se). |
92d6db66 LV |
5 | Modified for OS/390 LanguageEnvironment C by Dave Pitts (dpitts@cozx.com) |
6 | Hacked for Linux-ELF/390 by Linas Vepstas (linas@linas.org) | |
f2423e19 PE |
7 | |
8 | This file is part of GNU CC. | |
9 | ||
10 | GNU CC is free software; you can redistribute it and/or modify | |
11 | it under the terms of the GNU General Public License as published by | |
12 | the Free Software Foundation; either version 2, or (at your option) | |
13 | any later version. | |
14 | ||
15 | GNU CC is distributed in the hope that it will be useful, | |
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | GNU General Public License for more details. | |
19 | ||
20 | You should have received a copy of the GNU General Public License | |
21 | along with GNU CC; see the file COPYING. If not, write to | |
22 | the Free Software Foundation, 59 Temple Place - Suite 330, | |
23 | Boston, MA 02111-1307, USA. */ | |
24 | ||
a0d66c8d | 25 | #include "config.h" |
c5c76735 | 26 | #include "system.h" |
f2423e19 | 27 | #include "rtl.h" |
92d6db66 | 28 | #include "tree.h" |
f2423e19 PE |
29 | #include "regs.h" |
30 | #include "hard-reg-set.h" | |
31 | #include "real.h" | |
32 | #include "insn-config.h" | |
33 | #include "conditions.h" | |
34 | #include "insn-flags.h" | |
35 | #include "output.h" | |
36 | #include "insn-attr.h" | |
104dfaa8 | 37 | #include "function.h" |
f2423e19 PE |
38 | #include "flags.h" |
39 | #include "recog.h" | |
104dfaa8 | 40 | #include "toplev.h" |
8b97c5f8 ZW |
41 | #include "cpplib.h" |
42 | #include "c-pragma.h" | |
43 | #include "c-lex.h" | |
f50ec635 | 44 | #include "tm_p.h" |
f2423e19 | 45 | |
92d6db66 LV |
46 | extern FILE *asm_out_file; |
47 | ||
48 | /* Label node. This structure is used to keep track of labels | |
49 | on the various pages in the current routine. | |
50 | The label_id is the numeric ID of the label, | |
51 | The label_page is the page on which it actually appears, | |
52 | The first_ref_page is the page on which the true first ref appears. | |
53 | The label_addr is an estimate of its location in the current routine, | |
54 | The label_first & last_ref are estimates of where the earliest and | |
55 | latest references to this label occur. */ | |
f2423e19 | 56 | |
f2423e19 PE |
57 | typedef struct label_node |
58 | { | |
59 | struct label_node *label_next; | |
60 | int label_id; | |
61 | int label_page; | |
92d6db66 LV |
62 | int first_ref_page; |
63 | ||
64 | int label_addr; | |
65 | int label_first_ref; | |
66 | int label_last_ref; | |
f2423e19 PE |
67 | } |
68 | label_node_t; | |
69 | ||
92d6db66 LV |
70 | /* Is 1 when a label has been generated and the base register must be reloaded. */ |
71 | int mvs_need_base_reload = 0; | |
f2423e19 PE |
72 | |
73 | /* Current function starting base page. */ | |
74 | int function_base_page; | |
75 | ||
76 | /* Length of the current page code. */ | |
77 | int mvs_page_code; | |
78 | ||
79 | /* Length of the current page literals. */ | |
80 | int mvs_page_lit; | |
81 | ||
82 | /* Current function name. */ | |
83 | char *mvs_function_name = 0; | |
84 | ||
85 | /* Current function name length. */ | |
86 | int mvs_function_name_length = 0; | |
87 | ||
88 | /* Page number for multi-page functions. */ | |
89 | int mvs_page_num = 0; | |
90 | ||
91 | /* Label node list anchor. */ | |
92 | static label_node_t *label_anchor = 0; | |
93 | ||
94 | /* Label node free list anchor. */ | |
95 | static label_node_t *free_anchor = 0; | |
96 | ||
97 | /* Assembler source file descriptor. */ | |
98 | static FILE *assembler_source = 0; | |
99 | ||
f50ec635 KG |
100 | static label_node_t * mvs_get_label PARAMS ((int)); |
101 | static void i370_label_scan PARAMS ((void)); | |
92d6db66 LV |
102 | |
103 | /* ===================================================== */ | |
104 | /* defines and functions specific to the HLASM assembler */ | |
105 | #ifdef TARGET_HLASM | |
106 | ||
8d1349cc DP |
107 | #define MVS_HASH_PRIME 999983 |
108 | #if defined(HOST_EBCDIC) | |
109 | #define MVS_SET_SIZE 256 | |
110 | #else | |
111 | #define MVS_SET_SIZE 128 | |
112 | #endif | |
113 | ||
92d6db66 LV |
114 | #ifndef MAX_MVS_LABEL_SIZE |
115 | #define MAX_MVS_LABEL_SIZE 8 | |
116 | #endif | |
117 | ||
118 | #define MAX_LONG_LABEL_SIZE 255 | |
119 | ||
120 | /* Alias node, this structure is used to keep track of aliases to external | |
121 | variables. The IBM assembler allows an alias to an external name | |
122 | that is longer that 8 characters; but only once per assembly. | |
123 | Also, this structure stores the #pragma map info. */ | |
124 | typedef struct alias_node | |
125 | { | |
126 | struct alias_node *alias_next; | |
127 | int alias_emitted; | |
128 | char alias_name [MAX_MVS_LABEL_SIZE + 1]; | |
129 | char real_name [MAX_LONG_LABEL_SIZE + 1]; | |
130 | } | |
131 | alias_node_t; | |
132 | ||
133 | /* Alias node list anchor. */ | |
134 | static alias_node_t *alias_anchor = 0; | |
135 | ||
136 | /* Alias number */ | |
f50ec635 | 137 | static int alias_number = 0; |
92d6db66 | 138 | |
f2423e19 PE |
139 | /* Define the length of the internal MVS function table. */ |
140 | #define MVS_FUNCTION_TABLE_LENGTH 32 | |
141 | ||
142 | /* C/370 internal function table. These functions use non-standard linkage | |
143 | and must handled in a special manner. */ | |
f50ec635 | 144 | static const char *const mvs_function_table[MVS_FUNCTION_TABLE_LENGTH] = |
f2423e19 | 145 | { |
92d6db66 LV |
146 | #if defined(HOST_EBCDIC) /* Changed for EBCDIC collating sequence */ |
147 | "ceil", "edc_acos", "edc_asin", "edc_atan", "edc_ata2", "edc_cos", | |
148 | "edc_cosh", "edc_erf", "edc_erfc", "edc_exp", "edc_gamm", "edc_lg10", | |
149 | "edc_log", "edc_sin", "edc_sinh", "edc_sqrt", "edc_tan", "edc_tanh", | |
150 | "fabs", "floor", "fmod", "frexp", "hypot", "jn", | |
151 | "j0", "j1", "ldexp", "modf", "pow", "yn", | |
152 | "y0", "y1" | |
153 | #else | |
f2423e19 PE |
154 | "ceil", "edc_acos", "edc_asin", "edc_ata2", "edc_atan", "edc_cos", |
155 | "edc_cosh", "edc_erf", "edc_erfc", "edc_exp", "edc_gamm", "edc_lg10", | |
156 | "edc_log", "edc_sin", "edc_sinh", "edc_sqrt", "edc_tan", "edc_tanh", | |
157 | "fabs", "floor", "fmod", "frexp", "hypot", "j0", | |
158 | "j1", "jn", "ldexp", "modf", "pow", "y0", | |
159 | "y1", "yn" | |
92d6db66 | 160 | #endif |
f2423e19 PE |
161 | }; |
162 | ||
92d6db66 LV |
163 | #endif /* TARGET_HLASM */ |
164 | /* ===================================================== */ | |
165 | ||
f2423e19 | 166 | /* ASCII to EBCDIC conversion table. */ |
f50ec635 | 167 | static const unsigned char ascebc[256] = |
f2423e19 PE |
168 | { |
169 | /*00 NL SH SX EX ET NQ AK BL */ | |
170 | 0x00, 0x01, 0x02, 0x03, 0x37, 0x2D, 0x2E, 0x2F, | |
171 | /*08 BS HT LF VT FF CR SO SI */ | |
172 | 0x16, 0x05, 0x15, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, | |
173 | /*10 DL D1 D2 D3 D4 NK SN EB */ | |
174 | 0x10, 0x11, 0x12, 0x13, 0x3C, 0x3D, 0x32, 0x26, | |
175 | /*18 CN EM SB EC FS GS RS US */ | |
176 | 0x18, 0x19, 0x3F, 0x27, 0x1C, 0x1D, 0x1E, 0x1F, | |
177 | /*20 SP ! " # $ % & ' */ | |
178 | 0x40, 0x5A, 0x7F, 0x7B, 0x5B, 0x6C, 0x50, 0x7D, | |
179 | /*28 ( ) * + , - . / */ | |
180 | 0x4D, 0x5D, 0x5C, 0x4E, 0x6B, 0x60, 0x4B, 0x61, | |
181 | /*30 0 1 2 3 4 5 6 7 */ | |
182 | 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, | |
183 | /*38 8 9 : ; < = > ? */ | |
184 | 0xF8, 0xF9, 0x7A, 0x5E, 0x4C, 0x7E, 0x6E, 0x6F, | |
185 | /*40 @ A B C D E F G */ | |
186 | 0x7C, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, | |
187 | /*48 H I J K L M N O */ | |
188 | 0xC8, 0xC9, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, | |
189 | /*50 P Q R S T U V W */ | |
190 | 0xD7, 0xD8, 0xD9, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, | |
191 | /*58 X Y Z [ \ ] ^ _ */ | |
192 | 0xE7, 0xE8, 0xE9, 0xAD, 0xE0, 0xBD, 0x5F, 0x6D, | |
193 | /*60 ` a b c d e f g */ | |
194 | 0x79, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, | |
195 | /*68 h i j k l m n o */ | |
196 | 0x88, 0x89, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, | |
197 | /*70 p q r s t u v w */ | |
198 | 0x97, 0x98, 0x99, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, | |
199 | /*78 x y z { | } ~ DL */ | |
200 | 0xA7, 0xA8, 0xA9, 0xC0, 0x4F, 0xD0, 0xA1, 0x07, | |
201 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
202 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
203 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
204 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
205 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
206 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
207 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
208 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
209 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
210 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
211 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
212 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
213 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
214 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
215 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, | |
216 | 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0x3F, 0xFF | |
217 | }; | |
f2423e19 PE |
218 | |
219 | /* EBCDIC to ASCII conversion table. */ | |
f50ec635 | 220 | static const unsigned char ebcasc[256] = |
f2423e19 PE |
221 | { |
222 | /*00 NU SH SX EX PF HT LC DL */ | |
223 | 0x00, 0x01, 0x02, 0x03, 0x00, 0x09, 0x00, 0x7F, | |
224 | /*08 SM VT FF CR SO SI */ | |
225 | 0x00, 0x00, 0x00, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, | |
226 | /*10 DE D1 D2 TM RS NL BS IL */ | |
227 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x0A, 0x08, 0x00, | |
228 | /*18 CN EM CC C1 FS GS RS US */ | |
229 | 0x18, 0x19, 0x00, 0x00, 0x1C, 0x1D, 0x1E, 0x1F, | |
230 | /*20 DS SS FS BP LF EB EC */ | |
231 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x17, 0x1B, | |
232 | /*28 SM C2 EQ AK BL */ | |
233 | 0x00, 0x00, 0x00, 0x00, 0x05, 0x06, 0x07, 0x00, | |
234 | /*30 SY PN RS UC ET */ | |
235 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, | |
236 | /*38 C3 D4 NK SU */ | |
237 | 0x00, 0x00, 0x00, 0x00, 0x14, 0x15, 0x00, 0x1A, | |
238 | /*40 SP */ | |
239 | 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
240 | /*48 . < ( + | */ | |
241 | 0x00, 0x00, 0x00, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, | |
242 | /*50 & */ | |
243 | 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
244 | /*58 ! $ * ) ; ^ */ | |
245 | 0x00, 0x00, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0x5E, | |
246 | /*60 - / */ | |
247 | 0x2D, 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
248 | /*68 , % _ > ? */ | |
249 | 0x00, 0x00, 0x00, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, | |
250 | /*70 */ | |
251 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
252 | /*78 ` : # @ ' = " */ | |
253 | 0x00, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, | |
254 | /*80 a b c d e f g */ | |
255 | 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, | |
256 | /*88 h i { */ | |
257 | 0x68, 0x69, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, | |
258 | /*90 j k l m n o p */ | |
259 | 0x00, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, | |
260 | /*98 q r } */ | |
261 | 0x71, 0x72, 0x00, 0x7D, 0x00, 0x00, 0x00, 0x00, | |
262 | /*A0 ~ s t u v w x */ | |
263 | 0x00, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, | |
264 | /*A8 y z [ */ | |
265 | 0x79, 0x7A, 0x00, 0x00, 0x00, 0x5B, 0x00, 0x00, | |
266 | /*B0 */ | |
267 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
268 | /*B8 ] */ | |
269 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x00, | |
270 | /*C0 { A B C D E F G */ | |
271 | 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, | |
272 | /*C8 H I */ | |
273 | 0x48, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
274 | /*D0 } J K L M N O P */ | |
275 | 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, | |
276 | /*D8 Q R */ | |
277 | 0x51, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
278 | /*E0 \ S T U V W X */ | |
279 | 0x5C, 0x00, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, | |
280 | /*E8 Y Z */ | |
281 | 0x59, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, | |
282 | /*F0 0 1 2 3 4 5 6 7 */ | |
283 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, | |
284 | /*F8 8 9 */ | |
285 | 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF | |
286 | }; | |
f2423e19 PE |
287 | |
288 | /* Map characters from one character set to another. | |
289 | C is the character to be translated. */ | |
290 | ||
291 | char | |
292 | mvs_map_char (c) | |
f50ec635 | 293 | int c; |
f2423e19 PE |
294 | { |
295 | #if defined(TARGET_EBCDIC) && !defined(HOST_EBCDIC) | |
92d6db66 | 296 | fprintf (stderr, "mvs_map_char: TE & !HE: c = %02x\n", c); |
f2423e19 PE |
297 | return ascebc[c]; |
298 | #else | |
299 | #if defined(HOST_EBCDIC) && !defined(TARGET_EBCDIC) | |
92d6db66 | 300 | fprintf (stderr, "mvs_map_char: !TE & HE: c = %02x\n", c); |
f2423e19 PE |
301 | return ebcasc[c]; |
302 | #else | |
92d6db66 | 303 | fprintf (stderr, "mvs_map_char: !TE & !HE: c = %02x\n", c); |
f2423e19 PE |
304 | return c; |
305 | #endif | |
306 | #endif | |
307 | } | |
308 | ||
92d6db66 LV |
309 | /* ===================================================== */ |
310 | /* The following three routines are used to determine whther | |
311 | forward branch is on this page, or is a far jump. We use | |
312 | the "length" attr on an insn [(set_atter "length" "4")] | |
313 | to store the largest possible code length that insn | |
314 | could have. This gives us a hint of the address of a | |
315 | branch destination, and from that, we can work out | |
316 | the length of the jump, and whether its on page or not. | |
317 | */ | |
318 | ||
319 | /* Return the destination address of a branch. */ | |
320 | ||
321 | int | |
322 | i370_branch_dest (branch) | |
323 | rtx branch; | |
324 | { | |
325 | rtx dest = SET_SRC (PATTERN (branch)); | |
326 | int dest_uid; | |
327 | int dest_addr; | |
328 | ||
329 | /* first, compute the estimated address of the branch target */ | |
330 | if (GET_CODE (dest) == IF_THEN_ELSE) | |
331 | dest = XEXP (dest, 1); | |
332 | dest = XEXP (dest, 0); | |
333 | dest_uid = INSN_UID (dest); | |
9d98a694 | 334 | dest_addr = INSN_ADDRESSES (dest_uid); |
92d6db66 LV |
335 | |
336 | /* next, record the address of this insn as the true addr of first ref */ | |
337 | { | |
338 | label_node_t *lp; | |
339 | rtx label = JUMP_LABEL (branch); | |
340 | int labelno = CODE_LABEL_NUMBER (label); | |
341 | ||
342 | if (!label || CODE_LABEL != GET_CODE (label)) abort (); | |
343 | ||
344 | lp = mvs_get_label (labelno); | |
345 | if (-1 == lp -> first_ref_page) lp->first_ref_page = mvs_page_num; | |
346 | } | |
347 | return dest_addr; | |
348 | } | |
349 | ||
350 | int | |
351 | i370_branch_length (insn) | |
352 | rtx insn; | |
353 | { | |
354 | int here, there; | |
9d98a694 | 355 | here = INSN_ADDRESSES (INSN_UID (insn)); |
92d6db66 LV |
356 | there = i370_branch_dest (insn); |
357 | return (there - here); | |
358 | } | |
359 | ||
360 | ||
361 | int | |
362 | i370_short_branch (insn) | |
363 | rtx insn; | |
364 | { | |
365 | int base_offset; | |
366 | ||
367 | base_offset = i370_branch_length(insn); | |
368 | if (0 > base_offset) | |
369 | { | |
370 | base_offset += mvs_page_code; | |
371 | } | |
372 | else | |
373 | { | |
374 | /* avoid bumping into lit pool; use 2x to estimate max possible lits */ | |
375 | base_offset *= 2; | |
376 | base_offset += mvs_page_code + mvs_page_lit; | |
377 | } | |
378 | ||
379 | /* make a conservative estimate of room left on page */ | |
380 | if ((4060 >base_offset) && ( 0 < base_offset)) return 1; | |
381 | return 0; | |
382 | } | |
383 | ||
384 | /* The i370_label_scan() routine is supposed to loop over | |
385 | all labels and label references in a compilation unit, | |
386 | and determine whether all label refs appear on the same | |
ff9350e7 | 387 | code page as the label. If they do, then we can avoid |
92d6db66 LV |
388 | a reload of the base register for that label. |
389 | ||
ff9350e7 | 390 | Note that the instruction addresses used here are only |
92d6db66 LV |
391 | approximate, and make the sizes of the jumps appear |
392 | farther apart then they will actually be. This makes | |
ff9350e7 | 393 | this code far more conservative than it needs to be. |
92d6db66 LV |
394 | */ |
395 | ||
396 | #define I370_RECORD_LABEL_REF(label,addr) { \ | |
397 | label_node_t *lp; \ | |
398 | int labelno = CODE_LABEL_NUMBER (label); \ | |
399 | lp = mvs_get_label (labelno); \ | |
400 | if (addr < lp -> label_first_ref) lp->label_first_ref = addr; \ | |
401 | if (addr > lp -> label_last_ref) lp->label_last_ref = addr; \ | |
402 | } | |
403 | ||
404 | void | |
f50ec635 | 405 | i370_label_scan () |
92d6db66 LV |
406 | { |
407 | rtx insn; | |
408 | label_node_t *lp; | |
409 | int tablejump_offset = 0; | |
410 | ||
411 | for (insn = get_insns(); insn; insn = NEXT_INSN(insn)) | |
412 | { | |
9d98a694 | 413 | int here = INSN_ADDRESSES (INSN_UID (insn)); |
92d6db66 LV |
414 | enum rtx_code code = GET_CODE(insn); |
415 | ||
416 | /* ??? adjust for tables embedded in the .text section that | |
417 | * the compiler didn't take into account */ | |
418 | here += tablejump_offset; | |
9d98a694 | 419 | INSN_ADDRESSES (INSN_UID (insn)) = here; |
92d6db66 LV |
420 | |
421 | /* check to see if this insn is a label ... */ | |
422 | if (CODE_LABEL == code) | |
423 | { | |
424 | int labelno = CODE_LABEL_NUMBER (insn); | |
425 | ||
426 | lp = mvs_get_label (labelno); | |
427 | lp -> label_addr = here; | |
428 | #if 0 | |
429 | /* Supposedly, labels are supposed to have circular | |
430 | lists of label-refs that reference them, | |
431 | setup in flow.c, but this does not appear to be the case. */ | |
432 | rtx labelref = LABEL_REFS (insn); | |
433 | rtx ref = labelref; | |
434 | do | |
435 | { | |
436 | rtx linsn = CONTAINING_INSN(ref); | |
437 | ref = LABEL_NEXTREF(ref); | |
438 | } while (ref && (ref != labelref)); | |
439 | #endif | |
440 | } | |
441 | else | |
442 | if (JUMP_INSN == code) | |
443 | { | |
444 | rtx label = JUMP_LABEL (insn); | |
92d6db66 LV |
445 | |
446 | /* If there is no label for this jump, then this | |
447 | had better be a ADDR_VEC or an ADDR_DIFF_VEC | |
448 | and there had better be a vector of labels. */ | |
449 | if (!label) | |
450 | { | |
451 | int j; | |
452 | rtx body = PATTERN (insn); | |
453 | if (ADDR_VEC == GET_CODE(body)) | |
454 | { | |
455 | for (j=0; j < XVECLEN (body, 0); j++) | |
456 | { | |
92d6db66 LV |
457 | rtx lref = XVECEXP (body, 0, j); |
458 | if (LABEL_REF != GET_CODE (lref)) abort (); | |
459 | label = XEXP (lref,0); | |
460 | if (CODE_LABEL != GET_CODE (label)) abort (); | |
461 | tablejump_offset += 4; | |
462 | here += 4; | |
463 | I370_RECORD_LABEL_REF(label,here); | |
464 | } | |
465 | /* finished with the vector go do next insn */ | |
466 | continue; | |
467 | } | |
468 | else | |
469 | if (ADDR_DIFF_VEC == GET_CODE(body)) | |
470 | { | |
471 | /* XXX hack alert. | |
472 | Right now, we leave this as a no-op, but strictly speaking, | |
473 | this is incorrect. It is possible that a table-jump | |
474 | driven off of a relative address could take us off-page, | |
475 | to a place where we need to reload the base reg. So really, | |
476 | we need to examing both labels, and compare thier values | |
477 | to the current basereg value. | |
478 | ||
479 | More generally, this brings up a troubling issue overall: | |
480 | what happens if a tablejump is split across two pages? I do | |
481 | not beleive that this case is handled correctly at all, and | |
482 | can only lead to horrible results if this were to occur. | |
483 | ||
484 | However, the current situation is not any worse than it was | |
ff9350e7 LV |
485 | last week, and so we punt for now. */ |
486 | ||
92d6db66 | 487 | debug_rtx (insn); |
92d6db66 LV |
488 | for (j=0; j < XVECLEN (body, 0); j++) |
489 | { | |
92d6db66 LV |
490 | } |
491 | /* finished with the vector go do next insn */ | |
492 | continue; | |
493 | } | |
494 | else | |
495 | { | |
ff9350e7 LV |
496 | /* XXX hack alert. |
497 | Compiling the execption handling (L_eh) in libgcc2.a will trip | |
498 | up right here, with something that looks like | |
499 | (set (pc) (mem:SI (plus:SI (reg/v:SI 1 r1) (const_int 4)))) | |
500 | {indirect_jump} | |
501 | I'm not sure of what leads up to this, but it looks like | |
502 | the makings of a long jump which will surely get us into trouble | |
503 | because the base & page registers don't get reloaded. For now | |
504 | I'm not sure of what to do ... again we punt ... we are not worse | |
505 | off than yesterday. */ | |
506 | ||
507 | /* print_rtl_single (stdout, insn); */ | |
92d6db66 | 508 | debug_rtx (insn); |
ff9350e7 | 509 | /* abort(); */ |
92d6db66 LV |
510 | continue; |
511 | } | |
512 | } | |
ff9350e7 LV |
513 | else |
514 | { | |
515 | /* At this point, this jump_insn had better be a plain-old | |
516 | ordinary one, grap the label id and go */ | |
517 | if (CODE_LABEL != GET_CODE (label)) abort (); | |
518 | I370_RECORD_LABEL_REF(label,here); | |
519 | } | |
92d6db66 LV |
520 | } |
521 | ||
522 | /* Sometimes, we take addresses of labels and use them | |
523 | as instruction operands ... these show up as REG_NOTES */ | |
524 | else | |
525 | if (INSN == code) | |
526 | { | |
527 | if ('i' == GET_RTX_CLASS (code)) | |
528 | { | |
529 | rtx note; | |
530 | for (note = REG_NOTES (insn); note; note = XEXP(note,1)) | |
531 | { | |
532 | if (REG_LABEL == REG_NOTE_KIND(note)) | |
533 | { | |
534 | rtx label = XEXP (note,0); | |
535 | if (!label || CODE_LABEL != GET_CODE (label)) abort (); | |
536 | ||
537 | I370_RECORD_LABEL_REF(label,here); | |
538 | } | |
539 | } | |
540 | } | |
541 | } | |
542 | } | |
543 | } | |
544 | ||
545 | /* ===================================================== */ | |
546 | ||
f2423e19 PE |
547 | /* Emit reload of base register if indicated. This is to eliminate multiple |
548 | reloads when several labels are generated pointing to the same place | |
92d6db66 LV |
549 | in the code. |
550 | ||
551 | The page table is written at the end of the function. | |
552 | The entries in the page table look like | |
553 | .LPGT0: // PGT0 EQU * | |
554 | .long .LPG0 // DC A(PG0) | |
555 | .long .LPG1 // DC A(PG1) | |
556 | while the prologue generates | |
557 | L r4,=A(.LPGT0) | |
558 | ||
559 | Note that this paging scheme breaks down if a single subroutine | |
560 | has more than about 10MB of code in it ... as long as humans write | |
561 | code, this shouldn't be a problem ... | |
562 | */ | |
f2423e19 | 563 | |
f50ec635 KG |
564 | void |
565 | check_label_emit () | |
f2423e19 | 566 | { |
92d6db66 | 567 | if (mvs_need_base_reload) |
f2423e19 | 568 | { |
92d6db66 LV |
569 | mvs_need_base_reload = 0; |
570 | ||
f2423e19 PE |
571 | mvs_page_code += 4; |
572 | fprintf (assembler_source, "\tL\t%d,%d(,%d)\n", | |
573 | BASE_REGISTER, (mvs_page_num - function_base_page) * 4, | |
574 | PAGE_REGISTER); | |
575 | } | |
576 | } | |
577 | ||
578 | /* Add the label to the current page label list. If a free element is available | |
579 | it will be used for the new label. Otherwise, a label element will be | |
580 | allocated from memory. | |
581 | ID is the label number of the label being added to the list. */ | |
582 | ||
92d6db66 LV |
583 | label_node_t * |
584 | mvs_get_label (id) | |
f2423e19 PE |
585 | int id; |
586 | { | |
587 | label_node_t *lp; | |
588 | ||
92d6db66 LV |
589 | /* first, lets see if we already go one, if so, use that. */ |
590 | for (lp = label_anchor; lp; lp = lp->label_next) | |
591 | { | |
592 | if (lp->label_id == id) return lp; | |
593 | } | |
594 | ||
595 | /* not found, get a new one */ | |
f2423e19 PE |
596 | if (free_anchor) |
597 | { | |
598 | lp = free_anchor; | |
599 | free_anchor = lp->label_next; | |
600 | } | |
601 | else | |
602 | { | |
92d6db66 | 603 | lp = (label_node_t *) xmalloc (sizeof (label_node_t)); |
f2423e19 | 604 | } |
92d6db66 LV |
605 | |
606 | /* initialize for new label */ | |
f2423e19 | 607 | lp->label_id = id; |
92d6db66 | 608 | lp->label_page = -1; |
f2423e19 | 609 | lp->label_next = label_anchor; |
92d6db66 LV |
610 | lp->label_first_ref = 2000123123; |
611 | lp->label_last_ref = -1; | |
612 | lp->label_addr = -1; | |
613 | lp->first_ref_page = -1; | |
f2423e19 | 614 | label_anchor = lp; |
92d6db66 LV |
615 | |
616 | return lp; | |
f2423e19 PE |
617 | } |
618 | ||
92d6db66 LV |
619 | void |
620 | mvs_add_label (id) | |
621 | int id; | |
622 | { | |
623 | label_node_t *lp; | |
624 | int fwd_distance; | |
625 | ||
626 | lp = mvs_get_label (id); | |
627 | lp->label_page = mvs_page_num; | |
628 | ||
629 | /* OK, we just saw the label. Determine if this label | |
630 | * needs a reload of the base register */ | |
631 | if ((-1 != lp->first_ref_page) && | |
632 | (lp->first_ref_page != mvs_page_num)) | |
633 | { | |
634 | /* Yep; the first label_ref was on a different page. */ | |
635 | mvs_need_base_reload ++; | |
636 | return; | |
637 | } | |
638 | ||
639 | /* Hmm. Try to see if the estimated address of the last | |
640 | label_ref is on the current page. If it is, then we | |
641 | don't need a base reg reload. Note that this estimate | |
642 | is very conservatively handled; we'll tend to have | |
643 | a good bit more reloads than actually needed. Someday, | |
644 | we should tighten the estimates (which are driven by | |
645 | the (set_att "length") insn attibute. | |
646 | ||
647 | Currently, we estimate that number of page literals | |
648 | same as number of insns, which is a vast overestimate, | |
649 | esp that the estimate of each insn size is its max size. */ | |
650 | ||
651 | /* if latest ref comes before label, we are clear */ | |
652 | if (lp->label_last_ref < lp->label_addr) return; | |
653 | ||
654 | fwd_distance = lp->label_last_ref - lp->label_addr; | |
655 | ||
e13f6154 | 656 | if (mvs_page_code + 2 * fwd_distance + mvs_page_lit < 4060) return; |
92d6db66 LV |
657 | |
658 | mvs_need_base_reload ++; | |
659 | } | |
660 | ||
661 | /* Check to see if the label is in the list and in the current | |
662 | page. If not found, we have to make worst case assumption | |
663 | that label will be on a different page, and thus will have to | |
664 | generate a load and branch on register. This is rather | |
665 | ugly for forward-jumps, but what can we do? For backward | |
666 | jumps on the same page we can branch directly to address. | |
f2423e19 PE |
667 | ID is the label number of the label being checked. */ |
668 | ||
669 | int | |
670 | mvs_check_label (id) | |
671 | int id; | |
672 | { | |
673 | label_node_t *lp; | |
674 | ||
675 | for (lp = label_anchor; lp; lp = lp->label_next) | |
676 | { | |
92d6db66 LV |
677 | if (lp->label_id == id) |
678 | { | |
679 | if (lp->label_page == mvs_page_num) | |
680 | { | |
681 | return 1; | |
682 | } | |
683 | else | |
684 | { | |
685 | return 0; | |
686 | } | |
687 | } | |
f2423e19 PE |
688 | } |
689 | return 0; | |
690 | } | |
691 | ||
92d6db66 LV |
692 | /* Get the page on which the label sits. This will be used to |
693 | determine is a register reload is really needed. */ | |
694 | ||
695 | int | |
696 | mvs_get_label_page(int id) | |
697 | { | |
698 | label_node_t *lp; | |
699 | ||
700 | for (lp = label_anchor; lp; lp = lp->label_next) | |
701 | { | |
702 | if (lp->label_id == id) | |
703 | return lp->label_page; | |
704 | } | |
705 | return -1; | |
706 | } | |
707 | ||
f2423e19 PE |
708 | /* The label list for the current page freed by linking the list onto the free |
709 | label element chain. */ | |
710 | ||
92d6db66 | 711 | void |
f50ec635 | 712 | mvs_free_label_list () |
f2423e19 | 713 | { |
92d6db66 | 714 | |
f2423e19 PE |
715 | if (label_anchor) |
716 | { | |
92d6db66 LV |
717 | label_node_t *last_lp = label_anchor; |
718 | while (last_lp->label_next) last_lp = last_lp->label_next; | |
719 | last_lp->label_next = free_anchor; | |
f2423e19 PE |
720 | free_anchor = label_anchor; |
721 | } | |
722 | label_anchor = 0; | |
723 | } | |
724 | ||
92d6db66 | 725 | /* ====================================================================== */ |
f2423e19 PE |
726 | /* If the page size limit is reached a new code page is started, and the base |
727 | register is set to it. This page break point is counted conservatively, | |
728 | most literals that have the same value are collapsed by the assembler. | |
729 | True is returned when a new page is started. | |
730 | FILE is the assembler output file descriptor. | |
731 | CODE is the length, in bytes, of the instruction to be emitted. | |
732 | LIT is the length of the literal to be emitted. */ | |
733 | ||
92d6db66 | 734 | #ifdef TARGET_HLASM |
f2423e19 PE |
735 | int |
736 | mvs_check_page (file, code, lit) | |
737 | FILE *file; | |
738 | int code, lit; | |
739 | { | |
740 | if (file) | |
741 | assembler_source = file; | |
742 | ||
743 | if (mvs_page_code + code + mvs_page_lit + lit > MAX_MVS_PAGE_LENGTH) | |
744 | { | |
745 | fprintf (assembler_source, "\tB\tPGE%d\n", mvs_page_num); | |
746 | fprintf (assembler_source, "\tDS\t0F\n"); | |
747 | fprintf (assembler_source, "\tLTORG\n"); | |
748 | fprintf (assembler_source, "\tDS\t0F\n"); | |
749 | fprintf (assembler_source, "PGE%d\tEQU\t*\n", mvs_page_num); | |
750 | fprintf (assembler_source, "\tDROP\t%d\n", BASE_REGISTER); | |
751 | mvs_page_num++; | |
92d6db66 LV |
752 | /* Safe to use BASR not BALR, since we are |
753 | * not switching addressing mode here ... */ | |
754 | fprintf (assembler_source, "\tBASR\t%d,0\n", BASE_REGISTER); | |
f2423e19 PE |
755 | fprintf (assembler_source, "PG%d\tEQU\t*\n", mvs_page_num); |
756 | fprintf (assembler_source, "\tUSING\t*,%d\n", BASE_REGISTER); | |
f2423e19 PE |
757 | mvs_page_code = code; |
758 | mvs_page_lit = lit; | |
759 | return 1; | |
760 | } | |
761 | mvs_page_code += code; | |
762 | mvs_page_lit += lit; | |
763 | return 0; | |
764 | } | |
92d6db66 LV |
765 | #endif /* TARGET_HLASM */ |
766 | ||
767 | ||
768 | #ifdef TARGET_ELF_ABI | |
769 | int | |
770 | mvs_check_page (file, code, lit) | |
771 | FILE *file; | |
772 | int code, lit; | |
773 | { | |
774 | if (file) | |
775 | assembler_source = file; | |
776 | ||
777 | if (mvs_page_code + code + mvs_page_lit + lit > MAX_MVS_PAGE_LENGTH) | |
778 | { | |
779 | /* hop past the literal pool */ | |
780 | fprintf (assembler_source, "\tB\t.LPGE%d\n", mvs_page_num); | |
781 | ||
782 | /* dump the literal pool. The .baligns are optional, since | |
783 | * ltorg will align to the size of the largest literal | |
784 | * (which is possibly 8 bytes) */ | |
785 | fprintf (assembler_source, "\t.balign\t4\n"); | |
786 | fprintf (assembler_source, "\t.LTORG\n"); | |
787 | fprintf (assembler_source, "\t.balign\t4\n"); | |
788 | ||
789 | /* we continue execution here ... */ | |
790 | fprintf (assembler_source, ".LPGE%d:\n", mvs_page_num); | |
791 | fprintf (assembler_source, "\t.DROP\t%d\n", BASE_REGISTER); | |
792 | mvs_page_num++; | |
793 | ||
794 | /* BASR puts the contents of the PSW into r3 | |
795 | * that is, r3 will be loaded with the address of "." */ | |
796 | fprintf (assembler_source, "\tBASR\tr%d,0\n", BASE_REGISTER); | |
797 | fprintf (assembler_source, ".LPG%d:\n", mvs_page_num); | |
798 | fprintf (assembler_source, "\t.USING\t.,r%d\n", BASE_REGISTER); | |
799 | mvs_page_code = code; | |
800 | mvs_page_lit = lit; | |
801 | return 1; | |
802 | } | |
803 | mvs_page_code += code; | |
804 | mvs_page_lit += lit; | |
805 | return 0; | |
806 | } | |
807 | #endif /* TARGET_ELF_ABI */ | |
808 | ||
809 | /* ===================================================== */ | |
810 | /* defines and functions specific to the HLASM assembler */ | |
811 | #ifdef TARGET_HLASM | |
f2423e19 PE |
812 | |
813 | /* Check for C/370 runtime function, they don't use standard calling | |
814 | conventions. True is returned if the function is in the table. | |
815 | NAME is the name of the current function. */ | |
816 | ||
817 | int | |
818 | mvs_function_check (name) | |
f50ec635 | 819 | const char *name; |
f2423e19 PE |
820 | { |
821 | int lower, middle, upper; | |
822 | int i; | |
823 | ||
824 | lower = 0; | |
825 | upper = MVS_FUNCTION_TABLE_LENGTH - 1; | |
826 | while (lower <= upper) | |
827 | { | |
828 | middle = (lower + upper) / 2; | |
829 | i = strcmp (name, mvs_function_table[middle]); | |
830 | if (i == 0) | |
831 | return 1; | |
832 | if (i < 0) | |
833 | upper = middle - 1; | |
834 | else | |
835 | lower = middle + 1; | |
836 | } | |
837 | return 0; | |
838 | } | |
839 | ||
8d1349cc DP |
840 | /* Generate a hash for a given key. */ |
841 | ||
842 | static int | |
843 | mvs_hash_alias (key) | |
844 | char *key; | |
845 | { | |
846 | int h; | |
847 | int i; | |
848 | int l = strlen (key); | |
849 | ||
850 | h = key[0]; | |
851 | for (i = 1; i < l; i++) | |
852 | h = ((h * MVS_SET_SIZE) + key[i]) % MVS_HASH_PRIME; | |
853 | return (h); | |
854 | } | |
855 | ||
f2423e19 | 856 | |
92d6db66 LV |
857 | /* Add the alias to the current alias list. */ |
858 | ||
f50ec635 | 859 | void |
92d6db66 | 860 | mvs_add_alias (realname, aliasname, emitted) |
f50ec635 KG |
861 | const char *realname; |
862 | const char *aliasname; | |
8d1349cc | 863 | int emitted; |
92d6db66 LV |
864 | { |
865 | alias_node_t *ap; | |
866 | ||
867 | ap = (alias_node_t *) xmalloc (sizeof (alias_node_t)); | |
8b97c5f8 ZW |
868 | if (strlen (realname) > MAX_LONG_LABEL_SIZE) |
869 | { | |
870 | warning ("real name is too long - alias ignored"); | |
871 | return; | |
872 | } | |
873 | if (strlen (aliasname) > MAX_MVS_LABEL_SIZE) | |
874 | { | |
875 | warning ("alias name is too long - alias ignored"); | |
876 | return; | |
877 | } | |
878 | ||
92d6db66 LV |
879 | strcpy (ap->real_name, realname); |
880 | strcpy (ap->alias_name, aliasname); | |
881 | ap->alias_emitted = emitted; | |
882 | ap->alias_next = alias_anchor; | |
883 | alias_anchor = ap; | |
884 | } | |
885 | ||
8d1349cc DP |
886 | /* Check to see if the name needs aliasing. ie. the name is either: |
887 | 1. Longer than 8 characters | |
888 | 2. Contains an underscore | |
889 | 3. Is mixed case */ | |
92d6db66 LV |
890 | |
891 | int | |
892 | mvs_need_alias (realname) | |
f50ec635 | 893 | const char *realname; |
92d6db66 | 894 | { |
8d1349cc DP |
895 | int i, j = strlen (realname); |
896 | ||
92d6db66 LV |
897 | if (mvs_function_check (realname)) |
898 | return 0; | |
8d1349cc DP |
899 | #if 0 |
900 | if (!strcmp (realname, "gccmain")) | |
901 | return 0; | |
902 | if (!strcmp (realname, "main")) | |
903 | return 0; | |
904 | #endif | |
905 | if (j > MAX_MVS_LABEL_SIZE) | |
92d6db66 LV |
906 | return 1; |
907 | if (strchr (realname, '_') != 0) | |
908 | return 1; | |
8d1349cc DP |
909 | if (isupper (realname[0])) |
910 | { | |
911 | for (i = 1; i < j; i++) | |
912 | { | |
913 | if (islower (realname[i])) | |
914 | return 1; | |
915 | } | |
916 | } | |
917 | else | |
918 | { | |
919 | for (i = 1; i < j; i++) | |
920 | { | |
921 | if (isupper (realname[i])) | |
922 | return 1; | |
923 | } | |
924 | } | |
925 | ||
92d6db66 LV |
926 | return 0; |
927 | } | |
928 | ||
929 | /* Get the alias from the list. | |
930 | If 1 is returned then it's in the alias list, 0 if it was not */ | |
931 | ||
932 | int | |
933 | mvs_get_alias (realname, aliasname) | |
f50ec635 | 934 | const char *realname; |
92d6db66 LV |
935 | char *aliasname; |
936 | { | |
937 | #ifdef LONGEXTERNAL | |
938 | alias_node_t *ap; | |
939 | ||
940 | for (ap = alias_anchor; ap; ap = ap->alias_next) | |
941 | { | |
942 | if (!strcmp (ap->real_name, realname)) | |
943 | { | |
944 | strcpy (aliasname, ap->alias_name); | |
945 | return 1; | |
946 | } | |
947 | } | |
948 | if (mvs_need_alias (realname)) | |
949 | { | |
8d1349cc DP |
950 | char c1, c2; |
951 | ||
952 | c1 = realname[0]; | |
953 | c2 = realname[1]; | |
954 | if (islower (c1)) c1 = toupper (c1); | |
955 | else if (c1 == '_') c1 = 'A'; | |
956 | if (islower (c2)) c2 = toupper (c2); | |
957 | else if (c2 == '_' || c2 == '\0') c2 = '#'; | |
958 | ||
959 | sprintf (aliasname, "%c%c%06d", c1, c2, mvs_hash_alias (realname)); | |
92d6db66 LV |
960 | mvs_add_alias (realname, aliasname, 0); |
961 | return 1; | |
962 | } | |
963 | #else | |
964 | if (strlen (realname) > MAX_MVS_LABEL_SIZE) | |
965 | { | |
966 | strncpy (aliasname, realname, MAX_MVS_LABEL_SIZE); | |
967 | aliasname[MAX_MVS_LABEL_SIZE] = '\0'; | |
968 | return 1; | |
969 | } | |
970 | #endif | |
971 | return 0; | |
972 | } | |
973 | ||
974 | /* Check to see if the alias is in the list. | |
975 | If 1 is returned then it's in the alias list, 2 it was emitted */ | |
976 | ||
977 | int | |
978 | mvs_check_alias (realname, aliasname) | |
f50ec635 | 979 | const char *realname; |
92d6db66 LV |
980 | char *aliasname; |
981 | { | |
982 | #ifdef LONGEXTERNAL | |
983 | alias_node_t *ap; | |
984 | ||
985 | for (ap = alias_anchor; ap; ap = ap->alias_next) | |
986 | { | |
987 | if (!strcmp (ap->real_name, realname)) | |
988 | { | |
989 | int rc = (ap->alias_emitted == 1) ? 1 : 2; | |
990 | strcpy (aliasname, ap->alias_name); | |
991 | ap->alias_emitted = 1; | |
992 | return rc; | |
993 | } | |
994 | } | |
995 | if (mvs_need_alias (realname)) | |
996 | { | |
8d1349cc DP |
997 | char c1, c2; |
998 | ||
999 | c1 = realname[0]; | |
1000 | c2 = realname[1]; | |
1001 | if (islower (c1)) c1 = toupper (c1); | |
1002 | else if (c1 == '_') c1 = 'A'; | |
1003 | if (islower (c2)) c2 = toupper (c2); | |
1004 | else if (c2 == '_' || c2 == '\0') c2 = '#'; | |
1005 | ||
1006 | sprintf (aliasname, "%c%c%06d", c1, c2, mvs_hash_alias (realname)); | |
92d6db66 LV |
1007 | mvs_add_alias (realname, aliasname, 0); |
1008 | alias_anchor->alias_emitted = 1; | |
1009 | return 2; | |
1010 | } | |
1011 | #else | |
1012 | if (strlen (realname) > MAX_MVS_LABEL_SIZE) | |
1013 | { | |
1014 | strncpy (aliasname, realname, MAX_MVS_LABEL_SIZE); | |
1015 | aliasname[MAX_MVS_LABEL_SIZE] = '\0'; | |
1016 | return 1; | |
1017 | } | |
1018 | #endif | |
1019 | return 0; | |
1020 | } | |
1021 | ||
8b97c5f8 ZW |
1022 | /* #pragma map (name, alias) - |
1023 | In this implementation both name and alias are required to be | |
1024 | identifiers. The older code seemed to be more permissive. Can | |
1025 | anyone clarify? */ | |
92d6db66 | 1026 | |
8b97c5f8 ZW |
1027 | void |
1028 | i370_pr_map (pfile) | |
1029 | cpp_reader *pfile ATTRIBUTE_UNUSED; | |
92d6db66 | 1030 | { |
8b97c5f8 | 1031 | tree name, alias, x; |
92d6db66 | 1032 | |
8b97c5f8 ZW |
1033 | if (c_lex (&x) == CPP_OPEN_PAREN |
1034 | && c_lex (&name) == CPP_NAME | |
1035 | && c_lex (&x) == CPP_COMMA | |
1036 | && c_lex (&alias) == CPP_NAME | |
1037 | && c_lex (&x) == CPP_CLOSE_PAREN) | |
92d6db66 | 1038 | { |
8b97c5f8 ZW |
1039 | if (c_lex (&x) != CPP_EOF) |
1040 | warning ("junk at end of #pragma map"); | |
92d6db66 | 1041 | |
8b97c5f8 ZW |
1042 | mvs_add_alias (IDENTIFIER_POINTER (name), IDENTIFIER_POINTER (alias), 1); |
1043 | return; | |
92d6db66 LV |
1044 | } |
1045 | ||
8b97c5f8 | 1046 | warning ("malformed #pragma map, ignored"); |
92d6db66 LV |
1047 | } |
1048 | ||
1049 | /* defines and functions specific to the HLASM assembler */ | |
1050 | #endif /* TARGET_HLASM */ | |
1051 | /* ===================================================== */ | |
1052 | /* ===================================================== */ | |
1053 | /* defines and functions specific to the gas assembler */ | |
1054 | #ifdef TARGET_ELF_ABI | |
1055 | ||
1056 | /* Check for C/370 runtime function, they don't use standard calling | |
1057 | conventions. True is returned if the function is in the table. | |
1058 | NAME is the name of the current function. */ | |
1059 | /* no special calling conventions (yet ??) */ | |
1060 | ||
1061 | int | |
1062 | mvs_function_check (name) | |
f50ec635 | 1063 | const char *name ATTRIBUTE_UNUSED; |
92d6db66 LV |
1064 | { |
1065 | return 0; | |
1066 | } | |
1067 | ||
1068 | #endif /* TARGET_ELF_ABI */ | |
1069 | /* ===================================================== */ | |
1070 | ||
1071 | ||
f2423e19 PE |
1072 | /* Return 1 if OP is a valid S operand for an RS, SI or SS type instruction. |
1073 | OP is the current operation. | |
1074 | MODE is the current operation mode. */ | |
1075 | ||
1076 | int | |
1077 | s_operand (op, mode) | |
1078 | register rtx op; | |
1079 | enum machine_mode mode; | |
1080 | { | |
1081 | extern int volatile_ok; | |
1082 | register enum rtx_code code = GET_CODE (op); | |
1083 | ||
1084 | if (CONSTANT_ADDRESS_P (op)) | |
1085 | return 1; | |
1086 | if (mode == VOIDmode || GET_MODE (op) != mode) | |
1087 | return 0; | |
1088 | if (code == MEM) | |
1089 | { | |
1090 | register rtx x = XEXP (op, 0); | |
1091 | ||
1092 | if (!volatile_ok && op->volatil) | |
1093 | return 0; | |
1094 | if (REG_P (x) && REG_OK_FOR_BASE_P (x)) | |
1095 | return 1; | |
1096 | if (GET_CODE (x) == PLUS | |
1097 | && REG_P (XEXP (x, 0)) && REG_OK_FOR_BASE_P (XEXP (x, 0)) | |
1098 | && GET_CODE (XEXP (x, 1)) == CONST_INT | |
1099 | && (unsigned) INTVAL (XEXP (x, 1)) < 4096) | |
1100 | return 1; | |
1101 | } | |
1102 | return 0; | |
1103 | } | |
1104 | ||
1105 | ||
1106 | /* Return 1 if OP is a valid R or S operand for an RS, SI or SS type | |
1107 | instruction. | |
1108 | OP is the current operation. | |
1109 | MODE is the current operation mode. */ | |
1110 | ||
1111 | int | |
1112 | r_or_s_operand (op, mode) | |
1113 | register rtx op; | |
1114 | enum machine_mode mode; | |
1115 | { | |
1116 | extern int volatile_ok; | |
1117 | register enum rtx_code code = GET_CODE (op); | |
1118 | ||
1119 | if (CONSTANT_ADDRESS_P (op)) | |
1120 | return 1; | |
1121 | if (mode == VOIDmode || GET_MODE (op) != mode) | |
1122 | return 0; | |
1123 | if (code == REG) | |
1124 | return 1; | |
1125 | else if (code == MEM) | |
1126 | { | |
1127 | register rtx x = XEXP (op, 0); | |
1128 | ||
1129 | if (!volatile_ok && op->volatil) | |
1130 | return 0; | |
1131 | if (REG_P (x) && REG_OK_FOR_BASE_P (x)) | |
1132 | return 1; | |
1133 | if (GET_CODE (x) == PLUS | |
1134 | && REG_P (XEXP (x, 0)) && REG_OK_FOR_BASE_P (XEXP (x, 0)) | |
1135 | && GET_CODE (XEXP (x, 1)) == CONST_INT | |
1136 | && (unsigned) INTVAL (XEXP (x, 1)) < 4096) | |
1137 | return 1; | |
1138 | } | |
1139 | return 0; | |
1140 | } | |
1141 | ||
1142 | ||
92d6db66 LV |
1143 | /* Some remarks about unsigned_jump_follows_p(): |
1144 | gcc is built around the assumption that branches are signed | |
1145 | or unsigned, whereas the 370 doesn't care; its the compares that | |
1146 | are signed or unsigned. Thus, we need to somehow know if we | |
1147 | need to do a signed or an unsigned compare, and we do this by | |
1148 | looking ahead in the instruction sequence until we find a jump. | |
1149 | We then note whether this jump is signed or unsigned, and do the | |
1150 | compare appropriately. Note that we have to scan ahead indefinitley, | |
1151 | as the gcc optimizer may insert any number of instructions between | |
1152 | the compare and the jump. | |
1153 | ||
1154 | Note that using conditional branch expanders seems to be be a more | |
1155 | elegant/correct way of doing this. See, for instance, the Alpha | |
1156 | cmpdi and bgt patterns. Note also that for the i370, various | |
1157 | arithmetic insn's set the condition code as well. | |
1158 | ||
1159 | The unsigned_jump_follows_p() routine returns a 1 if the next jump | |
1160 | is unsigned. INSN is the current instruction. */ | |
f2423e19 | 1161 | |
f50ec635 | 1162 | int |
f2423e19 PE |
1163 | unsigned_jump_follows_p (insn) |
1164 | register rtx insn; | |
1165 | { | |
92d6db66 LV |
1166 | rtx orig_insn = insn; |
1167 | while (1) | |
1168 | { | |
1169 | register rtx tmp_insn; | |
1170 | enum rtx_code coda; | |
1171 | ||
1172 | insn = NEXT_INSN (insn); | |
1173 | if (!insn) fatal_insn ("internal error--no jump follows compare:", orig_insn); | |
1174 | ||
1175 | if (GET_CODE (insn) != JUMP_INSN) continue; | |
1176 | ||
1177 | tmp_insn = XEXP (insn, 3); | |
1178 | if (GET_CODE (tmp_insn) != SET) continue; | |
1179 | ||
1180 | if (GET_CODE (XEXP (tmp_insn, 0)) != PC) continue; | |
1181 | ||
1182 | tmp_insn = XEXP (tmp_insn, 1); | |
1183 | if (GET_CODE (tmp_insn) != IF_THEN_ELSE) continue; | |
1184 | ||
1185 | /* if we got to here, this instruction is a jump. Is it signed? */ | |
1186 | tmp_insn = XEXP (tmp_insn, 0); | |
1187 | coda = GET_CODE (tmp_insn); | |
1188 | ||
1189 | return coda != GE && coda != GT && coda != LE && coda != LT; | |
1190 | } | |
1191 | } | |
f2423e19 | 1192 | |
f2423e19 | 1193 | |
92d6db66 | 1194 | #ifdef TARGET_HLASM |
a0d66c8d JL |
1195 | |
1196 | void | |
1197 | i370_function_prolog (f, l) | |
1198 | FILE *f; | |
1199 | int l; | |
1200 | { | |
1201 | #if MACROPROLOGUE == 1 | |
92d6db66 | 1202 | fprintf (f, "* Function %s prologue\n", mvs_function_name); |
a0d66c8d JL |
1203 | fprintf (f, "\tEDCPRLG USRDSAL=%d,BASEREG=%d\n", |
1204 | STACK_POINTER_OFFSET + l - 120 + | |
1205 | current_function_outgoing_args_size, BASE_REGISTER); | |
a0d66c8d JL |
1206 | #else /* MACROPROLOGUE != 1 */ |
1207 | static int function_label_index = 1; | |
1208 | static int function_first = 0; | |
1209 | static int function_year, function_month, function_day; | |
1210 | static int function_hour, function_minute, function_second; | |
92d6db66 LV |
1211 | #if defined(LE370) |
1212 | if (!function_first) | |
1213 | { | |
1214 | struct tm *function_time; | |
1215 | time_t lcltime; | |
1216 | time (&lcltime); | |
1217 | function_time = localtime (&lcltime); | |
1218 | function_year = function_time->tm_year + 1900; | |
1219 | function_month = function_time->tm_mon + 1; | |
1220 | function_day = function_time->tm_mday; | |
1221 | function_hour = function_time->tm_hour; | |
1222 | function_minute = function_time->tm_min; | |
1223 | function_second = function_time->tm_sec; | |
1224 | } | |
1225 | fprintf (f, "* Function %s prologue\n", mvs_function_name); | |
1226 | fprintf (f, "FDSE%03d\tDSECT\n", function_label_index); | |
1227 | fprintf (f, "\tDS\tD\n"); | |
1228 | fprintf (f, "\tDS\tCL(%d)\n", STACK_POINTER_OFFSET + l | |
1229 | + current_function_outgoing_args_size); | |
1230 | fprintf (f, "\tORG\tFDSE%03d\n", function_label_index); | |
1231 | fprintf (f, "\tDS\tCL(120+8)\n"); | |
1232 | fprintf (f, "\tORG\n"); | |
1233 | fprintf (f, "\tDS\t0D\n"); | |
1234 | fprintf (f, "FDSL%03d\tEQU\t*-FDSE%03d-8\n", function_label_index, | |
1235 | function_label_index); | |
1236 | fprintf (f, "\tDS\t0H\n"); | |
1237 | assemble_name (f, mvs_function_name); | |
1238 | fprintf (f, "\tCSECT\n"); | |
1239 | fprintf (f, "\tUSING\t*,15\n"); | |
1240 | fprintf (f, "\tB\tFENT%03d\n", function_label_index); | |
1241 | fprintf (f, "\tDC\tAL1(FNAM%03d+4-*)\n", function_label_index); | |
1242 | fprintf (f, "\tDC\tX'CE',X'A0',AL1(16)\n"); | |
1243 | fprintf (f, "\tDC\tAL4(FPPA%03d)\n", function_label_index); | |
1244 | fprintf (f, "\tDC\tAL4(0)\n"); | |
1245 | fprintf (f, "\tDC\tAL4(FDSL%03d)\n", function_label_index); | |
1246 | fprintf (f, "FNAM%03d\tEQU\t*\n", function_label_index); | |
1247 | fprintf (f, "\tDC\tAL2(%d),C'%s'\n", strlen (mvs_function_name), | |
1248 | mvs_function_name); | |
1249 | fprintf (f, "FPPA%03d\tDS\t0F\n", function_label_index); | |
1250 | fprintf (f, "\tDC\tX'03',X'00',X'33',X'00'\n"); | |
1251 | fprintf (f, "\tDC\tV(CEESTART)\n"); | |
1252 | fprintf (f, "\tDC\tAL4(0)\n"); | |
1253 | fprintf (f, "\tDC\tAL4(FTIM%03d)\n", function_label_index); | |
1254 | fprintf (f, "FTIM%03d\tDS\t0F\n", function_label_index); | |
1255 | fprintf (f, "\tDC\tCL4'%d',CL4'%02d%02d',CL6'%02d%02d00'\n", | |
1256 | function_year, function_month, function_day, | |
f50ec635 | 1257 | function_hour, function_minute); |
92d6db66 LV |
1258 | fprintf (f, "\tDC\tCL2'01',CL4'0100'\n"); |
1259 | fprintf (f, "FENT%03d\tDS\t0H\n", function_label_index); | |
1260 | fprintf (f, "\tSTM\t14,12,12(13)\n"); | |
1261 | fprintf (f, "\tL\t2,76(,13)\n"); | |
1262 | fprintf (f, "\tL\t0,16(,15)\n"); | |
1263 | fprintf (f, "\tALR\t0,2\n"); | |
1264 | fprintf (f, "\tCL\t0,12(,12)\n"); | |
1265 | fprintf (f, "\tBNH\t*+10\n"); | |
1266 | fprintf (f, "\tL\t15,116(,12)\n"); | |
1267 | fprintf (f, "\tBALR\t14,15\n"); | |
1268 | fprintf (f, "\tL\t15,72(,13)\n"); | |
1269 | fprintf (f, "\tSTM\t15,0,72(2)\n"); | |
1270 | fprintf (f, "\tMVI\t0(2),X'10'\n"); | |
1271 | fprintf (f, "\tST\t2,8(,13)\n "); | |
1272 | fprintf (f, "\tST\t13,4(,2)\n "); | |
1273 | fprintf (f, "\tLR\t13,2\n"); | |
1274 | fprintf (f, "\tDROP\t15\n"); | |
1275 | fprintf (f, "\tBALR\t%d,0\n", BASE_REGISTER); | |
1276 | fprintf (f, "\tUSING\t*,%d\n", BASE_REGISTER); | |
1277 | function_first = 1; | |
1278 | function_label_index ++; | |
1279 | #else /* !LE370 */ | |
a0d66c8d JL |
1280 | if (!function_first) |
1281 | { | |
1282 | struct tm *function_time; | |
1283 | time_t lcltime; | |
1284 | time (&lcltime); | |
1285 | function_time = localtime (&lcltime); | |
1286 | function_year = function_time->tm_year + 1900; | |
1287 | function_month = function_time->tm_mon + 1; | |
1288 | function_day = function_time->tm_mday; | |
1289 | function_hour = function_time->tm_hour; | |
1290 | function_minute = function_time->tm_min; | |
1291 | function_second = function_time->tm_sec; | |
1292 | fprintf (f, "PPA2\tDS\t0F\n"); | |
1293 | fprintf (f, "\tDC\tX'03',X'00',X'33',X'00'\n"); | |
1294 | fprintf (f, "\tDC\tV(CEESTART),A(0)\n"); | |
1295 | fprintf (f, "\tDC\tA(CEETIMES)\n"); | |
1296 | fprintf (f, "CEETIMES\tDS\t0F\n"); | |
1297 | fprintf (f, "\tDC\tCL4'%d',CL4'%02d%02d',CL6'%02d%02d00'\n", | |
1298 | function_year, function_month, function_day, | |
1299 | function_hour, function_minute, function_second); | |
1300 | fprintf (f, "\tDC\tCL2'01',CL4'0100'\n"); | |
1301 | } | |
92d6db66 LV |
1302 | fprintf (f, "* Function %s prologue\n", mvs_function_name); |
1303 | fprintf (f, "FDSD%03d\tDSECT\n", function_label_index); | |
a0d66c8d JL |
1304 | fprintf (f, "\tDS\tD\n"); |
1305 | fprintf (f, "\tDS\tCL(%d)\n", STACK_POINTER_OFFSET + l | |
1306 | + current_function_outgoing_args_size); | |
92d6db66 | 1307 | fprintf (f, "\tORG\tFDSD%03d\n", function_label_index); |
a0d66c8d JL |
1308 | fprintf (f, "\tDS\tCL(120+8)\n"); |
1309 | fprintf (f, "\tORG\n"); | |
1310 | fprintf (f, "\tDS\t0D\n"); | |
92d6db66 | 1311 | fprintf (f, "FDSL%03d\tEQU\t*-FDSD%03d-8\n", function_label_index, |
a0d66c8d JL |
1312 | function_label_index); |
1313 | fprintf (f, "\tDS\t0H\n"); | |
1314 | assemble_name (f, mvs_function_name); | |
92d6db66 | 1315 | fprintf (f, "\tCSECT\n"); |
a0d66c8d JL |
1316 | fprintf (f, "\tUSING\t*,15\n"); |
1317 | fprintf (f, "\tB\tFPL%03d\n", function_label_index); | |
1318 | fprintf (f, "\tDC\tAL1(FPL%03d+4-*)\n", function_label_index + 1); | |
1319 | fprintf (f, "\tDC\tX'CE',X'A0',AL1(16)\n"); | |
1320 | fprintf (f, "\tDC\tAL4(PPA2)\n"); | |
1321 | fprintf (f, "\tDC\tAL4(0)\n"); | |
92d6db66 | 1322 | fprintf (f, "\tDC\tAL4(FDSL%03d)\n", function_label_index); |
a0d66c8d JL |
1323 | fprintf (f, "FPL%03d\tEQU\t*\n", function_label_index + 1); |
1324 | fprintf (f, "\tDC\tAL2(%d),C'%s'\n", strlen (mvs_function_name), | |
1325 | mvs_function_name); | |
1326 | fprintf (f, "FPL%03d\tDS\t0H\n", function_label_index); | |
1327 | fprintf (f, "\tSTM\t14,12,12(13)\n"); | |
1328 | fprintf (f, "\tL\t2,76(,13)\n"); | |
1329 | fprintf (f, "\tL\t0,16(,15)\n"); | |
1330 | fprintf (f, "\tALR\t0,2\n"); | |
1331 | fprintf (f, "\tCL\t0,12(,12)\n"); | |
1332 | fprintf (f, "\tBNH\t*+10\n"); | |
1333 | fprintf (f, "\tL\t15,116(,12)\n"); | |
1334 | fprintf (f, "\tBALR\t14,15\n"); | |
1335 | fprintf (f, "\tL\t15,72(,13)\n"); | |
1336 | fprintf (f, "\tSTM\t15,0,72(2)\n"); | |
1337 | fprintf (f, "\tMVI\t0(2),X'10'\n"); | |
1338 | fprintf (f, "\tST\t2,8(,13)\n "); | |
1339 | fprintf (f, "\tST\t13,4(,2)\n "); | |
1340 | fprintf (f, "\tLR\t13,2\n"); | |
1341 | fprintf (f, "\tDROP\t15\n"); | |
1342 | fprintf (f, "\tBALR\t%d,0\n", BASE_REGISTER); | |
a0d66c8d | 1343 | fprintf (f, "\tUSING\t*,%d\n", BASE_REGISTER); |
92d6db66 LV |
1344 | function_first = 1; |
1345 | function_label_index += 2; | |
1346 | #endif /* !LE370 */ | |
1347 | #endif /* MACROPROLOGUE */ | |
1348 | fprintf (f, "PG%d\tEQU\t*\n", mvs_page_num ); | |
1349 | fprintf (f, "\tLR\t11,1\n"); | |
a0d66c8d | 1350 | fprintf (f, "\tL\t%d,=A(PGT%d)\n", PAGE_REGISTER, mvs_page_num); |
92d6db66 LV |
1351 | fprintf (f, "* Function %s code\n", mvs_function_name); |
1352 | ||
1353 | mvs_free_label_list (); | |
1354 | mvs_page_code = 6; | |
a0d66c8d JL |
1355 | mvs_page_lit = 4; |
1356 | mvs_check_page (f, 0, 0); | |
1357 | function_base_page = mvs_page_num; | |
92d6db66 LV |
1358 | |
1359 | /* find all labels in this routine */ | |
1360 | i370_label_scan (); | |
1361 | } | |
1362 | #endif /* TARGET_HLASM */ | |
1363 | ||
1364 | ||
1365 | #ifdef TARGET_ELF_ABI | |
1366 | /* | |
1367 | The 370_function_prolog() routine generates the current ELF ABI ES/390 prolog. | |
ff9350e7 | 1368 | It implements a stack that grows downward. |
92d6db66 LV |
1369 | It performs the following steps: |
1370 | -- saves the callers non-volatile registers on the callers stack. | |
ff9350e7 LV |
1371 | -- subtracts stackframe size from the stack pointer. |
1372 | -- stores backpointer to old caller stack. | |
92d6db66 LV |
1373 | |
1374 | XXX hack alert -- if the global var int leaf_function is non-zero, | |
1375 | then this is a leaf, and it might be possible to optimize the prologue | |
1376 | into doing even less, e.g. not grabbing a new stackframe or maybe just a | |
1377 | partial stack frame. | |
1378 | ||
1379 | XXX hack alert -- the current stack frame is bloated into twice the | |
1380 | needed size by unused entries. These entries make it marginally | |
1381 | compatible with MVS/OE/USS C environment, but really they're not used | |
1382 | and could probably chopped out. Modifications to i370.md would be needed | |
1383 | also, to quite using addresses 136, 140, etc. | |
1384 | */ | |
1385 | ||
1386 | void | |
1387 | i370_function_prolog (f, frame_size) | |
1388 | FILE *f; | |
1389 | int frame_size; | |
1390 | { | |
1391 | static int function_label_index = 1; | |
1392 | static int function_first = 0; | |
f50ec635 | 1393 | int stackframe_size, aligned_size; |
92d6db66 LV |
1394 | |
1395 | fprintf (f, "# Function prologue\n"); | |
1396 | /* define the stack, put it into its own data segment | |
1397 | FDSE == Function Stack Entry | |
1398 | FDSL == Function Stack Length */ | |
1399 | stackframe_size = | |
1400 | STACK_POINTER_OFFSET + current_function_outgoing_args_size + frame_size; | |
1401 | aligned_size = (stackframe_size + 7) >> 3; | |
1402 | aligned_size <<= 3; | |
1403 | ||
1404 | fprintf (f, "# arg_size=0x%x frame_size=0x%x aligned size=0x%x\n", | |
1405 | current_function_outgoing_args_size, frame_size, aligned_size); | |
1406 | ||
1407 | fprintf (f, "\t.using\t.,r15\n"); | |
1408 | ||
1409 | /* Branch to exectuable part of prologue. */ | |
1410 | fprintf (f, "\tB\t.LFENT%03d\n", function_label_index); | |
1411 | ||
1412 | /* write the length of the stackframe */ | |
1413 | fprintf (f, "\t.long\t%d\n", aligned_size); | |
1414 | ||
1415 | /* FENT == function prologue entry */ | |
ff9350e7 | 1416 | fprintf (f, "\t.balign 2\n.LFENT%03d:\n", |
92d6db66 LV |
1417 | function_label_index); |
1418 | ||
ff9350e7 | 1419 | /* store multiple registers 14,15,0,...12 at 12 bytes from sp */ |
92d6db66 LV |
1420 | fprintf (f, "\tSTM\tr14,r12,12(sp)\n"); |
1421 | ||
ff9350e7 LV |
1422 | /* r3 == saved callee stack pointer */ |
1423 | fprintf (f, "\tLR\tr3,sp\n"); | |
92d6db66 | 1424 | |
ff9350e7 LV |
1425 | /* 4(r15) == stackframe size */ |
1426 | fprintf (f, "\tSL\tsp,4(,r15)\n"); | |
92d6db66 | 1427 | |
ff9350e7 LV |
1428 | /* r11 points to arg list in callers stackframe; was passed in r2 */ |
1429 | fprintf (f, "\tLR\tr11,r2\n"); | |
92d6db66 LV |
1430 | |
1431 | /* store callee stack pointer at 8(sp) */ | |
ff9350e7 | 1432 | /* fprintf (f, "\tST\tsp,8(,r3)\n "); wasted cycles, no one uses this ... */ |
92d6db66 | 1433 | |
ff9350e7 LV |
1434 | /* backchain -- store caller sp at 4(callee_sp) */ |
1435 | fprintf (f, "\tST\tr3,4(,sp)\n "); | |
92d6db66 LV |
1436 | |
1437 | fprintf (f, "\t.drop\tr15\n"); | |
ff9350e7 LV |
1438 | /* Place contents of the PSW into r3 |
1439 | that is, place the address of "." into r3 */ | |
92d6db66 LV |
1440 | fprintf (f, "\tBASR\tr%d,0\n", BASE_REGISTER); |
1441 | fprintf (f, "\t.using\t.,r%d\n", BASE_REGISTER); | |
a0d66c8d | 1442 | function_first = 1; |
92d6db66 LV |
1443 | function_label_index ++; |
1444 | ||
1445 | fprintf (f, ".LPG%d:\n", mvs_page_num ); | |
1446 | fprintf (f, "\tL\tr%d,=A(.LPGT%d)\n", PAGE_REGISTER, mvs_page_num); | |
1447 | fprintf (f, "# Function code\n"); | |
1448 | ||
1449 | mvs_free_label_list (); | |
1450 | mvs_page_code = 6; | |
1451 | mvs_page_lit = 4; | |
1452 | mvs_check_page (f, 0, 0); | |
1453 | function_base_page = mvs_page_num; | |
1454 | ||
1455 | /* find all labels in this routine */ | |
1456 | i370_label_scan (); | |
a0d66c8d | 1457 | } |
92d6db66 | 1458 | #endif /* TARGET_ELF_ABI */ |