os: graduate SYS_* defs to nr enum

`type nr = enum i64 { READ, WRITE, OPEN, ... }`. syscall0..4 take
`num: nr` so the wrong-arg-order trap is now a compile error
(`syscall1(0i64, ...)` no longer typechecks — it has to be
`syscall1(nr.READ, ...)`).

Internal-only (callers outside os.ww never touched the constants),
so no external API change. ABI is unchanged: nr's storage is i64
and rt_syscall's RDI is unchanged.

The selfhost combined.ww files regenerate as a side effect of
`make wwstage`.
This commit is contained in:
2026-05-12 04:44:57 +09:00
parent b9443b1f33
commit fc49da44d8
7 changed files with 287 additions and 245 deletions

View File

@@ -2,11 +2,11 @@
// either in libwwrt.a (rt_syscall trampoline) or libc bindings, // either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked. // depending on how the program was linked.
@symbol("rt_syscall") fn syscall0(num: i64) i64; @symbol("rt_syscall") fn syscall0(num: nr) i64;
@symbol("rt_syscall") fn syscall1(num: i64, a: i64) i64; @symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64; @symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
@symbol("rt_syscall") fn syscall3(num: i64, a: i64, b: i64, c: i64) i64; @symbol("rt_syscall") fn syscall3(num: nr, a: i64, b: i64, c: i64) i64;
@symbol("rt_syscall") fn syscall4(num: i64, a: i64, b: i64, c: i64, d: i64) i64; @symbol("rt_syscall") fn syscall4(num: nr, a: i64, b: i64, c: i64, d: i64) i64;
@symbol("rt_alloc") fn alloc(n: u64) *void; @symbol("rt_alloc") fn alloc(n: u64) *void;
@symbol("rt_free") fn free(p: *void, n: u64) void; @symbol("rt_free") fn free(p: *void, n: u64) void;
@@ -18,21 +18,27 @@ export fn assert(cond: bool, msg: str) void = {
if (!cond) { abort(msg); }; if (!cond) { abort(msg); };
}; };
def SYS_READ: i64 = 0; // Linux amd64 syscall numbers. Internal to this module — passed as
def SYS_WRITE: i64 = 1; // the first arg of syscall0..4 via libwwrt's rt_syscall trampoline.
def SYS_OPEN: i64 = 2; // `nr` is the type so the call sites can't accidentally pass an
def SYS_CLOSE: i64 = 3; // arbitrary i64 (`syscall1(0i64, ...)` no longer typechecks).
def SYS_LSEEK: i64 = 8; type nr = enum i64 {
def SYS_ACCESS: i64 = 21; READ = 0,
def SYS_DUP2: i64 = 33; WRITE = 1,
def SYS_GETPID: i64 = 39; OPEN = 2,
def SYS_FORK: i64 = 57; CLOSE = 3,
def SYS_EXECVE: i64 = 59; LSEEK = 8,
def SYS_EXIT: i64 = 60; ACCESS = 21,
def SYS_WAIT4: i64 = 61; DUP2 = 33,
def SYS_UNLINK: i64 = 87; GETPID = 39,
def SYS_GETCWD: i64 = 79; FORK = 57,
def SYS_GETDENTS64: i64 = 217; EXECVE = 59,
EXIT = 60,
WAIT4 = 61,
UNLINK = 87,
GETCWD = 79,
GETDENTS64 = 217,
};
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them // open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say // `fs::flag::RDONLY` etc; we use the same leaf names so callers say
@@ -53,22 +59,22 @@ export type whence = enum i32 {
}; };
export fn exit(code: i32) void = { export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64); syscall1(nr.EXIT, code: i64);
}; };
// Raw, non-fallible primitives. These return Linux's int conventions // Raw, non-fallible primitives. These return Linux's int conventions
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a // (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
// Hare-style fallible API use the wrappers below. // Hare-style fallible API use the wrappers below.
export fn write(fd: i32, buf: *u8, n: u64) i64 = { export fn write(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_WRITE, fd: i64, buf: i64, n: i64); return syscall3(nr.WRITE, fd: i64, buf: i64, n: i64);
}; };
export fn read(fd: i32, buf: *u8, n: u64) i64 = { export fn read(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_READ, fd: i64, buf: i64, n: i64); return syscall3(nr.READ, fd: i64, buf: i64, n: i64);
}; };
export fn close(fd: i32) i32 = { export fn close(fd: i32) i32 = {
return syscall1(SYS_CLOSE, fd: i64): i32; return syscall1(nr.CLOSE, fd: i64): i32;
}; };
// dup2(2): make `newfd` refer to the same description as `oldfd`, // dup2(2): make `newfd` refer to the same description as `oldfd`,
@@ -76,7 +82,7 @@ export fn close(fd: i32) i32 = {
// negative errno. Used by w6c_ww to redirect stdout into an output // negative errno. Used by w6c_ww to redirect stdout into an output
// file without changing the cgen emit path. // file without changing the cgen emit path.
export fn dup2(oldfd: i32, newfd: i32) i32 = { export fn dup2(oldfd: i32, newfd: i32) i32 = {
return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; return syscall2(nr.DUP2, oldfd: i64, newfd: i64): i32;
}; };
// Fallible wrappers. The error variant is `oserror` (an i64 carrying // Fallible wrappers. The error variant is `oserror` (an i64 carrying
@@ -99,7 +105,7 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// arena-copied paths usually are by construction). Returns -errno on // arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`. // failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: flag, mode: i32) i32 = { export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32; return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
}; };
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = { export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
@@ -112,7 +118,7 @@ export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
// a negative errno. We use this for fstat-free file-size discovery // a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back). // (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, w: whence) i64 = { export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64); return syscall3(nr.LSEEK, fd: i64, off, (w as i32): i64);
}; };
// oserror — the underlying errno from a failed syscall, as a // oserror — the underlying errno from a failed syscall, as a
@@ -163,34 +169,34 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// access(2): returns 0 if the file is reachable, negative errno // access(2): returns 0 if the file is reachable, negative errno
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0). // otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
export fn access(path: *u8, mode: i32) i32 = { export fn access(path: *u8, mode: i32) i32 = {
return syscall2(SYS_ACCESS, path: i64, mode: i64): i32; return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
}; };
// remove — unlink(2). Hare name; the underlying syscall is unlink(2). // remove — unlink(2). Hare name; the underlying syscall is unlink(2).
export fn remove(path: *u8) i32 = { export fn remove(path: *u8) i32 = {
return syscall1(SYS_UNLINK, path: i64): i32; return syscall1(nr.UNLINK, path: i64): i32;
}; };
// getpid(2). Used by the driver to mint unique scratch paths. // getpid(2). Used by the driver to mint unique scratch paths.
export fn getpid() i32 = { export fn getpid() i32 = {
return syscall0(SYS_GETPID): i32; return syscall0(nr.GETPID): i32;
}; };
// fork(2): 0 in the child, child pid in the parent, negative errno // fork(2): 0 in the child, child pid in the parent, negative errno
// on failure. // on failure.
export fn fork() i32 = { export fn fork() i32 = {
return syscall0(SYS_FORK): i32; return syscall0(nr.FORK): i32;
}; };
// execve(2): on success, does not return. // execve(2): on success, does not return.
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = { export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
return syscall3(SYS_EXECVE, path: i64, argv: i64, envp: i64): i32; return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
}; };
// wait4(2): wait for `pid` (or any child if -1), store status in // wait4(2): wait for `pid` (or any child if -1), store status in
// `*status`, return the pid that ended (or negative errno). // `*status`, return the pid that ended (or negative errno).
export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = { export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
return syscall4(SYS_WAIT4, pid: i64, status: i64, return syscall4(nr.WAIT4, pid: i64, status: i64,
options: i64, rusage: i64): i32; options: i64, rusage: i64): i32;
}; };
@@ -199,7 +205,7 @@ export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
// negative errno. The driver uses it to expand `.` to the cwd's // negative errno. The driver uses it to expand `.` to the cwd's
// basename for `ww build` / `ww test`. // basename for `ww build` / `ww test`.
export fn getcwd(buf: *u8, n: u64) i64 = { export fn getcwd(buf: *u8, n: u64) i64 = {
return syscall2(SYS_GETCWD, buf: i64, n: i64); return syscall2(nr.GETCWD, buf: i64, n: i64);
}; };
// getdents64(2) — Linux directory enumeration. The fd must be opened // getdents64(2) — Linux directory enumeration. The fd must be opened
@@ -217,5 +223,5 @@ export fn getcwd(buf: *u8, n: u64) i64 = {
// Returns bytes written into `buf` (advance by d_reclen to walk), // Returns bytes written into `buf` (advance by d_reclen to walk),
// 0 at end-of-directory, or a negative errno. // 0 at end-of-directory, or a negative errno.
export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); return syscall3(nr.GETDENTS64, fd: i64, buf: i64, n: i64);
}; };

