blob: 07a8783ff52e51c3c83b595e41c7229d705a7074 [file] [log] [blame]
Russ Cox602a4462009-06-01 22:14:57 -07001#!/usr/bin/perl
2# Copyright 2009 The Go Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style
4# license that can be found in the LICENSE file.
5
6# This program reads a file containing function prototypes
7# (like syscall_darwin.go) and generates system call bodies.
8# The prototypes are marked by lines beginning with "//sys"
9# and read like func declarations if //sys is replaced by func, but:
10# * The parameter lists must give a name for each argument.
11# This includes return parameters.
12# * The parameter lists must give a type for each argument:
13# the (x, y, z int) shorthand is not allowed.
14# * If the return parameter is an error number, it must be named errno.
15
Rob Pikecd324982009-08-13 13:22:37 -070016$cmdline = "mksyscall.sh " . join(' ', @ARGV);
Russ Cox602a4462009-06-01 22:14:57 -070017$errors = 0;
Russ Cox01b695d2009-06-08 22:10:48 -070018$_32bit = "";
Russ Cox602a4462009-06-01 22:14:57 -070019
20if($ARGV[0] eq "-b32") {
21 $_32bit = "big-endian";
22 shift;
23} elsif($ARGV[0] eq "-l32") {
24 $_32bit = "little-endian";
25 shift;
26}
27
28if($ARGV[0] =~ /^-/) {
Rob Pikecd324982009-08-13 13:22:37 -070029 print STDERR "usage: mksyscall.sh [-b32 | -l32] [file ...]\n";
Russ Cox602a4462009-06-01 22:14:57 -070030 exit 1;
31}
32
33sub parseparamlist($) {
34 my ($list) = @_;
35 $list =~ s/^\s*//;
36 $list =~ s/\s*$//;
37 if($list eq "") {
38 return ();
39 }
40 return split(/\s*,\s*/, $list);
41}
42
43sub parseparam($) {
44 my ($p) = @_;
45 if($p !~ /^(\S*) (\S*)$/) {
46 print STDERR "$ARGV:$.: malformed parameter: $p\n";
47 $errors = 1;
48 return ("xx", "int");
49 }
50 return ($1, $2);
51}
52
53$text = "";
54while(<>) {
55 chomp;
56 s/\s+/ /g;
57 s/^\s+//;
58 s/\s+$//;
59 next if !/^\/\/sys /;
60
61 # Line must be of the form
62 # func Open(path string, mode int, perm int) (fd int, errno int)
63 # Split into name, in params, out params.
64 if(!/^\/\/sys (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(SYS_[A-Z0-9_]+))?$/) {
65 print STDERR "$ARGV:$.: malformed //sys declaration\n";
66 $errors = 1;
67 next;
68 }
69 my ($func, $in, $out, $sysname) = ($1, $2, $3, $4);
70
71 # Split argument lists on comma.
72 my @in = parseparamlist($in);
73 my @out = parseparamlist($out);
74
75 # Go function header.
76 $text .= sprintf "func %s(%s) (%s) {\n", $func, join(', ', @in), join(', ', @out);
77
78 # Prepare arguments to Syscall.
79 my @args = ();
80 my $n = 0;
81 foreach my $p (@in) {
82 my ($name, $type) = parseparam($p);
83 if($type =~ /^\*/) {
84 push @args, "uintptr(unsafe.Pointer($name))";
85 } elsif($type eq "string") {
86 push @args, "uintptr(unsafe.Pointer(StringBytePtr($name)))";
87 } elsif($type =~ /^\[\](.*)/) {
88 # Convert slice into pointer, length.
89 # Have to be careful not to take address of &a[0] if len == 0:
90 # pass nil in that case.
91 $text .= "\tvar _p$n *$1;\n";
92 $text .= "\tif len($name) > 0 { _p$n = \&${name}[0]; }\n";
93 push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))";
94 $n++;
95 } elsif($type eq "int64" && $_32bit ne "") {
96 if($_32bit eq "big-endian") {
97 push @args, "uintptr($name >> 32)", "uintptr($name)";
98 } else {
99 push @args, "uintptr($name)", "uintptr($name >> 32)";
100 }
101 } else {
102 push @args, "uintptr($name)";
103 }
104 }
105
106 # Determine which form to use; pad args with zeros.
107 my $asm = "Syscall";
108 if(@args <= 3) {
109 while(@args < 3) {
110 push @args, "0";
111 }
112 } elsif(@args <= 6) {
113 $asm = "Syscall6";
114 while(@args < 6) {
115 push @args, "0";
116 }
117 } else {
118 print STDERR "$ARGV:$.: too many arguments to system call\n";
119 }
120
121 # System call number.
122 if($sysname eq "") {
123 $sysname = "SYS_$func";
124 $sysname =~ s/([a-z])([A-Z])/${1}_$2/g; # turn FooBar into Foo_Bar
125 $sysname =~ y/a-z/A-Z/;
126 }
127
128 # Actual call.
129 my $args = join(', ', @args);
Russ Cox98c98192009-09-15 13:51:33 -0700130 my $call = "$asm($sysname, $args)";
Russ Cox602a4462009-06-01 22:14:57 -0700131
132 # Assign return values.
Russ Cox98c98192009-09-15 13:51:33 -0700133 my $body = "";
134 my @ret = ("_", "_", "_");
Russ Cox602a4462009-06-01 22:14:57 -0700135 for(my $i=0; $i<@out; $i++) {
136 my $p = $out[$i];
137 my ($name, $type) = parseparam($p);
138 my $reg = "";
139 if($name eq "errno") {
140 $reg = "e1";
Russ Cox98c98192009-09-15 13:51:33 -0700141 $ret[2] = $reg;
Russ Cox602a4462009-06-01 22:14:57 -0700142 } else {
143 $reg = sprintf("r%d", $i);
Russ Cox98c98192009-09-15 13:51:33 -0700144 $ret[$i] = $reg;
Russ Cox602a4462009-06-01 22:14:57 -0700145 }
146 if($type eq "bool") {
147 $reg = "$reg != 0";
148 }
Russ Cox76c87d52009-06-16 17:17:02 -0700149 if($type eq "int64" && $_32bit ne "") {
150 # 64-bit number in r1:r0 or r0:r1.
151 if($i+2 > @out) {
152 print STDERR "$ARGV:$.: not enough registers for int64 return\n";
153 }
154 if($_32bit eq "big-endian") {
155 $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
156 } else {
157 $reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
158 }
Russ Cox98c98192009-09-15 13:51:33 -0700159 $ret[$i] = sprintf("r%d", $i);
160 $ret[$i+1] = sprintf("r%d", $i+1);
Russ Cox76c87d52009-06-16 17:17:02 -0700161 $i++; # loop will do another $i++
162 }
Russ Cox98c98192009-09-15 13:51:33 -0700163 $body .= "\t$name = $type($reg);\n";
Russ Cox602a4462009-06-01 22:14:57 -0700164 }
Russ Cox98c98192009-09-15 13:51:33 -0700165 if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
166 $text .= "\t$call;\n";
167 } else {
168 $text .= "\t$ret[0], $ret[1], $ret[2] := $call;\n";
169 }
170 $text .= $body;
Russ Cox602a4462009-06-01 22:14:57 -0700171
172 $text .= "\treturn;\n";
173 $text .= "}\n\n";
174}
175
176if($errors) {
177 exit 1;
178}
179
180print <<EOF;
181// $cmdline
182// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
183
184package syscall
185
Russ Cox9feee912009-08-24 11:03:23 -0700186import "unsafe"
Russ Cox602a4462009-06-01 22:14:57 -0700187
188$text
189
190EOF
191exit 0;