xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/config/pru/pru-passes.c (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
1 /* PRU target specific passes
2    Copyright (C) 2017-2020 Free Software Foundation, Inc.
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:
pass_tiabi_check(gcc::context * ctxt)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 
gate(function * fun ATTRIBUTE_UNUSED)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 
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
chkp_type_has_function_pointer(const_tree type)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
chk_function_decl(const_tree fntype,location_t call_location)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
check_op_callback(tree * tp,int * walk_subtrees,void * data)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
execute(function * fun)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 *
make_pass_tiabi_check(gcc::context * ctxt)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
pru_register_abicheck_pass(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 }
229