View File

@@ -3,11 +3,11 @@
// either in libwwrt.a (rt_syscall trampoline) or libc bindings, // either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked. // depending on how the program was linked.
@symbol("rt_syscall") fn syscall0(num: i64) i64; @symbol("rt_syscall") fn syscall0(num: nr) i64;
@symbol("rt_syscall") fn syscall1(num: i64, a: i64) i64; @symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64; @symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
@symbol("rt_syscall") fn syscall3(num: i64, a: i64, b: i64, c: i64) i64; @symbol("rt_syscall") fn syscall3(num: nr, a: i64, b: i64, c: i64) i64;
@symbol("rt_syscall") fn syscall4(num: i64, a: i64, b: i64, c: i64, d: i64) i64; @symbol("rt_syscall") fn syscall4(num: nr, a: i64, b: i64, c: i64, d: i64) i64;
@symbol("rt_alloc") fn alloc(n: u64) *void; @symbol("rt_alloc") fn alloc(n: u64) *void;
@symbol("rt_free") fn free(p: *void, n: u64) void; @symbol("rt_free") fn free(p: *void, n: u64) void;
@@ -19,21 +19,27 @@ export fn assert(cond: bool, msg: str) void = {
if (!cond) { abort(msg); }; if (!cond) { abort(msg); };
}; };
def SYS_READ: i64 = 0; // Linux amd64 syscall numbers. Internal to this module — passed as
def SYS_WRITE: i64 = 1; // the first arg of syscall0..4 via libwwrt's rt_syscall trampoline.
def SYS_OPEN: i64 = 2; // `nr` is the type so the call sites can't accidentally pass an
def SYS_CLOSE: i64 = 3; // arbitrary i64 (`syscall1(0i64, ...)` no longer typechecks).
def SYS_LSEEK: i64 = 8; type nr = enum i64 {
def SYS_ACCESS: i64 = 21; READ = 0,
def SYS_DUP2: i64 = 33; WRITE = 1,
def SYS_GETPID: i64 = 39; OPEN = 2,
def SYS_FORK: i64 = 57; CLOSE = 3,
def SYS_EXECVE: i64 = 59; LSEEK = 8,
def SYS_EXIT: i64 = 60; ACCESS = 21,
def SYS_WAIT4: i64 = 61; DUP2 = 33,
def SYS_UNLINK: i64 = 87; GETPID = 39,
def SYS_GETCWD: i64 = 79; FORK = 57,
def SYS_GETDENTS64: i64 = 217; EXECVE = 59,
EXIT = 60,
WAIT4 = 61,
UNLINK = 87,
GETCWD = 79,
GETDENTS64 = 217,
};
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them // open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say // `fs::flag::RDONLY` etc; we use the same leaf names so callers say
@@ -54,22 +60,22 @@ export type whence = enum i32 {
}; };
export fn exit(code: i32) void = { export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64); syscall1(nr.EXIT, code: i64);
}; };
// Raw, non-fallible primitives. These return Linux's int conventions // Raw, non-fallible primitives. These return Linux's int conventions
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a // (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
// Hare-style fallible API use the wrappers below. // Hare-style fallible API use the wrappers below.
export fn write(fd: i32, buf: *u8, n: u64) i64 = { export fn write(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_WRITE, fd: i64, buf: i64, n: i64); return syscall3(nr.WRITE, fd: i64, buf: i64, n: i64);
}; };
export fn read(fd: i32, buf: *u8, n: u64) i64 = { export fn read(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_READ, fd: i64, buf: i64, n: i64); return syscall3(nr.READ, fd: i64, buf: i64, n: i64);
}; };
export fn close(fd: i32) i32 = { export fn close(fd: i32) i32 = {
return syscall1(SYS_CLOSE, fd: i64): i32; return syscall1(nr.CLOSE, fd: i64): i32;
}; };
// dup2(2): make `newfd` refer to the same description as `oldfd`, // dup2(2): make `newfd` refer to the same description as `oldfd`,
@@ -77,7 +83,7 @@ export fn close(fd: i32) i32 = {
// negative errno. Used by w6c_ww to redirect stdout into an output // negative errno. Used by w6c_ww to redirect stdout into an output
// file without changing the cgen emit path. // file without changing the cgen emit path.
export fn dup2(oldfd: i32, newfd: i32) i32 = { export fn dup2(oldfd: i32, newfd: i32) i32 = {
return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; return syscall2(nr.DUP2, oldfd: i64, newfd: i64): i32;
}; };
// Fallible wrappers. The error variant is `oserror` (an i64 carrying // Fallible wrappers. The error variant is `oserror` (an i64 carrying
@@ -100,7 +106,7 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// arena-copied paths usually are by construction). Returns -errno on // arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`. // failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: flag, mode: i32) i32 = { export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32; return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
}; };
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = { export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
@@ -113,7 +119,7 @@ export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
// a negative errno. We use this for fstat-free file-size discovery // a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back). // (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, w: whence) i64 = { export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64); return syscall3(nr.LSEEK, fd: i64, off, (w as i32): i64);
}; };
// oserror — the underlying errno from a failed syscall, as a // oserror — the underlying errno from a failed syscall, as a
@@ -164,34 +170,34 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// access(2): returns 0 if the file is reachable, negative errno // access(2): returns 0 if the file is reachable, negative errno
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0). // otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
export fn access(path: *u8, mode: i32) i32 = { export fn access(path: *u8, mode: i32) i32 = {
return syscall2(SYS_ACCESS, path: i64, mode: i64): i32; return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
}; };
// remove — unlink(2). Hare name; the underlying syscall is unlink(2). // remove — unlink(2). Hare name; the underlying syscall is unlink(2).
export fn remove(path: *u8) i32 = { export fn remove(path: *u8) i32 = {
return syscall1(SYS_UNLINK, path: i64): i32; return syscall1(nr.UNLINK, path: i64): i32;
}; };
// getpid(2). Used by the driver to mint unique scratch paths. // getpid(2). Used by the driver to mint unique scratch paths.
export fn getpid() i32 = { export fn getpid() i32 = {
return syscall0(SYS_GETPID): i32; return syscall0(nr.GETPID): i32;
}; };
// fork(2): 0 in the child, child pid in the parent, negative errno // fork(2): 0 in the child, child pid in the parent, negative errno
// on failure. // on failure.
export fn fork() i32 = { export fn fork() i32 = {
return syscall0(SYS_FORK): i32; return syscall0(nr.FORK): i32;
}; };
// execve(2): on success, does not return. // execve(2): on success, does not return.
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = { export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
return syscall3(SYS_EXECVE, path: i64, argv: i64, envp: i64): i32; return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
}; };
// wait4(2): wait for `pid` (or any child if -1), store status in // wait4(2): wait for `pid` (or any child if -1), store status in
// `*status`, return the pid that ended (or negative errno). // `*status`, return the pid that ended (or negative errno).
export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = { export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
return syscall4(SYS_WAIT4, pid: i64, status: i64, return syscall4(nr.WAIT4, pid: i64, status: i64,
options: i64, rusage: i64): i32; options: i64, rusage: i64): i32;
}; };
@@ -200,7 +206,7 @@ export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
// negative errno. The driver uses it to expand `.` to the cwd's // negative errno. The driver uses it to expand `.` to the cwd's
// basename for `ww build` / `ww test`. // basename for `ww build` / `ww test`.
export fn getcwd(buf: *u8, n: u64) i64 = { export fn getcwd(buf: *u8, n: u64) i64 = {
return syscall2(SYS_GETCWD, buf: i64, n: i64); return syscall2(nr.GETCWD, buf: i64, n: i64);
}; };
// getdents64(2) — Linux directory enumeration. The fd must be opened // getdents64(2) — Linux directory enumeration. The fd must be opened
@@ -218,7 +224,7 @@ export fn getcwd(buf: *u8, n: u64) i64 = {
// Returns bytes written into `buf` (advance by d_reclen to walk), // Returns bytes written into `buf` (advance by d_reclen to walk),
// 0 at end-of-directory, or a negative errno. // 0 at end-of-directory, or a negative errno.
export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); return syscall3(nr.GETDENTS64, fd: i64, buf: i64, n: i64);
}; };
// MODULE: wcc // MODULE: wcc

