1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
#!/usr/bin/perl -w
use strict;
use Data::Dumper;
our %enums;
# Usage: abi-check C-file Ocaml-file
# Writes out a BUILD_BUG_ON() list to be included back into C.
#
# Ocaml-file should be the .ml file. The ocaml compiler will check
# that any declarations in a .mli correspond. We check the .ml
# rather than the .mli in case there are private types in future.
@ARGV == 2 or die;
our ($c, $o) = @ARGV;
open C_FILE, "<", $c or die $!;
our $cline = -1;
our $ei;
# Parse the C file looking for calls to:
# c_bitmap_to_ocaml_list()
# ocaml_list_to_c_bitmap()
#
# followed by anotations of the following form:
# /* ! OType OPrefix Mangle */
# /* ! CPrefix CFinal CFinalHow */
# or, for subsequent invocations for the same OType, just
# /* ! OType */
#
# The function definitions use /* ! */ which simply skips that instance.
while (<C_FILE>) {
if ($cline == -1) {
if (m/c_bitmap_to_ocaml_list|ocaml_list_to_c_bitmap/) {
$cline = 0;
$ei = { };
}
} else {
$cline++;
m{^\s+/\* \s+ ! \s+ (.*?) \s* \*/\s*$}x or
die "at line $cline of annotation, did not expect $_ ?";
my @vals = split /\s+/, $1;
if ($cline == 1 && !@vals) {
$cline = -1;
} elsif ($cline == 1 && @vals == 1) {
my ($otype) = @vals;
die "reference to undefined OType $otype" unless $enums{$otype};
$cline = -1;
} elsif ($cline == 1 && @vals == 3) {
$ei->{$_} = shift @vals foreach qw(OType OPrefix Mangle);
} elsif ($cline == 2 && @vals == 3) {
$ei->{$_} = shift @vals foreach qw(CPrefix CFinal CFinalHow);
die "redefining OType $ei->{OType}" if $enums{ $ei->{OType} };
$enums{ $ei->{OType} } = $ei;
$cline = -1;
} else {
die "$_ ?";
}
}
}
sub expect ($$) {
printf "BUILD_BUG_ON( %-30s != %-10s );\n", @_ or die $!;
}
open OCAML_FILE, "<", $o or die $!;
my $cval;
$ei = undef;
my $bitnum = 0;
while (<OCAML_FILE>) {
if ($ei) {
if (m{^\s+ \| \s* $ei->{OPrefix} (\w+) \s*$}x) {
$cval = $1;
if ($ei->{Mangle} eq 'lc') {
$cval = lc $cval;
} elsif ($ei->{Mangle} eq 'none') {
} else {
die;
}
$cval = $ei->{CPrefix}.$cval;
expect($cval, "(1u << $bitnum)");
$bitnum++;
} elsif (m/^\w|\{/) {
if ($ei->{CFinalHow} eq 'max') {
expect($ei->{CFinal}, "(1u << ".($bitnum-1).")");
} elsif ($ei->{CFinalHow} eq 'all') {
expect($ei->{CFinal}, "(1u << $bitnum)-1u");
} else {
die Dumper($ei)." ?";
}
$ei->{Checked} = 1;
$ei = undef;
} elsif (!m{\S}) {
} else {
die "$_ ?";
}
}
if (!$ei) {
if (m{^type \s+ (\w+) \s* \= \s* $}x && $enums{$1}) {
print "// found ocaml type $1 at $o:$.\n" or die $!;
$ei = $enums{$1};
$cval = '';
$bitnum = 0;
}
}
}
foreach $ei (values %enums) {
next if $ei->{Checked};
die "did not find ocaml type definition for $ei->{OType} in $o";
}
close STDOUT or die $!;
|