StackThreads/MP: version 0.77 User's Guide
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.