View File

@@ -3,11 +3,11 @@
// either in libwwrt.a (rt_syscall trampoline) or libc bindings, // either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked. // depending on how the program was linked.
@symbol("rt_syscall") fn syscall0(num: i64) i64; @symbol("rt_syscall") fn syscall0(num: nr) i64;
@symbol("rt_syscall") fn syscall1(num: i64, a: i64) i64; @symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64; @symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
@symbol("rt_syscall") fn syscall3(num: i64, a: i64, b: i64, c: i64) i64; @symbol("rt_syscall") fn syscall3(num: nr, a: i64, b: i64, c: i64) i64;
@symbol("rt_syscall") fn syscall4(num: i64, a: i64, b: i64, c: i64, d: i64) i64; @symbol("rt_syscall") fn syscall4(num: nr, a: i64, b: i64, c: i64, d: i64) i64;
@symbol("rt_alloc") fn alloc(n: u64) *void; @symbol("rt_alloc") fn alloc(n: u64) *void;
@symbol("rt_free") fn free(p: *void, n: u64) void; @symbol("rt_free") fn free(p: *void, n: u64) void;
@@ -19,21 +19,27 @@ export fn assert(cond: bool, msg: str) void = {
if (!cond) { abort(msg); }; if (!cond) { abort(msg); };
}; };
def SYS_READ: i64 = 0; // Linux amd64 syscall numbers. Internal to this module — passed as
def SYS_WRITE: i64 = 1; // the first arg of syscall0..4 via libwwrt's rt_syscall trampoline.
def SYS_OPEN: i64 = 2; // `nr` is the type so the call sites can't accidentally pass an
def SYS_CLOSE: i64 = 3; // arbitrary i64 (`syscall1(0i64, ...)` no longer typechecks).
def SYS_LSEEK: i64 = 8; type nr = enum i64 {
def SYS_ACCESS: i64 = 21; READ = 0,
def SYS_DUP2: i64 = 33; WRITE = 1,
def SYS_GETPID: i64 = 39; OPEN = 2,
def SYS_FORK: i64 = 57; CLOSE = 3,
def SYS_EXECVE: i64 = 59; LSEEK = 8,
def SYS_EXIT: i64 = 60; ACCESS = 21,
def SYS_WAIT4: i64 = 61; DUP2 = 33,
def SYS_UNLINK: i64 = 87; GETPID = 39,
def SYS_GETCWD: i64 = 79; FORK = 57,
def SYS_GETDENTS64: i64 = 217; EXECVE = 59,
EXIT = 60,
WAIT4 = 61,
UNLINK = 87,
GETCWD = 79,
GETDENTS64 = 217,
};
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them // open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say // `fs::flag::RDONLY` etc; we use the same leaf names so callers say
@@ -54,22 +60,22 @@ export type whence = enum i32 {
}; };
export fn exit(code: i32) void = { export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64); syscall1(nr.EXIT, code: i64);
}; };
// Raw, non-fallible primitives. These return Linux's int conventions // Raw, non-fallible primitives. These return Linux's int conventions
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a // (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
// Hare-style fallible API use the wrappers below. // Hare-style fallible API use the wrappers below.
export fn write(fd: i32, buf: *u8, n: u64) i64 = { export fn write(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_WRITE, fd: i64, buf: i64, n: i64); return syscall3(nr.WRITE, fd: i64, buf: i64, n: i64);
}; };
export fn read(fd: i32, buf: *u8, n: u64) i64 = { export fn read(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_READ, fd: i64, buf: i64, n: i64); return syscall3(nr.READ, fd: i64, buf: i64, n: i64);
}; };
export fn close(fd: i32) i32 = { export fn close(fd: i32) i32 = {
return syscall1(SYS_CLOSE, fd: i64): i32; return syscall1(nr.CLOSE, fd: i64): i32;
}; };
// dup2(2): make `newfd` refer to the same description as `oldfd`, // dup2(2): make `newfd` refer to the same description as `oldfd`,
@@ -77,7 +83,7 @@ export fn close(fd: i32) i32 = {
// negative errno. Used by w6c_ww to redirect stdout into an output // negative errno. Used by w6c_ww to redirect stdout into an output
// file without changing the cgen emit path. // file without changing the cgen emit path.
export fn dup2(oldfd: i32, newfd: i32) i32 = { export fn dup2(oldfd: i32, newfd: i32) i32 = {
return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; return syscall2(nr.DUP2, oldfd: i64, newfd: i64): i32;
}; };
// Fallible wrappers. The error variant is `oserror` (an i64 carrying // Fallible wrappers. The error variant is `oserror` (an i64 carrying
@@ -100,7 +106,7 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// arena-copied paths usually are by construction). Returns -errno on // arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`. // failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: flag, mode: i32) i32 = { export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32; return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
}; };
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = { export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
@@ -113,7 +119,7 @@ export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
// a negative errno. We use this for fstat-free file-size discovery // a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back). // (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, w: whence) i64 = { export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64); return syscall3(nr.LSEEK, fd: i64, off, (w as i32): i64);
}; };
// oserror — the underlying errno from a failed syscall, as a // oserror — the underlying errno from a failed syscall, as a
@@ -164,34 +170,34 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// access(2): returns 0 if the file is reachable, negative errno // access(2): returns 0 if the file is reachable, negative errno
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0). // otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
export fn access(path: *u8, mode: i32) i32 = { export fn access(path: *u8, mode: i32) i32 = {
return syscall2(SYS_ACCESS, path: i64, mode: i64): i32; return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
}; };
// remove — unlink(2). Hare name; the underlying syscall is unlink(2). // remove — unlink(2). Hare name; the underlying syscall is unlink(2).
export fn remove(path: *u8) i32 = { export fn remove(path: *u8) i32 = {
return syscall1(SYS_UNLINK, path: i64): i32; return syscall1(nr.UNLINK, path: i64): i32;
}; };
// getpid(2). Used by the driver to mint unique scratch paths. // getpid(2). Used by the driver to mint unique scratch paths.
export fn getpid() i32 = { export fn getpid() i32 = {
return syscall0(SYS_GETPID): i32; return syscall0(nr.GETPID): i32;
}; };
// fork(2): 0 in the child, child pid in the parent, negative errno // fork(2): 0 in the child, child pid in the parent, negative errno
// on failure. // on failure.
export fn fork() i32 = { export fn fork() i32 = {
return syscall0(SYS_FORK): i32; return syscall0(nr.FORK): i32;
}; };
// execve(2): on success, does not return. // execve(2): on success, does not return.
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = { export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
return syscall3(SYS_EXECVE, path: i64, argv: i64, envp: i64): i32; return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
}; };
// wait4(2): wait for `pid` (or any child if -1), store status in // wait4(2): wait for `pid` (or any child if -1), store status in
// `*status`, return the pid that ended (or negative errno). // `*status`, return the pid that ended (or negative errno).
export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = { export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
return syscall4(SYS_WAIT4, pid: i64, status: i64, return syscall4(nr.WAIT4, pid: i64, status: i64,
options: i64, rusage: i64): i32; options: i64, rusage: i64): i32;
}; };
@@ -200,7 +206,7 @@ export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
// negative errno. The driver uses it to expand `.` to the cwd's // negative errno. The driver uses it to expand `.` to the cwd's
// basename for `ww build` / `ww test`. // basename for `ww build` / `ww test`.
export fn getcwd(buf: *u8, n: u64) i64 = { export fn getcwd(buf: *u8, n: u64) i64 = {
return syscall2(SYS_GETCWD, buf: i64, n: i64); return syscall2(nr.GETCWD, buf: i64, n: i64);
}; };
// getdents64(2) — Linux directory enumeration. The fd must be opened // getdents64(2) — Linux directory enumeration. The fd must be opened
@@ -218,7 +224,7 @@ export fn getcwd(buf: *u8, n: u64) i64 = {
// Returns bytes written into `buf` (advance by d_reclen to walk), // Returns bytes written into `buf` (advance by d_reclen to walk),
// 0 at end-of-directory, or a negative errno. // 0 at end-of-directory, or a negative errno.
export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); return syscall3(nr.GETDENTS64, fd: i64, buf: i64, n: i64);
}; };
// MODULE: wcc // MODULE: wcc

