1#  Copyright (C) 2013-2021 Free Software Foundation, Inc.
2#
3# This program is free software; you can redistribute it and/or modify it
4# under the terms of the GNU General Public License as published by the
5# Free Software Foundation; either version 3, or (at your option) any
6# later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
14# along with this program; see the file COPYING3.  If not see
15# <http://www.gnu.org/licenses/>.
16
17# This Awk script takes passes.def and writes pass-instances.def,
18# counting the instances of each kind of pass, adding an instance number
19# to everywhere that NEXT_PASS is used.
20# Also handle INSERT_PASS_AFTER, INSERT_PASS_BEFORE and REPLACE_PASS
21# directives.
22#
23# For example, the single-instanced pass:
24#     NEXT_PASS (pass_warn_unused_result);
25# becomes this in the output:
26#     NEXT_PASS (pass_warn_unused_result, 1);
27#
28# The various instances of
29#   NEXT_PASS (pass_copy_prop);
30# become:
31#   NEXT_PASS (pass_copy_prop, 1);
32# through:
33#   NEXT_PASS (pass_copy_prop, 8);
34# (currently there are 8 instances of that pass)
35#
36#     INSERT_PASS_AFTER (pass_copy_prop, 1, pass_stv);
37# will insert
38#     NEXT_PASS (pass_stv, 1);
39# immediately after the NEXT_PASS (pass_copy_prop, 1) line,
40# similarly INSERT_PASS_BEFORE inserts immediately before that line.
41#     REPLACE_PASS (pass_copy_prop, 1, pass_stv, true);
42# will replace NEXT_PASS (pass_copy_prop, 1) line with
43#     NEXT_PASS (pass_stv, 1, true);
44# line and renumber all higher pass_copy_prop instances if any.
45
46# Usage: awk -f gen-pass-instances.awk passes.def
47
48BEGIN {
49  print "/* This file is auto-generated by gen-pass-instances.awk";
50  print "   from passes.def.  */";
51  lineno = 1;
52}
53
54function parse_line(line, fnname,	len_of_call, len_of_start,
55					len_of_open, len_of_close,
56					len_of_args, args_start_at,
57					args_str, len_of_prefix,
58					call_starts_at,
59					postfix_starts_at)
60{
61  # Find call expression.
62  call_starts_at = match(line, fnname " \\(.+\\)");
63  if (call_starts_at == 0)
64    return 0;
65
66  # Length of the call expression.
67  len_of_call = RLENGTH;
68
69  len_of_start = length(fnname " (");
70  len_of_open = length("(");
71  len_of_close = length(")");
72
73  # Find arguments
74  len_of_args = len_of_call - (len_of_start + len_of_close);
75  args_start_at = call_starts_at + len_of_start;
76  args_str = substr(line, args_start_at, len_of_args);
77  split(args_str, args, ",");
78
79  # Find call expression prefix
80  len_of_prefix = call_starts_at - 1;
81  prefix = substr(line, 1, len_of_prefix);
82
83  # Find call expression postfix
84  postfix_starts_at = call_starts_at + len_of_call;
85  postfix = substr(line, postfix_starts_at);
86  return 1;
87}
88
89function adjust_linenos(above, increment,	p, i)
90{
91  for (p in pass_lines)
92    if (pass_lines[p] >= above)
93      pass_lines[p] += increment;
94  if (increment > 0)
95    for (i = lineno - 1; i >= above; i--)
96      lines[i + increment] = lines[i];
97  else
98    for (i = above; i < lineno; i++)
99      lines[i + increment] = lines[i];
100  lineno += increment;
101}
102
103function insert_remove_pass(line, fnname,	arg3)
104{
105  parse_line($0, fnname);
106  pass_name = args[1];
107  if (pass_name == "PASS")
108    return 1;
109  pass_num = args[2] + 0;
110  arg3 = args[3];
111  sub(/^[ \t]*/, "", arg3);
112  new_line = prefix "NEXT_PASS (" arg3;
113  if (args[4])
114    new_line = new_line "," args[4];
115  new_line = new_line ")" postfix;
116  if (!pass_lines[pass_name, pass_num])
117    {
118      print "ERROR: Can't locate instance of the pass mentioned in " fnname;
119      return 1;
120    }
121  return 0;
122}
123
124function insert_pass(line, fnname, after,		num)
125{
126  if (insert_remove_pass(line, fnname))
127    return;
128  num = pass_lines[pass_name, pass_num];
129  adjust_linenos(num + after, 1);
130  pass_name = args[3];
131  # Set pass_counts
132  if (args[3] in pass_counts)
133    pass_counts[pass_name]++;
134  else
135    pass_counts[pass_name] = 1;
136
137  pass_lines[pass_name, pass_counts[pass_name]] = num + after;
138  lines[num + after] = new_line;
139}
140
141function replace_pass(line, fnname,			num, i)
142{
143  if (insert_remove_pass(line, "REPLACE_PASS"))
144    return;
145  num = pass_lines[pass_name, pass_num];
146  for (i = pass_counts[pass_name]; i > pass_num; i--)
147    pass_lines[pass_name, i - 1] = pass_lines[pass_name, i];
148  delete pass_lines[pass_name, pass_counts[pass_name]];
149  if (pass_counts[pass_name] == 1)
150    delete pass_counts[pass_name];
151  else
152    pass_counts[pass_name]--;
153
154  pass_name = args[3];
155  # Set pass_counts
156  if (args[3] in pass_counts)
157    pass_counts[pass_name]++;
158  else
159    pass_counts[pass_name] = 1;
160
161  pass_lines[pass_name, pass_counts[pass_name]] = num;
162  lines[num] = new_line;
163}
164
165/INSERT_PASS_AFTER \(.+\)/ {
166  insert_pass($0, "INSERT_PASS_AFTER", 1);
167  next;
168}
169
170/INSERT_PASS_BEFORE \(.+\)/ {
171  insert_pass($0, "INSERT_PASS_BEFORE", 0);
172  next;
173}
174
175/REPLACE_PASS \(.+\)/ {
176  replace_pass($0, "REPLACE_PASS");
177  next;
178}
179
180{
181  ret = parse_line($0, "NEXT_PASS");
182  if (ret)
183    {
184      pass_name = args[1];
185
186      # Set pass_counts
187      if (pass_name in pass_counts)
188	pass_counts[pass_name]++;
189      else
190	pass_counts[pass_name] = 1;
191
192      pass_lines[pass_name, pass_counts[pass_name]] = lineno;
193    }
194  lines[lineno++] = $0;
195}
196
197END {
198  for (i = 1; i < lineno; i++)
199    {
200      ret = parse_line(lines[i], "NEXT_PASS");
201      if (ret)
202	{
203	  # Set pass_name argument, an optional with_arg argument
204	  pass_name = args[1];
205	  with_arg = args[2];
206
207	  # Set pass_final_counts
208	  if (pass_name in pass_final_counts)
209	    pass_final_counts[pass_name]++;
210	  else
211	    pass_final_counts[pass_name] = 1;
212
213	  pass_num = pass_final_counts[pass_name];
214
215	  # Print call expression with extra pass_num argument
216	  printf "%s", prefix;
217	  if (with_arg)
218	    printf "NEXT_PASS_WITH_ARG";
219	  else
220	    printf "NEXT_PASS";
221	  printf " (%s, %s", pass_name, pass_num;
222	  if (with_arg)
223	    printf ",%s", with_arg;
224	  printf ")%s\n", postfix;
225	}
226      else
227	print lines[i];
228    }
229}
230
231# Local Variables:
232# mode:awk
233# c-basic-offset:8
234# End:
235