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