View File

@@ -3,11 +3,11 @@
// either in libwwrt.a (rt_syscall trampoline) or libc bindings, // either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked. // depending on how the program was linked.
@symbol("rt_syscall") fn syscall0(num: i64) i64; @symbol("rt_syscall") fn syscall0(num: nr) i64;
@symbol("rt_syscall") fn syscall1(num: i64, a: i64) i64; @symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64; @symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
@symbol("rt_syscall") fn syscall3(num: i64, a: i64, b: i64, c: i64) i64; @symbol("rt_syscall") fn syscall3(num: nr, a: i64, b: i64, c: i64) i64;
@symbol("rt_syscall") fn syscall4(num: i64, a: i64, b: i64, c: i64, d: i64) i64; @symbol("rt_syscall") fn syscall4(num: nr, a: i64, b: i64, c: i64, d: i64) i64;
@symbol("rt_alloc") fn alloc(n: u64) *void; @symbol("rt_alloc") fn alloc(n: u64) *void;
@symbol("rt_free") fn free(p: *void, n: u64) void; @symbol("rt_free") fn free(p: *void, n: u64) void;
@@ -19,21 +19,27 @@ export fn assert(cond: bool, msg: str) void = {
if (!cond) { abort(msg); }; if (!cond) { abort(msg); };
}; };
def SYS_READ: i64 = 0; // Linux amd64 syscall numbers. Internal to this module — passed as
def SYS_WRITE: i64 = 1; // the first arg of syscall0..4 via libwwrt's rt_syscall trampoline.
def SYS_OPEN: i64 = 2; // `nr` is the type so the call sites can't accidentally pass an
def SYS_CLOSE: i64 = 3; // arbitrary i64 (`syscall1(0i64, ...)` no longer typechecks).
def SYS_LSEEK: i64 = 8; type nr = enum i64 {
def SYS_ACCESS: i64 = 21; READ = 0,
def SYS_DUP2: i64 = 33; WRITE = 1,
def SYS_GETPID: i64 = 39; OPEN = 2,
def SYS_FORK: i64 = 57; CLOSE = 3,
def SYS_EXECVE: i64 = 59; LSEEK = 8,
def SYS_EXIT: i64 = 60; ACCESS = 21,
def SYS_WAIT4: i64 = 61; DUP2 = 33,
def SYS_UNLINK: i64 = 87; GETPID = 39,
def SYS_GETCWD: i64 = 79; FORK = 57,
def SYS_GETDENTS64: i64 = 217; EXECVE = 59,
EXIT = 60,
WAIT4 = 61,
UNLINK = 87,
GETCWD = 79,
GETDENTS64 = 217,
};
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them // open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say // `fs::flag::RDONLY` etc; we use the same leaf names so callers say
@@ -54,22 +60,22 @@ export type whence = enum i32 {
}; };
export fn exit(code: i32) void = { export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64); syscall1(nr.EXIT, code: i64);
}; };
// Raw, non-fallible primitives. These return Linux's int conventions // Raw, non-fallible primitives. These return Linux's int conventions
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a // (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
// Hare-style fallible API use the wrappers below. // Hare-style fallible API use the wrappers below.
export fn write(fd: i32, buf: *u8, n: u64) i64 = { export fn write(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_WRITE, fd: i64, buf: i64, n: i64); return syscall3(nr.WRITE, fd: i64, buf: i64, n: i64);
}; };
export fn read(fd: i32, buf: *u8, n: u64) i64 = { export fn read(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_READ, fd: i64, buf: i64, n: i64); return syscall3(nr.READ, fd: i64, buf: i64, n: i64);
}; };
export fn close(fd: i32) i32 = { export fn close(fd: i32) i32 = {
return syscall1(SYS_CLOSE, fd: i64): i32; return syscall1(nr.CLOSE, fd: i64): i32;
}; };
// dup2(2): make `newfd` refer to the same description as `oldfd`, // dup2(2): make `newfd` refer to the same description as `oldfd`,
@@ -77,7 +83,7 @@ export fn close(fd: i32) i32 = {
// negative errno. Used by w6c_ww to redirect stdout into an output // negative errno. Used by w6c_ww to redirect stdout into an output
// file without changing the cgen emit path. // file without changing the cgen emit path.
export fn dup2(oldfd: i32, newfd: i32) i32 = { export fn dup2(oldfd: i32, newfd: i32) i32 = {
return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; return syscall2(nr.DUP2, oldfd: i64, newfd: i64): i32;
}; };
// Fallible wrappers. The error variant is `oserror` (an i64 carrying // Fallible wrappers. The error variant is `oserror` (an i64 carrying
@@ -100,7 +106,7 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// arena-copied paths usually are by construction). Returns -errno on // arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`. // failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: flag, mode: i32) i32 = { export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32; return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
}; };
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = { export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
@@ -113,7 +119,7 @@ export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
// a negative errno. We use this for fstat-free file-size discovery // a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back). // (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, w: whence) i64 = { export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64); return syscall3(nr.LSEEK, fd: i64, off, (w as i32): i64);
}; };
// oserror — the underlying errno from a failed syscall, as a // oserror — the underlying errno from a failed syscall, as a
@@ -164,34 +170,34 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// access(2): returns 0 if the file is reachable, negative errno // access(2): returns 0 if the file is reachable, negative errno
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0). // otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
export fn access(path: *u8, mode: i32) i32 = { export fn access(path: *u8, mode: i32) i32 = {
return syscall2(SYS_ACCESS, path: i64, mode: i64): i32; return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
}; };
// remove — unlink(2). Hare name; the underlying syscall is unlink(2). // remove — unlink(2). Hare name; the underlying syscall is unlink(2).
export fn remove(path: *u8) i32 = { export fn remove(path: *u8) i32 = {
return syscall1(SYS_UNLINK, path: i64): i32; return syscall1(nr.UNLINK, path: i64): i32;
}; };
// getpid(2). Used by the driver to mint unique scratch paths. // getpid(2). Used by the driver to mint unique scratch paths.
export fn getpid() i32 = { export fn getpid() i32 = {
return syscall0(SYS_GETPID): i32; return syscall0(nr.GETPID): i32;
}; };
// fork(2): 0 in the child, child pid in the parent, negative errno // fork(2): 0 in the child, child pid in the parent, negative errno
// on failure. // on failure.
export fn fork() i32 = { export fn fork() i32 = {
return syscall0(SYS_FORK): i32; return syscall0(nr.FORK): i32;
}; };
// execve(2): on success, does not return. // execve(2): on success, does not return.
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = { export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
return syscall3(SYS_EXECVE, path: i64, argv: i64, envp: i64): i32; return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
}; };
// wait4(2): wait for `pid` (or any child if -1), store status in // wait4(2): wait for `pid` (or any child if -1), store status in
// `*status`, return the pid that ended (or negative errno). // `*status`, return the pid that ended (or negative errno).
export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = { export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
return syscall4(SYS_WAIT4, pid: i64, status: i64, return syscall4(nr.WAIT4, pid: i64, status: i64,
options: i64, rusage: i64): i32; options: i64, rusage: i64): i32;
}; };
@@ -200,7 +206,7 @@ export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
// negative errno. The driver uses it to expand `.` to the cwd's // negative errno. The driver uses it to expand `.` to the cwd's
// basename for `ww build` / `ww test`. // basename for `ww build` / `ww test`.
export fn getcwd(buf: *u8, n: u64) i64 = { export fn getcwd(buf: *u8, n: u64) i64 = {
return syscall2(SYS_GETCWD, buf: i64, n: i64); return syscall2(nr.GETCWD, buf: i64, n: i64);
}; };
// getdents64(2) — Linux directory enumeration. The fd must be opened // getdents64(2) — Linux directory enumeration. The fd must be opened
@@ -218,7 +224,7 @@ export fn getcwd(buf: *u8, n: u64) i64 = {
// Returns bytes written into `buf` (advance by d_reclen to walk), // Returns bytes written into `buf` (advance by d_reclen to walk),
// 0 at end-of-directory, or a negative errno. // 0 at end-of-directory, or a negative errno.
export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); return syscall3(nr.GETDENTS64, fd: i64, buf: i64, n: i64);
}; };
// MODULE: wcc // MODULE: wcc

