Date: Fri, 27 Oct 1995 13:09:46 -0700 (MST) From: Terry Lambert <terry@lambert.org> To: bde@zeta.org.au (Bruce Evans) Cc: bde@zeta.org.au, terry@lambert.org, CVS-commiters@freefall.freebsd.org, bde@freefall.freebsd.org, cvs-sys@freefall.freebsd.org, hackers@freebsd.org, swallace@ece.uci.edu Subject: Re: SYSCALL IDEAS [Was: cvs commit: src/sys/kern sysv_msg.c sysv_sem.c sysv_shm.c] Message-ID: <199510272009.NAA23809@phaeton.artisoft.com> In-Reply-To: <199510271903.FAA24658@godzilla.zeta.org.au> from "Bruce Evans" at Oct 28, 95 05:03:48 am
next in thread | previous in thread | raw e-mail | index | archive | help
> >What wall time differentials do you expect from the conversion, and > >why? > > Small. The current unportable code is close to optimal. I wouldn't > make it slower. The machine generated code for the current FreeBSD > i386 ABI might even be exactly the same as now because all conversions > can be reduced to no-ops (this must be possible because the unportable > code actually works). The reason for my asking was to determine if you believed this to be a speed optimization (which I might be able to get behind) or if it's simply an interface change (which I suspected it was, and so it's now an issue of whether it's gratuitious or if ti really buys something). [ ... reordered for coherency ... ] > >#pragma. If we are talking about compiler modifications, packing #pragma's > >are much more generally useful than register call wrappings and the > >internal code you have suggested for varargs. > > All #pragmas are unportable. All packing assumptions are unportable. I argue that a #pragma beats the __attrib crap because a compiler may ignore it. > Conceptually it's a different layer but it shouldn't be implemented as > a separate layer. My idea is to introduce such a conceptual layer > somewhere between the syscall entry point (Xsyscall()) and the syscall > implementing functions (e.g., read()) and then merge the layers as > much as possible using machine-generated code. [ ...] > >What is your wrapper for open( const char *, int, ...)? > > On the user side (so that the open syscall doesn't have a variable number > of args: > > int open(char const *path, int flags, ...) Whoah. Right there we have a problem. I'll be happy to give you my varrarg macros to make them ANSI-C and K&R C independent. Usign varrargs in an ANSI dependent fashion reduces portability to non-ANIC-C envirnments. The things belong in ctypes.h anyway. [ ... ] > int open(char const *path, int flags, ...) > { > va_list argp; > int retval; > > va_start(argp, flags); > if (flags & O_CREAT) { > promoted_mode_t mode; > registered_mode_t rmode; > > mode = va_arg(argp, int); > rmode = = (registered_mode_t)mode; > retval = three_arg_open_syscall(path, flags, rmode); > } else > retval = two_arg_open_syscall(path, flags); > va_end(argp); > return retval; > } I think that the system call itself could make the decision based on the O_CREAT itself. I'd suggest changing: > retval = three_arg_open_syscall(path, flags, rmode); > } else > retval = two_arg_open_syscall(path, flags); to: > retval = three_arg_open_syscall(path, flags, rmode); > } else > retval = three_arg_open_syscall(path, flags, 0); [ ... ] > On the kernel side: I can't write it without knowing the target machine. > Assuming a simple case where the user side stored the args in a uniform > way on the stack or in registers and the lower level kernel code has > copied the args from to memory: [ ... ] > path = *(char const *)((char *)argp + PATH_OFFSET); > flags = *(int *)((char *)argp + FLAGS_OFFSET); > mode = *(promoted_mode_t *)((char *)argp + MODE_OFFSET); Forgive me... but this is butt-ugly. I mean really, *really* butt-ugly. I find this horribly obfucated. This is exactly what I feared when I objected. 8-(. BTW: the 'path' line is broken... "*(const char **)", maybe? Since the stack variable assigns are based on an entry argument, then the assignments can be done at declaration time. Therefore something like: #define ARG(argtype,arg) \ argtype arg = *(__CONCAT(argtype,*))((char *)argp + \ __CONCAT(arg,_OFFSET)); Then: > int syscall_entry_open(struct proc *p, void *argp, int *retval) > { > ARG( char const *, PATH); > ARG( int, FLAGS); > ARG( promoted_mode_t, MODE); > > if (flags & O_CREAT) { > return open(p, retval, PATH, FLAGS, MODE); > } > return open(p, retval, PATH, FLAGS); > } Would be more readable. I don't understand why passing a bogus mode to open when O_CREAT wasn't present would be a problem??? That is, why not: > int syscall_entry_open(struct proc *p, void *argp, int *retval) > { > ARG( char const *, PATH); > ARG( int, FLAGS); > ARG( promoted_mode_t, MODE); > > return open(p, retval, PATH, FLAGS, MODE); > } ??? Hell: #define SYSCALL_ENTRY(function) \ int __CONCAT(syscall_entry_,function) \ (struct proc *p, void *argp, int *retval) > SYSCALL_ENTRY(open) > { > ARG( char const *, PATH); > ARG( int, FLAGS); > ARG( promoted_mode_t, MODE); > > return open(p, retval, PATH, FLAGS, MODE); > } > >What about non-native architecture ABI support? > > It requires more code to generate the conversion code. In particular, > sizeof() and `struct' can't be used, because the non-native compiler > may have completely different behaviour (of course, it's not practical > to emulate a machine with a larger address space or ints larger than > your quads). I agree with the practicality argument: after all, that's exactly what we are doing already with lseek, et al. 8-). I know the XDR aspects re structure packing are an issue, though since you seem to be assuming the same compiler source everywhere (GCC), I don't see where a packing paragma would be more of an issue than any other approach requiring compiler modifications. > >By going to a register passing mechanism, you destroy this, and complicate > >the non-native ABI's at the same time... don't you see this as well? > > The register passing mechanism for syscalls is a side issue. I only > mentioned it as an example of an ABI that we have to emulate (for Linux) > and which is better so we should use it. There would be no new > complications (except for improvements) because we already support the > Linux ABI. I'm glad to see this. I think, though, the rationale for the changes still hasn't been clarified. > >??? If it's not required, then either you *aren't* talking about passing > >arguments to system calls in registers *or* you expect the compiler to > >"do the right thing" magically. > > I'm talking about the requirement in C to pass parameters of the correct > type (if a prototype is in scope, then there are more correct types). OK. I buy this too. But this implies that if there is not a prototype in scope, a function that uses a type that is not correct without a prototype in scope should not generate a bogus call reference. This was my point on the quad crap and reverting the default interfaces to POSIX compliance (and double doesn't count unless you can use it without a prototype in scope). > >Right now I can write code that makes system calls in assembly. With > >register passing conventions, I will need to either use library or > >machine generated external routine calls (unacceptable), or I will have > >to have documentation of what the compiler has done so that I can do > >the same thing (non-portable as hell -- ever use M4 to write machine > >independent assembly code?). > > It's always been necessary to know what what the compiler does if you > write glue functions in assembler. (gcc) inline assembler has many > advantages here. You don't need to know what the compiler does (you > just tell it how to load syscall args), and one layer of glue can be > avoided. But *lord*! Having to happen to know what register the argument goes into before the call! > >> >Note that prototypes of system calls screw up your ability to properly > >> >utilize the syscall(2) call gate mechanism the way it was intended. A > >> > >> The use of syscall() in general requires handling all the messy conversion > >> issues that we have been discussing in your own code. > > >Why is that? A push is a push. A trap is a trap. > > >The only "messy conversion" is in pushing the arguments on the stack, > >and I still fail to see the benefit of playing "keep up with Linux" > >in this regard. > > syscall() is written in C, so the compiler can pass args to it anywhere it > wants. syscall() then has the task of putting the args where the kernel > expects them. In general, it would have to know about all syscalls and > do the inverse of the conversions that I'm talking about doing in the > kernel. Bletch. How does Linux deal with it (my linux box is running Win95 right now)? > Consider a 68K compiler that passes the first 2 pointer args (if any) > in a0-a1 and the first 2 integer args (if any) in d0-d1 and the other > args on the stack. How are you going to push the args in syscall()? > Hint: you'll need to know the arg types of all syscalls. That's an evil way to pass arguments, then. 8-(. > >> (2) choose a different violation of the ANSI C > >> >and POSIX standards -- interface extension -- instead of passing quad's > >> > >> POSIX allows most reasonable extensions. > > >Yeah, well using quad for off_t isn't one of them. > > Would you prefer double? Allowing it seems to be a bug in POSIX.1-1990. > More things would break. No. I'd prefer int/long. > It's worse than that. Linux doesn't have quads. Applications have to use > pairs of longs and do their own (equivalent to quad) arithmetic on them. > So ifdefs like the above all through the code might be necessary. Linux has GCC. Linux has quads. > We're in violent agreement :-). I'd like to hide the current > unportabilities and scattered casts in a conceptually simple layer. I agree. > This requires a complicated and unportable implementation for the > layer. I don't agree. 8-). Terry Lambert terry@lambert.org --- Any opinions in this posting are my own and not those of my present or previous employers.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199510272009.NAA23809>