#!/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 () { 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 () { 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 $!;