View File

@@ -3,11 +3,11 @@
// either in libwwrt.a (rt_syscall trampoline) or libc bindings, // either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked. // depending on how the program was linked.
@symbol("rt_syscall") fn syscall0(num: i64) i64; @symbol("rt_syscall") fn syscall0(num: nr) i64;
@symbol("rt_syscall") fn syscall1(num: i64, a: i64) i64; @symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64; @symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
@symbol("rt_syscall") fn syscall3(num: i64, a: i64, b: i64, c: i64) i64; @symbol("rt_syscall") fn syscall3(num: nr, a: i64, b: i64, c: i64) i64;
@symbol("rt_syscall") fn syscall4(num: i64, a: i64, b: i64, c: i64, d: i64) i64; @symbol("rt_syscall") fn syscall4(num: nr, a: i64, b: i64, c: i64, d: i64) i64;
@symbol("rt_alloc") fn alloc(n: u64) *void; @symbol("rt_alloc") fn alloc(n: u64) *void;
@symbol("rt_free") fn free(p: *void, n: u64) void; @symbol("rt_free") fn free(p: *void, n: u64) void;
@@ -19,21 +19,27 @@ export fn assert(cond: bool, msg: str) void = {
if (!cond) { abort(msg); }; if (!cond) { abort(msg); };
}; };
def SYS_READ: i64 = 0; // Linux amd64 syscall numbers. Internal to this module — passed as
def SYS_WRITE: i64 = 1; // the first arg of syscall0..4 via libwwrt's rt_syscall trampoline.
def SYS_OPEN: i64 = 2; // `nr` is the type so the call sites can't accidentally pass an
def SYS_CLOSE: i64 = 3; // arbitrary i64 (`syscall1(0i64, ...)` no longer typechecks).
def SYS_LSEEK: i64 = 8; type nr = enum i64 {
def SYS_ACCESS: i64 = 21; READ = 0,
def SYS_DUP2: i64 = 33; WRITE = 1,
def SYS_GETPID: i64 = 39; OPEN = 2,
def SYS_FORK: i64 = 57; CLOSE = 3,
def SYS_EXECVE: i64 = 59; LSEEK = 8,
def SYS_EXIT: i64 = 60; ACCESS = 21,
def SYS_WAIT4: i64 = 61; DUP2 = 33,
def SYS_UNLINK: i64 = 87; GETPID = 39,
def SYS_GETCWD: i64 = 79; FORK = 57,
def SYS_GETDENTS64: i64 = 217; EXECVE = 59,
EXIT = 60,
WAIT4 = 61,
UNLINK = 87,
GETCWD = 79,
GETDENTS64 = 217,
};
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them // open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say // `fs::flag::RDONLY` etc; we use the same leaf names so callers say
@@ -54,22 +60,22 @@ export type whence = enum i32 {
}; };
export fn exit(code: i32) void = { export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64); syscall1(nr.EXIT, code: i64);
}; };
// Raw, non-fallible primitives. These return Linux's int conventions // Raw, non-fallible primitives. These return Linux's int conventions
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a // (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
// Hare-style fallible API use the wrappers below. // Hare-style fallible API use the wrappers below.
export fn write(fd: i32, buf: *u8, n: u64) i64 = { export fn write(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_WRITE, fd: i64, buf: i64, n: i64); return syscall3(nr.WRITE, fd: i64, buf: i64, n: i64);
}; };
export fn read(fd: i32, buf: *u8, n: u64) i64 = { export fn read(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_READ, fd: i64, buf: i64, n: i64); return syscall3(nr.READ, fd: i64, buf: i64, n: i64);
}; };
export fn close(fd: i32) i32 = { export fn close(fd: i32) i32 = {
return syscall1(SYS_CLOSE, fd: i64): i32; return syscall1(nr.CLOSE, fd: i64): i32;
}; };
// dup2(2): make `newfd` refer to the same description as `oldfd`, // dup2(2): make `newfd` refer to the same description as `oldfd`,
@@ -77,7 +83,7 @@ export fn close(fd: i32) i32 = {
// negative errno. Used by w6c_ww to redirect stdout into an output // negative errno. Used by w6c_ww to redirect stdout into an output
// file without changing the cgen emit path. // file without changing the cgen emit path.
export fn dup2(oldfd: i32, newfd: i32) i32 = { export fn dup2(oldfd: i32, newfd: i32) i32 = {
return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; return syscall2(nr.DUP2, oldfd: i64, newfd: i64): i32;
}; };
// Fallible wrappers. The error variant is `oserror` (an i64 carrying // Fallible wrappers. The error variant is `oserror` (an i64 carrying
@@ -100,7 +106,7 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// arena-copied paths usually are by construction). Returns -errno on // arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`. // failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: flag, mode: i32) i32 = { export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32; return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
}; };
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = { export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
@@ -113,7 +119,7 @@ export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
// a negative errno. We use this for fstat-free file-size discovery // a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back). // (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, w: whence) i64 = { export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64); return syscall3(nr.LSEEK, fd: i64, off, (w as i32): i64);
}; };
// oserror — the underlying errno from a failed syscall, as a // oserror — the underlying errno from a failed syscall, as a
@@ -164,34 +170,34 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// access(2): returns 0 if the file is reachable, negative errno // access(2): returns 0 if the file is reachable, negative errno
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0). // otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
export fn access(path: *u8, mode: i32) i32 = { export fn access(path: *u8, mode: i32) i32 = {
return syscall2(SYS_ACCESS, path: i64, mode: i64): i32; return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
}; };
// remove — unlink(2). Hare name; the underlying syscall is unlink(2). // remove — unlink(2). Hare name; the underlying syscall is unlink(2).
export fn remove(path: *u8) i32 = { export fn remove(path: *u8) i32 = {
return syscall1(SYS_UNLINK, path: i64): i32; return syscall1(nr.UNLINK, path: i64): i32;
}; };
// getpid(2). Used by the driver to mint unique scratch paths. // getpid(2). Used by the driver to mint unique scratch paths.
export fn getpid() i32 = { export fn getpid() i32 = {
return syscall0(SYS_GETPID): i32; return syscall0(nr.GETPID): i32;
}; };
// fork(2): 0 in the child, child pid in the parent, negative errno // fork(2): 0 in the child, child pid in the parent, negative errno
// on failure. // on failure.
export fn fork() i32 = { export fn fork() i32 = {
return syscall0(SYS_FORK): i32; return syscall0(nr.FORK): i32;
}; };
// execve(2): on success, does not return. // execve(2): on success, does not return.
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = { export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
return syscall3(SYS_EXECVE, path: i64, argv: i64, envp: i64): i32; return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
}; };
// wait4(2): wait for `pid` (or any child if -1), store status in // wait4(2): wait for `pid` (or any child if -1), store status in
// `*status`, return the pid that ended (or negative errno). // `*status`, return the pid that ended (or negative errno).
export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = { export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
return syscall4(SYS_WAIT4, pid: i64, status: i64, return syscall4(nr.WAIT4, pid: i64, status: i64,
options: i64, rusage: i64): i32; options: i64, rusage: i64): i32;
}; };
@@ -200,7 +206,7 @@ export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
// negative errno. The driver uses it to expand `.` to the cwd's // negative errno. The driver uses it to expand `.` to the cwd's
// basename for `ww build` / `ww test`. // basename for `ww build` / `ww test`.
export fn getcwd(buf: *u8, n: u64) i64 = { export fn getcwd(buf: *u8, n: u64) i64 = {
return syscall2(SYS_GETCWD, buf: i64, n: i64); return syscall2(nr.GETCWD, buf: i64, n: i64);
}; };
// getdents64(2) — Linux directory enumeration. The fd must be opened // getdents64(2) — Linux directory enumeration. The fd must be opened
@@ -218,7 +224,7 @@ export fn getcwd(buf: *u8, n: u64) i64 = {
// Returns bytes written into `buf` (advance by d_reclen to walk), // Returns bytes written into `buf` (advance by d_reclen to walk),
// 0 at end-of-directory, or a negative errno. // 0 at end-of-directory, or a negative errno.
export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); return syscall3(nr.GETDENTS64, fd: i64, buf: i64, n: i64);
}; };
// MODULE: wcc // MODULE: wcc

