Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!seismo!mimsy!chris From: chris@mimsy.UUCP (Chris Torek) Newsgroups: comp.lang.c Subject: Re: structure function returns -- how? Message-ID: <4686@mimsy.UUCP> Date: Sun, 7-Dec-86 22:29:23 EST Article-I.D.: mimsy.4686 Posted: Sun Dec 7 22:29:23 1986 Date-Received: Mon, 8-Dec-86 01:57:36 EST References: <131@hcx1.UUCP> Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742 Lines: 124 In article <131@hcx1.UUCP> notes@hcx1.UUCP (Dave Ray) writes: >The pcc compilers I have seen apparently "set" [a return value] in >[the staack frame of the function that returns a structure]. >They then return a pointer to [this stack object], and copying from >that pointer is done [by the caller]. But what happens if a signal >comes through during the copy? The return value gets clobbered. The 4BSD Vax PCC allocates a static return value in a function that returns a structure, and returns a pointer to the static return value. I.e., given struct foo f() { ... return (rv); } g() { struct foo t; t = f(); } the compiler in fact generates code more like this: struct foo * f() { static struct foo _temp_; ... _temp_ = rv; return (&_temp_); } g() { struct foo t; t = *f(); } >Another compiler overcomes this problem by passing the address of a >local structure in [the caller]'s frame to the [structure valued] function. That works too. >Are there other issues to be considered? PCC's method is not re-entrant, but averts disaster when people call a structure-valued function just for side effects, and neglect to declare the function first. If I were writing the compiler, I would use the last method: the caller should pass a pointer to an object to be set to the return value. This is in particular more efficient in the f() and g() example above, since g() can pass a pointer to `t' and avoid a copy. One possible problem with this is in code of this form: struct foo f() { ... } g() { static struct foo t; t = f(); } Since `t' is static here, it is conceivable that there might be problems with non-atomic adjustment of `t' in an optimised version of f(), if f() calls g() recursively. To give a more concrete example (vertically compressed for small screens): typedef struct point { int x, y; } point; point f() { struct point rv; rv.y = global_y++; rv.x = more() ? g() : 0; return (rv); } int g() { static struct point last_point = { 1, 1 }; if (more()) return (last_point.y + 1); last_point = f(); return (last_point.y); } An `optimising' compiler might compile instead this: void f(rvp) point *rvp; { rvp->y = global_y++; rvp->x = more() ? g() : 0; } int g() { static struct point last_point = { 1, 1 }; if (more()) return (last_point.y + 1); f(&last_point); return (last_point.y); } But this will not always have the same effect, since f() could alter g's last_point during, rather than after, the recursion, making g() return a different value in the recursive call. A `correct' optimisation of f(): void f(rvp) point *rvp; { int _temp_y = global_y++; rvp->x = more() ? g() : 0; rvp->y = _temp_y; } That is, all the assignments must be done at the end of the function in the presence of recursion. Asynchronous calls (signals) are even worse, but create atomicity problems with any scheme. -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690) UUCP: seismo!mimsy!chris ARPA/CSNet: chris@mimsy.umd.edu