PREV UP NEXT StackThreads/MP: version 0.77 User's Guide

10.3: Structure passing on SPARC

If you apply patches under gccpatch to your gcc, you can forget about the problem.

The problem arises when a procedure performs two or more logically concurrent procedure calls that take a structure as an argument. The problem is, in such programs, structure arguments passed to a procedure call may be overwritten by subsequent ones.

For example,

@ 1       typedef struct S { ... };
@ 2       typedef struct T { ... };
@ 3
@ 4       void g(S x) { ... };
@ 5       void h(T y) { ... };
@ 6
@ 7       void f()
@ 8       {
@ 9         S s; T t;
10         ...
11         ST_THREAD_CREATE(g(s));
12         h(t);
13       }

Procedure calls g(s) and h(t) are logically concurrent and both take a structure argument. Procedure g may observe that its incoming parameter (x) is overwritten.

This problem occurs basically because SPARC calling convention uses the same storage to pass structure arguments to procedures. In this example, the storage for passing s to g and passing t to h are the same. The above program is equivalent to:

        void f()
@        {
@          union { S s; T t; } struct_passing_area;
@          ...
@          ST_THREAD_CREATE(g(&struct_passing_area.s));
@          h(&struct_passing_area.t);
@        }

This is not a problem in sequential programs, because when f calls h(t), procedure call g(s) has terminated. This is not the case when g(s) is evaluated by a new thread. What happens is that, concurrent activities h and g unconsciously share an unnamed storage.

To sidestep this problem, all procedures that take a structure parameter can copy the parameter into a local storage just after it is called. For example, the above procedure g can be rewritten as follows.

        void g(S _x) { S x = _x; ... };

If such a copy is too expensive, and if you can change the signature of g, you may pass the structure by reference as follows.

        void f()
@        {
@          S s; T t;
@          ...
@          ST_THREAD_CREATE(g(&s));
@          h(&t);
@        }

This does not work if f itself modifies s after calling g. Here we are facing essentially the same problem as before, but this time, the programmer can easily reason about the problem because the storage is explicitly named by the programmer. The real problem in the first program is that an unnamed storage is shared and thus the programmer cannot reason about it.

You may wonder why doesn't this problem occur on other CPUs or for scalar arguments? Procedures normally place arguments to a procedure call on registers and SP-relative locations (memory locations addressed via stack pointer). On Mips, Alpha, and Pentium, this is the case for any type of arguments. SPARC places scalar arguments on registers and SP-relative locations, but places structure arguments on FP-relative locations (memory locations addressed via frame pointer). For parameters passed via registers, the compiler-generated code, if necessary, saves them when the control departs from the procedure (by calling another procedure). For parameters passed via SP-relative locations, StackThreads/MP runtime system ensures that, if the control returns to f before g exits, the stack pointer is adjusted so that h(t) does use the same storage as g(s). When the control returns to f before g exits, the values of the stack pointer at different at line 11 and line 12 are different, effectively renaming the storage for SP-relative locations.