View File

@@ -3,11 +3,11 @@
// either in libwwrt.a (rt_syscall trampoline) or libc bindings, // either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked. // depending on how the program was linked.
@symbol("rt_syscall") fn syscall0(num: i64) i64; @symbol("rt_syscall") fn syscall0(num: nr) i64;
@symbol("rt_syscall") fn syscall1(num: i64, a: i64) i64; @symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64; @symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
@symbol("rt_syscall") fn syscall3(num: i64, a: i64, b: i64, c: i64) i64; @symbol("rt_syscall") fn syscall3(num: nr, a: i64, b: i64, c: i64) i64;
@symbol("rt_syscall") fn syscall4(num: i64, a: i64, b: i64, c: i64, d: i64) i64; @symbol("rt_syscall") fn syscall4(num: nr, a: i64, b: i64, c: i64, d: i64) i64;
@symbol("rt_alloc") fn alloc(n: u64) *void; @symbol("rt_alloc") fn alloc(n: u64) *void;
@symbol("rt_free") fn free(p: *void, n: u64) void; @symbol("rt_free") fn free(p: *void, n: u64) void;
@@ -19,21 +19,27 @@ export fn assert(cond: bool, msg: str) void = {
if (!cond) { abort(msg); }; if (!cond) { abort(msg); };
}; };
def SYS_READ: i64 = 0; // Linux amd64 syscall numbers. Internal to this module — passed as
def SYS_WRITE: i64 = 1; // the first arg of syscall0..4 via libwwrt's rt_syscall trampoline.
def SYS_OPEN: i64 = 2; // `nr` is the type so the call sites can't accidentally pass an
def SYS_CLOSE: i64 = 3; // arbitrary i64 (`syscall1(0i64, ...)` no longer typechecks).
def SYS_LSEEK: i64 = 8; type nr = enum i64 {
def SYS_ACCESS: i64 = 21; READ = 0,
def SYS_DUP2: i64 = 33; WRITE = 1,
def SYS_GETPID: i64 = 39; OPEN = 2,
def SYS_FORK: i64 = 57; CLOSE = 3,
def SYS_EXECVE: i64 = 59; LSEEK = 8,
def SYS_EXIT: i64 = 60; ACCESS = 21,
def SYS_WAIT4: i64 = 61; DUP2 = 33,
def SYS_UNLINK: i64 = 87; GETPID = 39,
def SYS_GETCWD: i64 = 79; FORK = 57,
def SYS_GETDENTS64: i64 = 217; EXECVE = 59,
EXIT = 60,
WAIT4 = 61,
UNLINK = 87,
GETCWD = 79,
GETDENTS64 = 217,
};
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them // open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say // `fs::flag::RDONLY` etc; we use the same leaf names so callers say
@@ -54,22 +60,22 @@ export type whence = enum i32 {
}; };
export fn exit(code: i32) void = { export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64); syscall1(nr.EXIT, code: i64);
}; };
// Raw, non-fallible primitives. These return Linux's int conventions // Raw, non-fallible primitives. These return Linux's int conventions
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a // (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
// Hare-style fallible API use the wrappers below. // Hare-style fallible API use the wrappers below.
export fn write(fd: i32, buf: *u8, n: u64) i64 = { export fn write(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_WRITE, fd: i64, buf: i64, n: i64); return syscall3(nr.WRITE, fd: i64, buf: i64, n: i64);
}; };
export fn read(fd: i32, buf: *u8, n: u64) i64 = { export fn read(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_READ, fd: i64, buf: i64, n: i64); return syscall3(nr.READ, fd: i64, buf: i64, n: i64);
}; };
export fn close(fd: i32) i32 = { export fn close(fd: i32) i32 = {
return syscall1(SYS_CLOSE, fd: i64): i32; return syscall1(nr.CLOSE, fd: i64): i32;
}; };
// dup2(2): make `newfd` refer to the same description as `oldfd`, // dup2(2): make `newfd` refer to the same description as `oldfd`,
@@ -77,7 +83,7 @@ export fn close(fd: i32) i32 = {
// negative errno. Used by w6c_ww to redirect stdout into an output // negative errno. Used by w6c_ww to redirect stdout into an output
// file without changing the cgen emit path. // file without changing the cgen emit path.
export fn dup2(oldfd: i32, newfd: i32) i32 = { export fn dup2(oldfd: i32, newfd: i32) i32 = {
return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; return syscall2(nr.DUP2, oldfd: i64, newfd: i64): i32;
}; };
// Fallible wrappers. The error variant is `oserror` (an i64 carrying // Fallible wrappers. The error variant is `oserror` (an i64 carrying
@@ -100,7 +106,7 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// arena-copied paths usually are by construction). Returns -errno on // arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`. // failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: flag, mode: i32) i32 = { export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32; return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
}; };
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = { export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
@@ -113,7 +119,7 @@ export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
// a negative errno. We use this for fstat-free file-size discovery // a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back). // (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, w: whence) i64 = { export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64); return syscall3(nr.LSEEK, fd: i64, off, (w as i32): i64);
}; };
// oserror — the underlying errno from a failed syscall, as a // oserror — the underlying errno from a failed syscall, as a
@@ -164,34 +170,34 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// access(2): returns 0 if the file is reachable, negative errno // access(2): returns 0 if the file is reachable, negative errno
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0). // otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
export fn access(path: *u8, mode: i32) i32 = { export fn access(path: *u8, mode: i32) i32 = {
return syscall2(SYS_ACCESS, path: i64, mode: i64): i32; return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
}; };
// remove — unlink(2). Hare name; the underlying syscall is unlink(2). // remove — unlink(2). Hare name; the underlying syscall is unlink(2).
export fn remove(path: *u8) i32 = { export fn remove(path: *u8) i32 = {
return syscall1(SYS_UNLINK, path: i64): i32; return syscall1(nr.UNLINK, path: i64): i32;
}; };
// getpid(2). Used by the driver to mint unique scratch paths. // getpid(2). Used by the driver to mint unique scratch paths.
export fn getpid() i32 = { export fn getpid() i32 = {
return syscall0(SYS_GETPID): i32; return syscall0(nr.GETPID): i32;
}; };
// fork(2): 0 in the child, child pid in the parent, negative errno // fork(2): 0 in the child, child pid in the parent, negative errno
// on failure. // on failure.
export fn fork() i32 = { export fn fork() i32 = {
return syscall0(SYS_FORK): i32; return syscall0(nr.FORK): i32;
}; };
// execve(2): on success, does not return. // execve(2): on success, does not return.
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = { export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
return syscall3(SYS_EXECVE, path: i64, argv: i64, envp: i64): i32; return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
}; };
// wait4(2): wait for `pid` (or any child if -1), store status in // wait4(2): wait for `pid` (or any child if -1), store status in
// `*status`, return the pid that ended (or negative errno). // `*status`, return the pid that ended (or negative errno).
export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = { export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
return syscall4(SYS_WAIT4, pid: i64, status: i64, return syscall4(nr.WAIT4, pid: i64, status: i64,
options: i64, rusage: i64): i32; options: i64, rusage: i64): i32;
}; };
@@ -200,7 +206,7 @@ export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
// negative errno. The driver uses it to expand `.` to the cwd's // negative errno. The driver uses it to expand `.` to the cwd's
// basename for `ww build` / `ww test`. // basename for `ww build` / `ww test`.
export fn getcwd(buf: *u8, n: u64) i64 = { export fn getcwd(buf: *u8, n: u64) i64 = {
return syscall2(SYS_GETCWD, buf: i64, n: i64); return syscall2(nr.GETCWD, buf: i64, n: i64);
}; };
// getdents64(2) — Linux directory enumeration. The fd must be opened // getdents64(2) — Linux directory enumeration. The fd must be opened
@@ -218,7 +224,7 @@ export fn getcwd(buf: *u8, n: u64) i64 = {
// Returns bytes written into `buf` (advance by d_reclen to walk), // Returns bytes written into `buf` (advance by d_reclen to walk),
// 0 at end-of-directory, or a negative errno. // 0 at end-of-directory, or a negative errno.
export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); return syscall3(nr.GETDENTS64, fd: i64, buf: i64, n: i64);
}; };
// MODULE: wcc // MODULE: wcc

