#!/usr/bin/awk -f
|
# SPDX-License-Identifier: GPL-2.0
|
|
# Modify SRCU for formal verification. The first argument should be srcu.h and
|
# the second should be srcu.c. Outputs modified srcu.h and srcu.c into the
|
# current directory.
|
|
BEGIN {
|
if (ARGC != 5) {
|
print "Usange: input.h input.c output.h output.c" > "/dev/stderr";
|
exit 1;
|
}
|
h_output = ARGV[3];
|
c_output = ARGV[4];
|
ARGC = 3;
|
|
# Tokenize using FS and not RS as FS supports regular expressions. Each
|
# record is one line of source, except that backslashed lines are
|
# combined. Comments are treated as field separators, as are quotes.
|
quote_regexp="\"([^\\\\\"]|\\\\.)*\"";
|
comment_regexp="\\/\\*([^*]|\\*+[^*/])*\\*\\/|\\/\\/.*(\n|$)";
|
FS="([ \\\\\t\n\v\f;,.=(){}+*/<>&|^-]|\\[|\\]|" comment_regexp "|" quote_regexp ")+";
|
|
inside_srcu_struct = 0;
|
inside_srcu_init_def = 0;
|
srcu_init_param_name = "";
|
in_macro = 0;
|
brace_nesting = 0;
|
paren_nesting = 0;
|
|
# Allow the manipulation of the last field separator after has been
|
# seen.
|
last_fs = "";
|
# Whether the last field separator was intended to be output.
|
last_fs_print = 0;
|
|
# rcu_batches stores the initialization for each instance of struct
|
# rcu_batch
|
|
in_comment = 0;
|
|
outputfile = "";
|
}
|
|
{
|
prev_outputfile = outputfile;
|
if (FILENAME ~ /\.h$/) {
|
outputfile = h_output;
|
if (FNR != NR) {
|
print "Incorrect file order" > "/dev/stderr";
|
exit 1;
|
}
|
}
|
else
|
outputfile = c_output;
|
|
if (prev_outputfile && outputfile != prev_outputfile) {
|
new_outputfile = outputfile;
|
outputfile = prev_outputfile;
|
update_fieldsep("", 0);
|
outputfile = new_outputfile;
|
}
|
}
|
|
# Combine the next line into $0.
|
function combine_line() {
|
ret = getline next_line;
|
if (ret == 0) {
|
# Don't allow two consecutive getlines at the end of the file
|
if (eof_found) {
|
print "Error: expected more input." > "/dev/stderr";
|
exit 1;
|
} else {
|
eof_found = 1;
|
}
|
} else if (ret == -1) {
|
print "Error reading next line of file" FILENAME > "/dev/stderr";
|
exit 1;
|
}
|
$0 = $0 "\n" next_line;
|
}
|
|
# Combine backslashed lines and multiline comments.
|
function combine_backslashes() {
|
while (/\\$|\/\*([^*]|\*+[^*\/])*\**$/) {
|
combine_line();
|
}
|
}
|
|
function read_line() {
|
combine_line();
|
combine_backslashes();
|
}
|
|
# Print out field separators and update variables that depend on them. Only
|
# print if p is true. Call with sep="" and p=0 to print out the last field
|
# separator.
|
function update_fieldsep(sep, p) {
|
# Count braces
|
sep_tmp = sep;
|
gsub(quote_regexp "|" comment_regexp, "", sep_tmp);
|
while (1)
|
{
|
if (sub("[^{}()]*\\{", "", sep_tmp)) {
|
brace_nesting++;
|
continue;
|
}
|
if (sub("[^{}()]*\\}", "", sep_tmp)) {
|
brace_nesting--;
|
if (brace_nesting < 0) {
|
print "Unbalanced braces!" > "/dev/stderr";
|
exit 1;
|
}
|
continue;
|
}
|
if (sub("[^{}()]*\\(", "", sep_tmp)) {
|
paren_nesting++;
|
continue;
|
}
|
if (sub("[^{}()]*\\)", "", sep_tmp)) {
|
paren_nesting--;
|
if (paren_nesting < 0) {
|
print "Unbalanced parenthesis!" > "/dev/stderr";
|
exit 1;
|
}
|
continue;
|
}
|
|
break;
|
}
|
|
if (last_fs_print)
|
printf("%s", last_fs) > outputfile;
|
last_fs = sep;
|
last_fs_print = p;
|
}
|
|
# Shifts the fields down by n positions. Calls next if there are no more. If p
|
# is true then print out field separators.
|
function shift_fields(n, p) {
|
do {
|
if (match($0, FS) > 0) {
|
update_fieldsep(substr($0, RSTART, RLENGTH), p);
|
if (RSTART + RLENGTH <= length())
|
$0 = substr($0, RSTART + RLENGTH);
|
else
|
$0 = "";
|
} else {
|
update_fieldsep("", 0);
|
print "" > outputfile;
|
next;
|
}
|
} while (--n > 0);
|
}
|
|
# Shifts and prints the first n fields.
|
function print_fields(n) {
|
do {
|
update_fieldsep("", 0);
|
printf("%s", $1) > outputfile;
|
shift_fields(1, 1);
|
} while (--n > 0);
|
}
|
|
{
|
combine_backslashes();
|
}
|
|
# Print leading FS
|
{
|
if (match($0, "^(" FS ")+") > 0) {
|
update_fieldsep(substr($0, RSTART, RLENGTH), 1);
|
if (RSTART + RLENGTH <= length())
|
$0 = substr($0, RSTART + RLENGTH);
|
else
|
$0 = "";
|
}
|
}
|
|
# Parse the line.
|
{
|
while (NF > 0) {
|
if ($1 == "struct" && NF < 3) {
|
read_line();
|
continue;
|
}
|
|
if (FILENAME ~ /\.h$/ && !inside_srcu_struct &&
|
brace_nesting == 0 && paren_nesting == 0 &&
|
$1 == "struct" && $2 == "srcu_struct" &&
|
$0 ~ "^struct(" FS ")+srcu_struct(" FS ")+\\{") {
|
inside_srcu_struct = 1;
|
print_fields(2);
|
continue;
|
}
|
if (inside_srcu_struct && brace_nesting == 0 &&
|
paren_nesting == 0) {
|
inside_srcu_struct = 0;
|
update_fieldsep("", 0);
|
for (name in rcu_batches)
|
print "extern struct rcu_batch " name ";" > outputfile;
|
}
|
|
if (inside_srcu_struct && $1 == "struct" && $2 == "rcu_batch") {
|
# Move rcu_batches outside of the struct.
|
rcu_batches[$3] = "";
|
shift_fields(3, 1);
|
sub(/;[[:space:]]*$/, "", last_fs);
|
continue;
|
}
|
|
if (FILENAME ~ /\.h$/ && !inside_srcu_init_def &&
|
$1 == "#define" && $2 == "__SRCU_STRUCT_INIT") {
|
inside_srcu_init_def = 1;
|
srcu_init_param_name = $3;
|
in_macro = 1;
|
print_fields(3);
|
continue;
|
}
|
if (inside_srcu_init_def && brace_nesting == 0 &&
|
paren_nesting == 0) {
|
inside_srcu_init_def = 0;
|
in_macro = 0;
|
continue;
|
}
|
|
if (inside_srcu_init_def && brace_nesting == 1 &&
|
paren_nesting == 0 && last_fs ~ /\.[[:space:]]*$/ &&
|
$1 ~ /^[[:alnum:]_]+$/) {
|
name = $1;
|
if (name in rcu_batches) {
|
# Remove the dot.
|
sub(/\.[[:space:]]*$/, "", last_fs);
|
|
old_record = $0;
|
do
|
shift_fields(1, 0);
|
while (last_fs !~ /,/ || paren_nesting > 0);
|
end_loc = length(old_record) - length($0);
|
end_loc += index(last_fs, ",") - length(last_fs);
|
|
last_fs = substr(last_fs, index(last_fs, ",") + 1);
|
last_fs_print = 1;
|
|
match(old_record, "^"name"("FS")+=");
|
start_loc = RSTART + RLENGTH;
|
|
len = end_loc - start_loc;
|
initializer = substr(old_record, start_loc, len);
|
gsub(srcu_init_param_name "\\.", "", initializer);
|
rcu_batches[name] = initializer;
|
continue;
|
}
|
}
|
|
# Don't include a nonexistent file
|
if (!in_macro && $1 == "#include" && /^#include[[:space:]]+"rcu\.h"/) {
|
update_fieldsep("", 0);
|
next;
|
}
|
|
# Ignore most preprocessor stuff.
|
if (!in_macro && $1 ~ /#/) {
|
break;
|
}
|
|
if (brace_nesting > 0 && $1 ~ "^[[:alnum:]_]+$" && NF < 2) {
|
read_line();
|
continue;
|
}
|
if (brace_nesting > 0 &&
|
$0 ~ "^[[:alnum:]_]+[[:space:]]*(\\.|->)[[:space:]]*[[:alnum:]_]+" &&
|
$2 in rcu_batches) {
|
# Make uses of rcu_batches global. Somewhat unreliable.
|
shift_fields(1, 0);
|
print_fields(1);
|
continue;
|
}
|
|
if ($1 == "static" && NF < 3) {
|
read_line();
|
continue;
|
}
|
if ($1 == "static" && ($2 == "bool" && $3 == "try_check_zero" ||
|
$2 == "void" && $3 == "srcu_flip")) {
|
shift_fields(1, 1);
|
print_fields(2);
|
continue;
|
}
|
|
# Distinguish between read-side and write-side memory barriers.
|
if ($1 == "smp_mb" && NF < 2) {
|
read_line();
|
continue;
|
}
|
if (match($0, /^smp_mb[[:space:]();\/*]*[[:alnum:]]/)) {
|
barrier_letter = substr($0, RLENGTH, 1);
|
if (barrier_letter ~ /A|D/)
|
new_barrier_name = "sync_smp_mb";
|
else if (barrier_letter ~ /B|C/)
|
new_barrier_name = "rs_smp_mb";
|
else {
|
print "Unrecognized memory barrier." > "/dev/null";
|
exit 1;
|
}
|
|
shift_fields(1, 1);
|
printf("%s", new_barrier_name) > outputfile;
|
continue;
|
}
|
|
# Skip definition of rcu_synchronize, since it is already
|
# defined in misc.h. Only present in old versions of srcu.
|
if (brace_nesting == 0 && paren_nesting == 0 &&
|
$1 == "struct" && $2 == "rcu_synchronize" &&
|
$0 ~ "^struct(" FS ")+rcu_synchronize(" FS ")+\\{") {
|
shift_fields(2, 0);
|
while (brace_nesting) {
|
if (NF < 2)
|
read_line();
|
shift_fields(1, 0);
|
}
|
}
|
|
# Skip definition of wakeme_after_rcu for the same reason
|
if (brace_nesting == 0 && $1 == "static" && $2 == "void" &&
|
$3 == "wakeme_after_rcu") {
|
while (NF < 5)
|
read_line();
|
shift_fields(3, 0);
|
do {
|
while (NF < 3)
|
read_line();
|
shift_fields(1, 0);
|
} while (paren_nesting || brace_nesting);
|
}
|
|
if ($1 ~ /^(unsigned|long)$/ && NF < 3) {
|
read_line();
|
continue;
|
}
|
|
# Give srcu_batches_completed the correct type for old SRCU.
|
if (brace_nesting == 0 && $1 == "long" &&
|
$2 == "srcu_batches_completed") {
|
update_fieldsep("", 0);
|
printf("unsigned ") > outputfile;
|
print_fields(2);
|
continue;
|
}
|
if (brace_nesting == 0 && $1 == "unsigned" && $2 == "long" &&
|
$3 == "srcu_batches_completed") {
|
print_fields(3);
|
continue;
|
}
|
|
# Just print out the input code by default.
|
print_fields(1);
|
}
|
update_fieldsep("", 0);
|
print > outputfile;
|
next;
|
}
|
|
END {
|
update_fieldsep("", 0);
|
|
if (brace_nesting != 0) {
|
print "Unbalanced braces!" > "/dev/stderr";
|
exit 1;
|
}
|
|
# Define the rcu_batches
|
for (name in rcu_batches)
|
print "struct rcu_batch " name " = " rcu_batches[name] ";" > c_output;
|
}
|