diff --git a/doc/occam/Makefile b/doc/occam/Makefile new file mode 100644 index 000000000..20ca79987 --- /dev/null +++ b/doc/occam/Makefile @@ -0,0 +1,17 @@ +EMHOME=../.. +FILES= p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 + +PIC=pic +EQN=eqn +TBL=tbl +../occam.doc: p0 p1 p2 p3 p4 p5 p6 p7 p8 p9 channel.h.t channel.c.t + cat $(FILES) | $(PIC) | $(TBL) | $(EQN) > $@ + +channel.h.t: $(EMHOME)/h/ocm_chan.h + ctot <$(CDIR)/h/ocm_chan.h >channel.h.t + +channel.c.t: channel.c + ctot channel.c.t + +channel.c: $(EMHOME)/lang/occam/lib/tail_ocm.a + arch x tail_ocm.a channel.c diff --git a/doc/occam/ctot b/doc/occam/ctot new file mode 100755 index 000000000..302c6910f --- /dev/null +++ b/doc/occam/ctot @@ -0,0 +1,8 @@ +sed 's/^$/.sp 0.5/ +s/\\/\\e/g +s/^ $/.ft\ +.DE\ +.bp\ +.DS\ +.ft 5\ +.ta 0.65i 1.3i 1.95i 2.6i 3.25i 3.9i 4.55i 5.2i 5.85i 6.5i/' diff --git a/doc/occam/p0 b/doc/occam/p0 new file mode 100644 index 000000000..1055ec1c4 --- /dev/null +++ b/doc/occam/p0 @@ -0,0 +1,21 @@ +.pl 11.7i +.ND +.de PT +.if \\n%>0 .if e .tl '\fB%\fP''' +.if \\n%>1 .if o .tl '''\fB%\fP' +.. +.TL +An Occam Compiler +.AU +Kees Bot +Edwin Scheffer +.AI +Vrije Universiteit +Amsterdam, The Netherlands +.AB +This document describes the implementation of an \fBOccam\fP to \fBEM\fP +compiler. The lexical analysis is done using \fBLex\fP. +For the semantic analysis the extended LL(1) parser generator \fBLLgen\fP is +used. To handle the Occam-specific features as channels and parallelism some +library routines are required. +.AE diff --git a/doc/occam/p1 b/doc/occam/p1 new file mode 100644 index 000000000..d1e2aa497 --- /dev/null +++ b/doc/occam/p1 @@ -0,0 +1,87 @@ +.NH +Introduction +.PP +Occam [1] is a programming language which is based on the concepts of +concurrency and communication. These concepts enable today's applications of +microprocessors and computers to be implemented more effectively. +.PP +An Occam program consists of a (dynamically determined) number +of processes communicating through channels. +To communicate with the outside world some predefined channels are needed. +A channel has only one writer and one reader; it carries machine words and +bytes, at the reader/writer's discretion. The process with its communication +in Occam replaces the procedure with parameters in other languages (there are +no procedures in Occam). +.PP +In addition to the normal assignment statement, Occam has two more +information-transfer statements, the input and the output: +.DS +.ft 5 + chan1 ? x -- reads a value from chan1 into x + chan2 ! x -- writes the value of x onto chan2 +.ft +.DE +Both the outputting and the inputting processes wait until the other is there. +Channels are declared and given names. Arrays of channels are possible. +.PP +Processes come in 5 varieties: sequential, parallel, alternative, +conditional and repetitive. A process starts with a reserved word telling +its nature, followed by an indented list of other processes. (Indentation +is used to indicate block structure.) It may be preceded by declarations. +The processes in a sequential/parallel process are executed sequentially/in +parallel. The processes in an alternative process have guards based on the +availability of input; the first to be ready is executed (this is waiting +for multiple input). The conditional and repetitive processes are normal +\fBIF\fPs and \fBWHILE\fPs. +.PP +\fIProducer-consumer example:\fP +.DS +.ft 5 +.nf +CHAN buffer: -- declares the channel buffer +PAR + WHILE TRUE -- the producer + VAR x: -- a local variable + SEQ + produce(x) -- in some way + buffer ! x -- and send it + WHILE TRUE -- the consumer + VAR x: + SEQ + buffer ? x -- get a value + consume(x) -- in some way +.ft +.fi +.DE +.bp +.PP +Processes can be replicated from a given template; this combines +with arrays of variables and/or channels. +.PP +\fIExample: 20 window-sorters in series:\fP +.DS +.ft 5 +.nf +CHAN s[20]: -- 20 channels +PAR i = [ 0 FOR 19 ] -- 19 processes + WHILE TRUE + VAR v1, v2: + SEQ + s[i] ? v1; v2 -- wait for 2 variables from s[i] + IF + v1 <= v2 -- ok + s[i+1] ! v1; v2 + v1 > v2 -- reorder + s[i+1] ! v2; v1 +.fi +.ft +.DE +.PP +A process may wait for a condition, which must include a comparison +with \fBNOW\fP, the present clock value. +.PP +Processes may be distributed over several processors; all processes +under a \fBVAR\fP declaration must run on the same processor. Concurrency can be +improved by avoiding \fBVAR\fP declarations, and replacing them by \fBCHAN\fP +declarations. Processes can be allocated explicitly on named processors and +channels can be connected to physical ports. diff --git a/doc/occam/p2 b/doc/occam/p2 new file mode 100644 index 000000000..771428c8b --- /dev/null +++ b/doc/occam/p2 @@ -0,0 +1,151 @@ +.NH +The Compiler +.PP +The compiler is written in \fBC\fP using LLgen and Lex and compiles +Occam programs to EM code, using the procedural interface as defined for EM. +In the following sub-sections we describe the LLgen parser generator and +the aspect of indentation. +.NH 2 +The LLgen Parser Generator +.PP +LLgen accepts a Context Free syntax extended with the operators `\f5*\fP', `\f5?\fP' and `\f5+\fP' +that have effects similar to those in regular expressions. +The `\f5*\fP' is the closure set operator without an upperbound; `\f5+\fP' is the positive +closure operator without an upperbound; `\f5?\fP' is the optional operator; +`\f5[\fP' and `\f5]\fP' can be used for grouping. +For example, a comma-separated list of expressions can be described as: +.DS +.ft 5 + expression_list: + expression [ ',' expression ]* + ; +.ft +.DE +.LP +Alternatives must be separated by `\f5|\fP'. +C code (``actions'') can be inserted at all points between the colon and the +semicolon. +Variables global to the complete rule can be declared just in front of the +colon enclosed in the brackets `\f5{\fP' and `\f5}\fP'. All other declarations are local to +their actions. +Nonterminals can have parameters to pass information. +A more mature version of the above example would be: +.DS +.ft 5 + expression_list(expr *e;) { expr e1, e2; } : + expression(&e1) + [ ',' expression(&e2) + { e1=append(e1, e2); } + ]* + { *e=e1; } + ; +.ft +.DE +As LLgen generates a recursive-descent parser with no backtrack, it must at all +times be able to determine what to do, based on the current input symbol. +Unfortunately, this cannot be done for all grammars. Two kinds of conflicts +are possible, viz. the \fBalternation\fP and \fBrepetition\fP conflict. +An alternation confict arises if two sides of an alternation can start with the +same symbol. E.g. +.DS +.ft 5 + plus: '+' | '+' ; +.ft +.DE +The parser doesn't know which `\f5+\fP' to choose (neither do we). +Such a conflict can be resolved by putting an \fBif-condition\fP in front of +the first conflicting production. It consists of a \fB``%if''\fP followed by a +C-expression between parentheses. +If a conflict occurs (and only if it does) the C-expression is evaluated and +parsing continues along this path if non-zero. Example: +.DS +.ft 5 + plus: + %if (some_plusses_are_more_equal_than_others()) + '+' + | + '+' + ; +.ft +.DE +A repetition conflict arises when the parser cannot decide whether +``\f5productionrule\fP'' in e.g. ``\f5[ productionrule ]*\fP'' must be chosen +once more, or that it should continue. +This kind of conflicts can be resolved by putting a \fBwhile-condition\fP right +after the opening parentheses. It consists of a \fB``%while''\fP +followed by a C-expression between parentheses. As an example, we can look at +the \fBcomma-expression\fP in C. The comma may only be used for the +comma-expression if the total expression is not part of another comma-separated +list: +.DS +.nf +.ft 5 + comma_expression: + sub_expression + [ %while (not_part_of_comma_separated_list()) + ',' sub_expression + ]* + ; +.ft +.fi +.DE +Again, the \fB``%while''\fP is only used in case of a conflict. +.LP +Error recovery is done almost completely automatically. All you have to do +is to write a routine called \fILLmessage\fP to give the necessary error +messages and supply information about terminals found missing. +.NH 2 +Indentation +.PP +The way conflicts can be resolved are of great use to Occam. The use of +indentation, to group statements, leads to many conflicts because the spaces +used for indentation are just token separators to the lexical analyzer, i.e. +``white space''. The lexical analyzer can be instructed to generate `BEGIN' and +`END' tokens at each indentation change, but that leads to great difficulties +as expressions may occupy several lines, thus leading to indentation changes +at the strangest moments. So we decided to resolve the conflicts by looking +at the indentation ourselves. The lexical analyzer puts the current indentation +level in the global variable \fIind\fP for use by the parser. The best example +is the \fBSEQ\fP construct, which exists in two flavors, one with a replicator +and one process: +.DS +.nf +.ft 5 + seq i = [ 1 for str[byte 0] ] + out ! str[byte i] +.ft +.fi +.DE +and one without a replicator and several processes: +.DS +.nf +.ft 5 + seq + in ? c + out ! c +.ft +.fi +.DE +The LLgen skeleton grammar to handle these two is: +.DS +.nf +.ft 5 + SEQ { line=yylineno; oind=ind; } + [ %if (line==yylineno) + replicator + process + | + [ %while (ind>oind) process ]* + ] +.ft +.fi +.DE +This shows clearly that, a replicator must be on the same line as the \fBSEQ\fP, +and new processes are collected as long as the indentation level of each process +is greater than the indentation level of \fBSEQ\fP (with appropriate checks on this +identation). +.PP +Different indentation styles are accepted, as long as the same amount of spaces +is used for each indentation shift. The ascii tab character sets the indentation +level to an eight space boundary. The first indentation level found in a file +is used to compare all other indentation levels to. diff --git a/doc/occam/p3 b/doc/occam/p3 new file mode 100644 index 000000000..143df659a --- /dev/null +++ b/doc/occam/p3 @@ -0,0 +1,337 @@ +.NH +Implementation +.PP +It is now time to describe the implementation of some of the occam-specific +features such as channels and \fBNOW\fP. Also the way communication with +UNIX\(dg is performed must be described. +.FS +\(dg UNIX is a trademark of Bell Laboratories +.FE +For a thorough description of the library routines to simulate parallelism, +which are e.g. used by the channel routines and by the \fBPAR\fP construct +in Appendix B, see [6]. +.NH 2 +Channels +.PP +There are currently two types of channels (see Figure 1.) indicated by the type +field of a channel variable: +.IP - +An interprocess communication channel with two additional fields: +.RS +.IP - +A synchronization field to hold the state of an interprocess communication +channel. +.IP - +An integer variable to hold the value to be send. +.RE +.IP - +An outside world communication channel. This is a member of an array of +channels connected to UNIX files. Its additional fields are: +.RS +.IP - +A flags field holding a readahead flag and a flag that tells if this channel +variable is currently connected to a file. +.IP - +A preread character, if readahead is done. +.IP - +An index field to find the corresponding UNIX file. +.RE +.LP +.PS +box ht 3.0 wid 3.0 +box ht 0.75 wid 0.75 with .nw at 1st box.nw + (0.5, -0.5) "Process 1" +box ht 0.75 wid 0.75 with .ne at 1st box.ne + (-0.5, -0.5) "Process 2" +box ht 0.75 wid 0.75 with .sw at 1st box.sw + (0.5, 0.5) "Process 3" +box ht 0.75 wid 0.75 with .se at 1st box.se + (-0.5, 0.5) "Process 4" +line right from 5/12 <2nd box.ne, 2nd box.se> to 3rd box +line right from 7/12 <2nd box.ne, 2nd box.se> to 3rd box +line right from 5/12 <4th box.ne, 4th box.se> to 5th box +line right from 7/12 <4th box.ne, 4th box.se> to 5th box +line down from 5/12 <2nd box.sw, 2nd box.se> to 4th box +line down from 7/12 <2nd box.sw, 2nd box.se> to 4th box +line down from 5/12 <3rd box.sw, 3rd box.se> to 5th box +line down from 7/12 <3rd box.sw, 3rd box.se> to 5th box +line right 1.0 from 5/12 <5th box.ne, 5th box.se> +line right 1.0 from 7/12 <5th box.ne, 5th box.se> +line left 1.0 from 5/12 <2nd box.nw, 2nd box.sw> +line left 1.0 from 7/12 <2nd box.nw, 2nd box.sw> +.PE +.DS C +\fIFigure 1. Interprocess and outside world communication channels\fP +.DE +The basic channel handling is done by \f5chan_in\fP and \f5chan_out\fP. All +other routines are based on them. The routine \f5chan_any\fP only checks if +there's a value available on a given channel. (It does not read this value!) +\f5C_init\fP initializes an array of interprocess communication channels. +.LP +The following table shows Occam statements paired with the routines used to +execute them. +.TS H +center, box; +c | c | c +lf5 | lf5 | lf5. +Occam statement Channel handling routine Called as += +.sp 0.5 +.TH +T{ +.nf +CHAN c: +CHAN c[z]: +.fi +T} T{ +.nf +c_init(c, z) +chan *c; unsigned z; +.fi +T} T{ +.nf +c_init(&c, 1); +c_init(&c, z); +.fi +T} +.sp 0.5 +_ +.sp 0.5 +T{ +.nf +c ? v +.fi +T} T{ +.nf +chan_in(v, c) +long *v; chan *c; +.fi +T} T{ +.nf +chan_in(&v, &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ? b[byte i] +.fi +T} T{ +.nf +cbyte_in(b, c) +char *b; chan *c; +.fi +T} T{ +.nf +cbyte_in(&b[i], &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ? a[i for z] +.fi +T} T{ +.nf +c_wa_in(a, z, c) +long *a; unsigned z; chan *c; +.fi +T} T{ +.nf +c_wa_in(&a[i], z, &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ? a[byte i for z] +.fi +T} T{ +.nf +c_ba_in(a, z, c) +long *a; unsigned z; chan *c; +.fi +T} T{ +.nf +c_ba_in(&a[i], z, &c); +.fi +T} +.sp 0.5 +_ +.sp 0.5 +T{ +.nf +c ! v +.fi +T} T{ +.nf +chan_out(v, c) +long *v; chan *c; +.fi +T} T{ +.nf +chan_out(&v, &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ! a[i for z] +.fi +T} T{ +.nf +c_wa_out(a, z, c) +long *a; unsigned z; chan *c; +.fi +T} T{ +.nf +c_wa_out(&a[i], z, &c); +.fi +T} +.sp 0.5 +T{ +.nf +c ! a[byte i for z] +.fi +T} T{ +.nf +c_ba_out(a, z, c) +long *a; unsigned z; chan *c; +.fi +T} T{ +.nf +c_ba_out(&a[i], z, &c); +.fi +T} +.sp 0.5 +_ +.sp 0.5 +T{ +.nf +alt + c ? .... + .... +.fi +T} T{ +.nf +int chan_any(c) +chan *c; +.fi +T} T{ +.nf +deadlock=0; +for(;;) { + if (chan_any(&c)) { + .... + .... +.fi +T} +.sp 0.5 +.TE +The code of \f5c_init\fP, \f5chan_in\fP, \f5chan_out\fP and \f5chan_any\fP +can be found in Appendix A. +.NH 3 +Synchronization on interprocess communication channels +.PP +The synchronization field can hold three different values indicating the +state the channel is in: +.IP "- \fBC\(ulS\(ulFREE\fP:" 15 +Ground state, channel not in use. +.IP "- \fBC\(ulS\(ulANY\fP:" 15 +Channel holds a value, the sending process is waiting for an acknowledgement +about its receipt. +.IP "- \fBC\(ulS\(ulACK\fP:" 15 +Channel data has been removed by a receiving process, the sending process can +set the channel free now. +.LP +A sending process cannot simply wait until the channel changes state C\(ulS\(ulANY +to state C\(ulS\(ulFREE before it continues. There is a third state needed to prevent +a third process from using the channel before our sending process is +acknowledged. Note, however that it is not allowed to use a channel for input +or output in more than one parallel process. This is too difficult to check +in practice, so we tried to smooth it a little. +.NH 2 +NOW +.PP +\fBNOW\fP evaluates to the current time returned by the time(2) system call. +The code is simply: +.DS +.ft 5 +.nf + long now() + { + deadlock=0; + return time((long *) 0); + } +.fi +.ft +.DE +The ``deadlock=0'' prevents deadlocks while using the clock. +.NH 2 +UNIX interface +.PP +To handle the communication with the outside world the following channels are +defined: +.IP - +\fBinput\fP, that corresponds with the standard input file, +.IP - +\fBoutput\fP, that corresponds with the standard output file, +.IP - +\fBerror\fP, that corresponds with the standard error file. +.IP - +\fBfile\fP, an array of channels that can be subscripted with an index +obtained by the builtin named process ``\f5open\fP''. Note that +\fBinput\fP=\fBfile\fP[0], \fBoutput\fP=\fBfile\fP[1] and +\fBerror\fP=\fBfile\fP[2]. +.LP +Builtin named processes to open and close files are defined as +.DS +.nf +.ft 5 +proc open(var index, value name[], mode[]) = ..... : +proc close(value index) = ..... : +.fi +.ft +.DE +To open a file `junk', write nonsense onto it, and close it, goes as follows: +.DS +.ft 5 +.nf + var i: + seq + open(i, "junk", "w") + file[i] ! nonsense + close(i) +.fi +.ft +.DE +Errors opening a file are reported by a negative index, which is the +negative value of the error number (called \fIerrno\fP in UNIX). +.LP +Bytes read from or written onto these channels are taken from occam variables. +As these variables can hold more than 256 values, some negative values are used +to control channels. These values are: +.IP "- \fBEOF\fP" 9 +(-1): Eof from file channel is read as -1. +.IP "- \fBTEXT\fP" 9 +(-2): A -2 written onto any channel connected to a terminal puts this +terminal in the normal line oriented mode (i.e. characters typed are echoed +and lines are buffered before they are read). +.IP "- \fBRAW\fP" 9 +(-3): A -3 written onto any channel connected to a terminal puts it in raw mode +(i.e. no echoing of typed characters and no line buffering). +.LP +To exit an Occam program, e.g. after an error, a builtin named process +\f5exit\fP is available that takes an exit code as its argument. +.NH 2 +Replicators and slices +.PP +Both the base and the count of replicators like in +.DS +.ft 5 + par i = [ base for count ] +.ft +.DE +may be arbitrary expressions. The count in array slices like in +.DS +.ft 5 + c ? A[ base for count ] +.ft +.DE +must be a constant expression however, the base is again free. diff --git a/doc/occam/p4 b/doc/occam/p4 new file mode 100644 index 000000000..e6da51698 --- /dev/null +++ b/doc/occam/p4 @@ -0,0 +1,42 @@ +.NH +Particular details +.NH 2 +Lower case/Upper case +.PP +Keywords must be either fully written in lower case or in upper case, thus +\fBPAR\fP is equivalent to \fBpar\fP but \fBPar\fP is not a keyword. Identifiers +may be of mixed case. Different styles are used in our examples just to indicate +what's accepted by the compiler. +.NH 2 +File inclusion +.PP +The C preprocessor is applied to the input file before +compilation, so that files containing useful \fBPROC\fP and \fBDEF\fP +declarations can be used in your program by using the \fB#include\fP-directive +of the preprocessor. +.NH 2 +Substitution +.PP +Named processes are not textually substituted. A procedure call is used instead. +The semantics of occam substitution imply this by letting a global variable +(i.e. not declared inside the named process' body) be found where the named +process is defined and not where it is substituted. +.NH 2 +ANY +.PP +According to the occam syntax the \fBANY\fP keyword may be the only argument of +an input or output process. Thus, +.DS +.ft 5 + c ? ANY; x +.ft +.DE +is not allowed. Because it was easy to add, and it was used by some programs, +our compiler allows it. (If you prefer portability you are advised not to make +use of it.) +.NH 2 +Configuration +.PP +The special configuration keywords like \fBPLACED\fP, \fBALLOCATE\fP, \fBPORT\fP +and \fBLOAD\fP are not implemented. Only \fBPRI\fP works because \fBPAR\fP and +\fBALT\fP work the same without it. diff --git a/doc/occam/p5 b/doc/occam/p5 new file mode 100644 index 000000000..1dc98e02c --- /dev/null +++ b/doc/occam/p5 @@ -0,0 +1,18 @@ +.NH +Conclusions +.PP +Writing the compiler was very straightforward using the LLgen parser generator. +Its extended grammar and its way of conflict resolving were of great use to us, +especially +the indentation handling could be implemented quite easily. The automatic +error recovery given by LLgen took a great weight of our shoulders. +.PP +A set of parallelism simulation routines makes implementing \fBPAR\fP constructs +very simple. And we consider it a necessity to have such a layer to shield the +compiler writer from these details. +.PP +The translation to EM code was fairly direct, no great tricks were needed to +make things work. Only the different sizes of words and pointers that are given +as parameters to the compiler must be carefully watched. Variables or pointers +must sometimes be handled with double word instructions for different word or +pointer sizes. diff --git a/doc/occam/p6 b/doc/occam/p6 new file mode 100644 index 000000000..2ce3d9da4 --- /dev/null +++ b/doc/occam/p6 @@ -0,0 +1,5 @@ +.NH +Acknowledgement +.PP +We want to thank Dick Grune for his description of Occam which is used +in the introduction. diff --git a/doc/occam/p7 b/doc/occam/p7 new file mode 100644 index 000000000..c9397d156 --- /dev/null +++ b/doc/occam/p7 @@ -0,0 +1,23 @@ +.bp +.NH +References +.LP +.IP [1] +INMOS limited, \fIOCCAM Programming manual\fP, Prentice-Hall, 1984. +.IP [2] +C. J. H. Jacobs, \fISome Topics in Parser Generation\fP, +Informatica Rapport IR-105, Vrije Universiteit, Amsterdam, October 1985. +.IP [3] +B. W. Kernighan and D. M. Ritchie, \fIThe C Programming Language\fP, +Prentice-Hall, 1978. +.IP [4] +M. E. Lesk, \fILex - A Lexical Analyser Generator\fP, Comp. Sci. Tech. Rep. +No. 39, Bell Laboratories, Murrey Hill, New Jersey, October 1975. +.IP [5] +A. S. Tanenbaum, H. van Staveren, E. G. Keizer, J. W. Stevenson, +\fIDescription of a Machine Architecture for use with Block Structured +Languages\fP, Informatica Rapport IR-81, Vrije Universiteit, Amsterdam, 1983. +.IP [6] +K. Bot and E. Scheffer, \fIA set of multi-process primitives for stack based +machines\fP, Vrije Universiteit, Amsterdam, 1986. +.LP diff --git a/doc/occam/p8 b/doc/occam/p8 new file mode 100644 index 000000000..88c0b58fe --- /dev/null +++ b/doc/occam/p8 @@ -0,0 +1,16 @@ +.bp +.NH +Appendix A: Implementation of the channel routines +.DS L +.ft 5 +.ta 0.65i 1.3i 1.95i 2.6i 3.25i 3.9i 4.55i 5.2i 5.85i 6.5i +.so channel.h.t +.ft +.DE +.bp +.DS L +.ft 5 +.ta 0.65i 1.3i 1.95i 2.6i 3.25i 3.9i 4.55i 5.2i 5.85i 6.5i +.so channel.c.t +.ft +.DE diff --git a/doc/occam/p9 b/doc/occam/p9 new file mode 100644 index 000000000..dd8292f2f --- /dev/null +++ b/doc/occam/p9 @@ -0,0 +1,60 @@ +.bp +.NH +Appendix B: Translation of a \fBPAR\fP construct to EM code using the library +routines to simulate parallelism +.PP +Translation of the parallel construct: +.DS +.ft 5 + par + P0 + par i = [ 1 for n ] + P(i) +.DE +is +.TS +center; +lf5 lf5. + lal -20 ; Assume 20 bytes of local variables at this moment + cal $parbegin ; Set up a process group + asp 4 ; Assume pointersize = 4 + cal $parfork ; Split stack in two from local -20 + lfr 4 ; Assume wordsize = 4 + zne *23 ; One end jumps to second process, other continues here + lor 0 ; Static link + cal $P0 + asp 4 + bra *24 ; Jump to the outer parend +23 + cal $parfork ; Fork off `par i = ...' process + lfr 4 + zne *25 ; One end jumps to end of outer par + lal -20 ; Place break just above i + cal $parbegin ; Set up another process group for the P(i) + loc 1 + stl -24 ; i:=1 + lol n ; Assume n can be addressed this simply + stl -28 ; A nameless counter + bra *26 ; Branch to counter test +27 + cal $parfork ; Fork off one P(i) + lfr 4 + zne *28 ; One jumps away to increment i, the other calls P(i) + lol -24 + lor 0 + cal $P + asp 8 + bra *29 +28 + inl -24 ; i:=i+1 + del -28 ; counter:=counter-1 +26 + lol -28 + zgt *27 ; while counter>0 repeat loop +29 + cal $parend ; Wait for the P(i) to finish, then delete group + bra *24 ; Jump to the higher up meeting place with P0 +25 ; Note that the bra will be optimized away +24 + cal $parend ; Wait for both processes to end, then delete group +.TE