View File

@@ -3,11 +3,11 @@
// either in libwwrt.a (rt_syscall trampoline) or libc bindings, // either in libwwrt.a (rt_syscall trampoline) or libc bindings,
// depending on how the program was linked. // depending on how the program was linked.
@symbol("rt_syscall") fn syscall0(num: i64) i64; @symbol("rt_syscall") fn syscall0(num: nr) i64;
@symbol("rt_syscall") fn syscall1(num: i64, a: i64) i64; @symbol("rt_syscall") fn syscall1(num: nr, a: i64) i64;
@symbol("rt_syscall") fn syscall2(num: i64, a: i64, b: i64) i64; @symbol("rt_syscall") fn syscall2(num: nr, a: i64, b: i64) i64;
@symbol("rt_syscall") fn syscall3(num: i64, a: i64, b: i64, c: i64) i64; @symbol("rt_syscall") fn syscall3(num: nr, a: i64, b: i64, c: i64) i64;
@symbol("rt_syscall") fn syscall4(num: i64, a: i64, b: i64, c: i64, d: i64) i64; @symbol("rt_syscall") fn syscall4(num: nr, a: i64, b: i64, c: i64, d: i64) i64;
@symbol("rt_alloc") fn alloc(n: u64) *void; @symbol("rt_alloc") fn alloc(n: u64) *void;
@symbol("rt_free") fn free(p: *void, n: u64) void; @symbol("rt_free") fn free(p: *void, n: u64) void;
@@ -19,21 +19,27 @@ export fn assert(cond: bool, msg: str) void = {
if (!cond) { abort(msg); }; if (!cond) { abort(msg); };
}; };
def SYS_READ: i64 = 0; // Linux amd64 syscall numbers. Internal to this module — passed as
def SYS_WRITE: i64 = 1; // the first arg of syscall0..4 via libwwrt's rt_syscall trampoline.
def SYS_OPEN: i64 = 2; // `nr` is the type so the call sites can't accidentally pass an
def SYS_CLOSE: i64 = 3; // arbitrary i64 (`syscall1(0i64, ...)` no longer typechecks).
def SYS_LSEEK: i64 = 8; type nr = enum i64 {
def SYS_ACCESS: i64 = 21; READ = 0,
def SYS_DUP2: i64 = 33; WRITE = 1,
def SYS_GETPID: i64 = 39; OPEN = 2,
def SYS_FORK: i64 = 57; CLOSE = 3,
def SYS_EXECVE: i64 = 59; LSEEK = 8,
def SYS_EXIT: i64 = 60; ACCESS = 21,
def SYS_WAIT4: i64 = 61; DUP2 = 33,
def SYS_UNLINK: i64 = 87; GETPID = 39,
def SYS_GETCWD: i64 = 79; FORK = 57,
def SYS_GETDENTS64: i64 = 217; EXECVE = 59,
EXIT = 60,
WAIT4 = 61,
UNLINK = 87,
GETCWD = 79,
GETDENTS64 = 217,
};
// open(2) flags. Linux values, matching <fcntl.h>. Hare names them // open(2) flags. Linux values, matching <fcntl.h>. Hare names them
// `fs::flag::RDONLY` etc; we use the same leaf names so callers say // `fs::flag::RDONLY` etc; we use the same leaf names so callers say
@@ -54,22 +60,22 @@ export type whence = enum i32 {
}; };
export fn exit(code: i32) void = { export fn exit(code: i32) void = {
syscall1(SYS_EXIT, code: i64); syscall1(nr.EXIT, code: i64);
}; };
// Raw, non-fallible primitives. These return Linux's int conventions // Raw, non-fallible primitives. These return Linux's int conventions
// (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a // (negative = -errno, non-negative = bytes/fd/etc). Callers wanting a
// Hare-style fallible API use the wrappers below. // Hare-style fallible API use the wrappers below.
export fn write(fd: i32, buf: *u8, n: u64) i64 = { export fn write(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_WRITE, fd: i64, buf: i64, n: i64); return syscall3(nr.WRITE, fd: i64, buf: i64, n: i64);
}; };
export fn read(fd: i32, buf: *u8, n: u64) i64 = { export fn read(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_READ, fd: i64, buf: i64, n: i64); return syscall3(nr.READ, fd: i64, buf: i64, n: i64);
}; };
export fn close(fd: i32) i32 = { export fn close(fd: i32) i32 = {
return syscall1(SYS_CLOSE, fd: i64): i32; return syscall1(nr.CLOSE, fd: i64): i32;
}; };
// dup2(2): make `newfd` refer to the same description as `oldfd`, // dup2(2): make `newfd` refer to the same description as `oldfd`,
@@ -77,7 +83,7 @@ export fn close(fd: i32) i32 = {
// negative errno. Used by w6c_ww to redirect stdout into an output // negative errno. Used by w6c_ww to redirect stdout into an output
// file without changing the cgen emit path. // file without changing the cgen emit path.
export fn dup2(oldfd: i32, newfd: i32) i32 = { export fn dup2(oldfd: i32, newfd: i32) i32 = {
return syscall2(SYS_DUP2, oldfd: i64, newfd: i64): i32; return syscall2(nr.DUP2, oldfd: i64, newfd: i64): i32;
}; };
// Fallible wrappers. The error variant is `oserror` (an i64 carrying // Fallible wrappers. The error variant is `oserror` (an i64 carrying
@@ -100,7 +106,7 @@ export fn trywrite(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// arena-copied paths usually are by construction). Returns -errno on // arena-copied paths usually are by construction). Returns -errno on
// failure, fd otherwise. Higher-level callers prefer `tryopen`. // failure, fd otherwise. Higher-level callers prefer `tryopen`.
export fn open(path: *u8, flags: flag, mode: i32) i32 = { export fn open(path: *u8, flags: flag, mode: i32) i32 = {
return syscall3(SYS_OPEN, path: i64, (flags as i32): i64, mode: i64): i32; return syscall3(nr.OPEN, path: i64, (flags as i32): i64, mode: i64): i32;
}; };
export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = { export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
@@ -113,7 +119,7 @@ export fn tryopen(path: *u8, flags: flag, mode: i32) (i32 | oserror) = {
// a negative errno. We use this for fstat-free file-size discovery // a negative errno. We use this for fstat-free file-size discovery
// (open ⇒ lseek to end ⇒ lseek back). // (open ⇒ lseek to end ⇒ lseek back).
export fn lseek(fd: i32, off: i64, w: whence) i64 = { export fn lseek(fd: i32, off: i64, w: whence) i64 = {
return syscall3(SYS_LSEEK, fd: i64, off, (w as i32): i64); return syscall3(nr.LSEEK, fd: i64, off, (w as i32): i64);
}; };
// oserror — the underlying errno from a failed syscall, as a // oserror — the underlying errno from a failed syscall, as a
@@ -164,34 +170,34 @@ export fn writeall(fd: i32, buf: *u8, n: u64) (i64 | oserror) = {
// access(2): returns 0 if the file is reachable, negative errno // access(2): returns 0 if the file is reachable, negative errno
// otherwise. mode is the bitset described in <unistd.h> (F_OK=0). // otherwise. mode is the bitset described in <unistd.h> (F_OK=0).
export fn access(path: *u8, mode: i32) i32 = { export fn access(path: *u8, mode: i32) i32 = {
return syscall2(SYS_ACCESS, path: i64, mode: i64): i32; return syscall2(nr.ACCESS, path: i64, mode: i64): i32;
}; };
// remove — unlink(2). Hare name; the underlying syscall is unlink(2). // remove — unlink(2). Hare name; the underlying syscall is unlink(2).
export fn remove(path: *u8) i32 = { export fn remove(path: *u8) i32 = {
return syscall1(SYS_UNLINK, path: i64): i32; return syscall1(nr.UNLINK, path: i64): i32;
}; };
// getpid(2). Used by the driver to mint unique scratch paths. // getpid(2). Used by the driver to mint unique scratch paths.
export fn getpid() i32 = { export fn getpid() i32 = {
return syscall0(SYS_GETPID): i32; return syscall0(nr.GETPID): i32;
}; };
// fork(2): 0 in the child, child pid in the parent, negative errno // fork(2): 0 in the child, child pid in the parent, negative errno
// on failure. // on failure.
export fn fork() i32 = { export fn fork() i32 = {
return syscall0(SYS_FORK): i32; return syscall0(nr.FORK): i32;
}; };
// execve(2): on success, does not return. // execve(2): on success, does not return.
export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = { export fn execve(path: *u8, argv: **u8, envp: **u8) i32 = {
return syscall3(SYS_EXECVE, path: i64, argv: i64, envp: i64): i32; return syscall3(nr.EXECVE, path: i64, argv: i64, envp: i64): i32;
}; };
// wait4(2): wait for `pid` (or any child if -1), store status in // wait4(2): wait for `pid` (or any child if -1), store status in
// `*status`, return the pid that ended (or negative errno). // `*status`, return the pid that ended (or negative errno).
export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = { export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
return syscall4(SYS_WAIT4, pid: i64, status: i64, return syscall4(nr.WAIT4, pid: i64, status: i64,
options: i64, rusage: i64): i32; options: i64, rusage: i64): i32;
}; };
@@ -200,7 +206,7 @@ export fn wait4(pid: i32, status: *i32, options: i32, rusage: *void) i32 = {
// negative errno. The driver uses it to expand `.` to the cwd's // negative errno. The driver uses it to expand `.` to the cwd's
// basename for `ww build` / `ww test`. // basename for `ww build` / `ww test`.
export fn getcwd(buf: *u8, n: u64) i64 = { export fn getcwd(buf: *u8, n: u64) i64 = {
return syscall2(SYS_GETCWD, buf: i64, n: i64); return syscall2(nr.GETCWD, buf: i64, n: i64);
}; };
// getdents64(2) — Linux directory enumeration. The fd must be opened // getdents64(2) — Linux directory enumeration. The fd must be opened
@@ -218,7 +224,7 @@ export fn getcwd(buf: *u8, n: u64) i64 = {
// Returns bytes written into `buf` (advance by d_reclen to walk), // Returns bytes written into `buf` (advance by d_reclen to walk),
// 0 at end-of-directory, or a negative errno. // 0 at end-of-directory, or a negative errno.
export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = { export fn getdents64(fd: i32, buf: *u8, n: u64) i64 = {
return syscall3(SYS_GETDENTS64, fd: i64, buf: i64, n: i64); return syscall3(nr.GETDENTS64, fd: i64, buf: i64, n: i64);
}; };
// MODULE: strconv // MODULE: strconv