]>
Commit | Line | Data |
---|---|---|
8d2af3a2 | 1 | /* PRU target specific passes |
a945c346 | 2 | Copyright (C) 2017-2024 Free Software Foundation, Inc. |
8d2af3a2 DD |
3 | Dimitar Dimitrov <dimitar@dinux.eu> |
4 | ||
5 | This file is part of GCC. | |
6 | ||
7 | GCC is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU General Public License as published | |
9 | by the Free Software Foundation; either version 3, or (at your | |
10 | option) any later version. | |
11 | ||
12 | GCC is distributed in the hope that it will be useful, but WITHOUT | |
13 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | |
14 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public | |
15 | License for more details. | |
16 | ||
17 | You should have received a copy of the GNU General Public License | |
18 | along with GCC; see the file COPYING3. If not see | |
19 | <http://www.gnu.org/licenses/>. */ | |
20 | ||
21 | #define IN_TARGET_CODE 1 | |
22 | ||
23 | #include "config.h" | |
24 | #include "system.h" | |
25 | #include "coretypes.h" | |
26 | #include "backend.h" | |
27 | #include "context.h" | |
28 | #include "tm.h" | |
29 | #include "alias.h" | |
30 | #include "symtab.h" | |
31 | #include "tree.h" | |
32 | #include "diagnostic-core.h" | |
33 | #include "function.h" | |
34 | #include "gimple.h" | |
35 | #include "gimple-iterator.h" | |
36 | #include "gimple-walk.h" | |
37 | #include "gimple-expr.h" | |
38 | #include "tree-pass.h" | |
39 | ||
40 | #include "pru-protos.h" | |
41 | ||
42 | namespace { | |
43 | ||
44 | /* Scan the tree to ensure that the compiled code by GCC | |
45 | conforms to the TI ABI specification. If GCC cannot | |
46 | output a conforming code, raise an error. */ | |
47 | const pass_data pass_data_tiabi_check = | |
48 | { | |
49 | GIMPLE_PASS, /* type */ | |
50 | "*tiabi_check", /* name */ | |
51 | OPTGROUP_NONE, /* optinfo_flags */ | |
52 | TV_NONE, /* tv_id */ | |
53 | PROP_gimple_any, /* properties_required */ | |
54 | 0, /* properties_provided */ | |
55 | 0, /* properties_destroyed */ | |
56 | 0, /* todo_flags_start */ | |
57 | 0, /* todo_flags_finish */ | |
58 | }; | |
59 | ||
60 | /* Implementation class for the TI ABI compliance-check pass. */ | |
61 | class pass_tiabi_check : public gimple_opt_pass | |
62 | { | |
63 | public: | |
64 | pass_tiabi_check (gcc::context *ctxt) | |
65 | : gimple_opt_pass (pass_data_tiabi_check, ctxt) | |
66 | {} | |
67 | ||
68 | /* opt_pass methods: */ | |
69 | virtual unsigned int execute (function *); | |
70 | ||
71 | virtual bool gate (function *fun ATTRIBUTE_UNUSED) | |
72 | { | |
73 | return pru_current_abi == PRU_ABI_TI; | |
74 | } | |
75 | ||
76 | }; // class pass_tiabi_check | |
77 | \f | |
78 | /* Return 1 if type TYPE is a pointer to function type or a | |
79 | structure having a pointer to function type as one of its fields. | |
80 | Otherwise return 0. */ | |
81 | static bool | |
82 | chkp_type_has_function_pointer (const_tree type) | |
83 | { | |
84 | bool res = false; | |
85 | ||
86 | if (POINTER_TYPE_P (type) && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (type))) | |
87 | res = true; | |
88 | else if (RECORD_OR_UNION_TYPE_P (type)) | |
89 | { | |
90 | tree field; | |
91 | ||
92 | for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field)) | |
93 | if (TREE_CODE (field) == FIELD_DECL) | |
94 | res = res || chkp_type_has_function_pointer (TREE_TYPE (field)); | |
95 | } | |
96 | else if (TREE_CODE (type) == ARRAY_TYPE) | |
97 | res = chkp_type_has_function_pointer (TREE_TYPE (type)); | |
98 | ||
99 | return res; | |
100 | } | |
101 | ||
102 | /* Check the function declaration FNTYPE for TI ABI compatibility. */ | |
103 | static void | |
104 | chk_function_decl (const_tree fntype, location_t call_location) | |
105 | { | |
106 | /* GCC does not check if the RETURN VALUE pointer is NULL, | |
107 | so do not allow GCC functions with large return values. */ | |
108 | if (!VOID_TYPE_P (TREE_TYPE (fntype)) | |
109 | && pru_return_in_memory (TREE_TYPE (fntype), fntype)) | |
110 | error_at (call_location, | |
111 | "large return values not supported with %<-mabi=ti%> option"); | |
112 | ||
113 | /* Check this function's arguments. */ | |
114 | for (tree p = TYPE_ARG_TYPES (fntype); p; p = TREE_CHAIN (p)) | |
115 | { | |
116 | tree arg_type = TREE_VALUE (p); | |
117 | if (chkp_type_has_function_pointer (arg_type)) | |
118 | error_at (call_location, | |
119 | "function pointers not supported with %<-mabi=ti%> option"); | |
120 | } | |
121 | } | |
122 | ||
123 | /* Callback for walk_gimple_seq that checks TP tree for TI ABI compliance. */ | |
124 | static tree | |
125 | check_op_callback (tree *tp, int *walk_subtrees, void *data) | |
126 | { | |
127 | struct walk_stmt_info *wi = (struct walk_stmt_info *) data; | |
128 | ||
129 | if (RECORD_OR_UNION_TYPE_P (*tp) || TREE_CODE (*tp) == ENUMERAL_TYPE) | |
130 | { | |
131 | /* Forward declarations have NULL tree type. Skip them. */ | |
132 | if (TREE_TYPE (*tp) == NULL) | |
133 | return NULL; | |
134 | } | |
135 | ||
136 | /* TODO - why C++ leaves INTEGER_TYPE forward declarations around? */ | |
137 | if (TREE_TYPE (*tp) == NULL) | |
138 | return NULL; | |
139 | ||
140 | const tree type = TREE_TYPE (*tp); | |
141 | ||
142 | /* Direct function calls are allowed, obviously. */ | |
143 | gcall *call = dyn_cast <gcall *> (gsi_stmt (wi->gsi)); | |
144 | if (call | |
145 | && tp == gimple_call_fn_ptr (call) | |
146 | && gimple_call_fndecl (call)) | |
147 | return NULL; | |
148 | ||
149 | switch (TREE_CODE (type)) | |
150 | { | |
151 | case FUNCTION_TYPE: | |
152 | case METHOD_TYPE: | |
153 | { | |
154 | /* Note: Do not enforce a small return value. It is safe to | |
155 | call any TI ABI function from GCC, since GCC will | |
156 | never pass NULL. */ | |
157 | ||
158 | /* Check arguments for function pointers. */ | |
159 | for (tree p = TYPE_ARG_TYPES (type); p; p = TREE_CHAIN (p)) | |
160 | { | |
161 | tree arg_type = TREE_VALUE (p); | |
162 | if (chkp_type_has_function_pointer (arg_type)) | |
163 | error_at (gimple_location (wi->stmt), "function pointers " | |
164 | "not supported with %<-mabi=ti%> option"); | |
165 | } | |
166 | break; | |
167 | } | |
168 | case RECORD_TYPE: | |
169 | case UNION_TYPE: | |
170 | case QUAL_UNION_TYPE: | |
171 | case POINTER_TYPE: | |
172 | { | |
173 | if (chkp_type_has_function_pointer (type)) | |
174 | { | |
175 | error_at (gimple_location (wi->stmt), | |
176 | "function pointers not supported with " | |
177 | "%<-mabi=ti%> option"); | |
178 | *walk_subtrees = false; | |
179 | } | |
180 | break; | |
181 | } | |
182 | default: | |
183 | break; | |
184 | } | |
185 | return NULL; | |
186 | } | |
187 | ||
188 | /* Pass implementation. */ | |
189 | unsigned | |
190 | pass_tiabi_check::execute (function *fun) | |
191 | { | |
192 | struct walk_stmt_info wi; | |
193 | const_tree fntype = TREE_TYPE (fun->decl); | |
194 | ||
195 | gimple_seq body = gimple_body (current_function_decl); | |
196 | ||
197 | memset (&wi, 0, sizeof (wi)); | |
198 | wi.info = NULL; | |
199 | wi.want_locations = true; | |
200 | ||
201 | /* Check the function body. */ | |
202 | walk_gimple_seq (body, NULL, check_op_callback, &wi); | |
203 | ||
204 | /* Check the function declaration. */ | |
205 | chk_function_decl (fntype, fun->function_start_locus); | |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | } // anon namespace | |
211 | ||
212 | gimple_opt_pass * | |
213 | make_pass_tiabi_check (gcc::context *ctxt) | |
214 | { | |
215 | return new pass_tiabi_check (ctxt); | |
216 | } | |
217 | ||
218 | /* Register as early as possible. */ | |
219 | void | |
220 | pru_register_abicheck_pass (void) | |
221 | { | |
222 | opt_pass *tiabi_check = make_pass_tiabi_check (g); | |
223 | struct register_pass_info tiabi_check_info | |
224 | = { tiabi_check, "*warn_unused_result", | |
225 | 1, PASS_POS_INSERT_AFTER | |
226 | }; | |
227 | register_pass (&tiabi_check_info); | |
228 | } |