REF POPC John Gibson Dec 1994
COPYRIGHT University of Sussex 1994. All Rights Reserved.
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<< POP-11 OBJECT-MODULE >>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<< COMPILATION >>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<< >>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
This file is a reference manual for the Pop-11 object-module compilation
and linking system. It describes in detail Popc, the object-module
compiler for Pop-11 programs, and also discusses the associated
utilities Poplink and Poplibr. It should be read in conjunction with
HELP * POPC, which is a user-guide for the three programs (listing all
their available options, etc).
CONTENTS - (Use <ENTER> g to access required sections)
1 Overview
2 Use of +strict compile_mode
2.1 Effect on Declarations
2.2 Effect on Writeability of Structures
3 How Popc Compiles a File
3.1 Compile Phase
... Shadow Values for Permanent Identifiers
... Use of Libraries
... Testing for Popc
3.2 Value Generation Phase
... Structures Generated by Poplink
... Constants vs Variables
... Value-Generation Errors
... Restriction on Constant Values
4 Changing Pop-11 Files to Work with Popc
4.1 Permanent Identifiers: Declarations & References
... Poplog System Identifiers and popsyslist
... Multi-File Programs
... Record and Vector Classes (defclass-declare)
... Other Declarations (declare_incremental and declare_updater)
... -include Option
4.2 Use of uses-now Instead of uses
4.3 Restrictions on Compile-Time Executed Code
4.4 Syntax, Macro & Other Compile-Time Executed Procedures
... General
... Permanent
... Lexical
5 Additional Syntax For Popc
5.1 Incremental Identifiers
... Syntax of declare_incremental
... Assignment Code: Basic Initialisation
... Lists
... Procedures
... Properties
5.2 Building W-Libraries
5.3 Other
6 Program Linking
6.1 Exporting of Identifiers and Sections
6.2 Run-Time Autoloading of Identifiers
6.3 Weakly-Referenced Identifiers
6.4 testdef
6.5 Entry Procedure
6.6 Embedding Pop in C
... Initialising Pop
... Unix Signals
7 Miscellaneous
7.1 Allocation of External Symbols
-----------
1 Overview
-----------
In conventional object-module (or 'batch') compilation, each source file
of program code is compiled to a corresponding object file, which
contains a translation of the program into host-machine instructions;
the set of object files is then linked by the operating system (O/S)
linker to produce an executable image file. This usually also results in
the incorporation of one or library object modules, which are extracted
automatically by the linker on demand.
With Popc the situation is similar, but with one major difference: in
order to handle the linking of global data structures within a Pop
program, a second file is produced for each source file compiled. These
extra files (called w-files, from their .w extension) are in binary
format, and describe the usage by their corresponding object files of
such things as Pop identifiers, quoted words, and other data.
Thus a w-file together with its associated object file can be thought of
as a composite 'Pop object file'. (Ideally, the two would actually be
combined into a single file, but O/S considerations make this
impossible.) Similarly, just as an O/S library archive can be built from
a set of object files (e.g. with the ar utility in Unix, or library in
VMS), so Poplibr can be used to create and maintain 'Pop object
libraries' from sets of w- and object- files, where each library
consists of a w-library file plus an ordinary O/S object library.
All the normal Poplog system libraries are compiled to corresponding
w+object libraries; these reside in the directory $popobjlib.
Another respect in which Popc differs from conventional object compilers
is that library archives are used not only at link-time, but also at
compile-time, to extract declarations for library identifiers used by
the files being compiled. This makes it unnecessary for programs to have
to provide declarations for system identifiers. For each file compiled,
the libraries so used are remembered on the output w-file, and
automatically supplied to the linking process.
Linking of a program is performed by the Pop linker, Poplink. This takes
as input the set of all w-files for the program, automatically extracts
any Pop system library modules used, and performs the necessary
global-structure linking. The linked global data structures are
generated as some additional object files, which Poplink then passes,
together with all object files/libraries corresponding to the
w-files/libraries used, as input to the O/S linker.
The diagram below illustrates the compilation and linking process for a
program consisting of two source files 'foo.p' and 'baz.p'. In this, the
dotted lines represent the usage of w-libraries by Popc for extracting
identifier declarations, and add.o represents the additional object
files produced by Poplink:
Popc Poplink O/S linker
---------
--------- | foo.w | -----
| foo.p | --O--> |-------| |
--------- | | foo.o | ----+--------------------
| --------- | |
| --------- | |
--------- | | baz.w | ----| |
| baz.p | --O--> |-------| | |
--------- | | baz.o | ----+-------------------|
| --------- | |
| | --------- | ----------
| O---> | add.o | ----O---> | image |
| | --------- | ----------
| --------- | |
w-library --<- | lib.w | ----- |
(.wlb) | ... | |
|-------| |
o-library | lib.o | -------------------------
(.olb) | ... |
---------
As the description above implies, Poplink runs the O/S linker
automatically. In addition (as with Unix C compilers), Popc runs Poplink
automatically for you (unless you specify otherwise with the -c option).
Thus Popc is used for both compilation and linking.
------------------------------
2 Use of +strict compile_mode
------------------------------
2.1 Effect on Declarations
---------------------------
In compiling Pop-11 programs with Popc, you are strongly advised to
follow Poplog system and library source code by using
compile_mode :pop11 +strict;
in all Pop-11 source files (see HELP * COMPILE_MODE).
First, this disallows the use of 'old-style' Pop-11 where the formal
argument and result identifiers of procedures default to
dynamically-local vars, and other procedure locals are declared with
vars. It forces all procedure formals to be declared explicitly (usually
as lvars).
For example, without +strict a procedure such as
define old_style(x, y, z);
vars u, v, w;
...
enddefine;
is allowed; all of its locals (x, y, z, u, v, w) are dynamically-local
permanent variables, and it is thus the same as
vars x, y, z, u, v, w;
define old_style(x, y, z);
dlocal x, y, z, u, v, w;
...
enddefine;
Compared to a similar procedure using lvars, the above is (a) highly
non-modular, (b) generates much less efficient code, and (c) in a
batch-compiled environment will lead to much slower linking times (since
every permanent identifier corresponds to a global external symbol).
Second, +strict establishes the default declaration for top-level
defines as 'constant procedure' (note that without +strict, the form
define fred();
...
enddefine;
declares fred as an untyped variable). With Popc, it is important to
have as many procedures as possible declared as procedure-type
constants, both for efficiency reasons (a procedure identifier generates
much more efficient code), and because of restrictions on execute-level
cross-file references to variables (described below).
+strict also disallows the vars default for nested defines, forcing them
to be declared explicitly. (Except for the occasional dlocal or lvars,
all nested procedures should be lconstant.)
Finally, +strict causes all permanent identifiers to be automatically
declared global.
2.2 Effect on Writeability of Structures
-----------------------------------------
As described by Creating Layered Systems in REF * SYSTEM,
'non-specialised' data structures such as pairs, lists, strings, full
vectors, etc, can have a general writeable or nonwriteable default
specified for them when creating a saved image with sys_lock_system.
There is also a separate default for (user-created) closures.
The corresponding behaviour for Popc is controlled by the compile_mode
:popc options wrdflt and wrclos (see HELP * COMPILE_MODE). Since the
composite +strict mode includes both -wrdflt and -wrclos for Popc, an
additional effect of using
compile_mode :pop11 +strict;
in programs is to force a nonwriteable default for all such structures,
including closures. That is, Popc will compile execute-level instances
of these structures into nonwriteable areas of memory.
For batch-compiled programs, this has similar advantages to those
described in Creating Layered Systems: reduced garbage collection time,
greater shareability of the executable image, etc. But it does mean you
will need to apply writeable to any structures of this type that your
program requires to update.
Alternatively, you can add another compile_mode statement such as
compile_mode :popc +wrdflt +wrclos;
after the one for +strict (note that the two statements cannot be
directly combined, since they specify contradictory values for wrdflt
and wrclos).
---------------------------
3 How Popc Compiles a File
---------------------------
3.1 Compile Phase
------------------
Popc compiles a file by calling the ordinary Pop-11 compiler, but after
setting the system variable pop_pas_mode to the word "popc". This
variable having a true value causes the Poplog Virtual Machine (VM) to
divert VM code-planting and permanent identifier declarations to various
hook procedures, for which Popc provides appropriate definitions.
This is 'Popc compile mode': all compilation in this mode results in
procedures containing Popc's own representation of the code, which will
be interpreted if necessary, i.e. when code is actually executed at
compile-time.
... Shadow Values for Permanent Identifiers
--------------------------------------------
In particular, Popc-compiled code traps all assignments to permanent
identifiers, and stores them on a global list. This list is the basic
target of Popc's operation, and is the only source for generation of
output object code.
However, the 'real' identifiers in the underlying system are not
actually assigned to. This is necessary because Popc can be used to
compile source code that makes assignments to identifiers actually being
used by the underlying Pop-11 system (in which Popc is running); if the
real identifiers were assigned to, the operation of the underlying
system would be changed or corrupted.
The point is that the purpose of a Popc-compiled assignment is to set
the value of an identifier in the output object file (and hence in the
linked executable image), not to set its value in the Pop-11 system in
which Popc is running.
On the other hand, a Popc-compiled access to an identifier returns any
Popc value assigned to that identifier (if it has one -- otherwise the
current value of the real identifier is returned).
In summary, Popc maintains shadow values for permanent identifiers that
have been assigned to. The Popc shadow value of an identifier is also
accessed and updated by sys_current_val when Popc mode is in force.
... Use of Libraries
---------------------
Not all code compiled during Popc's operation will necessarily be
compiled in Popc mode. When compilation of library files is invoked,
either by autoloading or with uses-now, Popc mode is turned off. (Note
that an ordinary uses does not compile libraries -- see Use of uses-now
Instead of uses below.) Popc mode can also be explicitly turned off with
the syntax construct normal_compile ... end_normal_compile.
The principal problem that arises with ordinary-mode compilation of
libraries is that Popc runs in a Pop-11 system containing neither the X
nor Ved parts of Poplog. Attempting to compile libraries that use these
facilities will generally lead to multiple 'DECLARING VARIABLE ...'
messages, or mishaps. It is thus essential to restrict loading of
libraries to those situations where the code is actually required at
compile-time.
On the one hand, if a library is explicitly specified with uses-now,
Popc has no choice but to compile it (it is up to you to ensure that
only suitable libraries are so specified).
On the other hand, Popc will not attempt to autoload a library
identifier unless absolutely necessary. Moreover, in certain cases Popc
limits the library directories that are searched for autoloadable files,
as follows:
As in the normal interactive system, autoloading will happen only for an
identifier that is strongly referenced but not declared (or only weakly
declared). Unlike the normal system, however, autoloading will not
generally happen if a declaration for the identifier can be extracted
from a w-library. The exceptions to this are where
(a) the identifier is a syntax word or macro (and therefore needs to
be executed immediately);
(b) the value of the identifier is actually accessed (by
execute-level code).
In case (a), the assumption is made that (being syntax or macro) the
library will not contain any 'hard' references to Ved or X facilities
(the section Syntax, Macro & Other Compile-Time Executed Procedures
below discusses this in more detail). Therefore the full set of
popautolist directories are searched, as normal. The case where the
identifier is completely undeclared has to be treated similarly (since
it might be syntax or macro).
However, in case (b) (that is, where a non-syntax/macro declaration is
available), it is in many circumstances good enough to simply allow the
undef value of the identifier to be accessed, without attempting to
autoload it. (The reason for this is explained more fully under Value
Generation Phase below; basically, providing the undef value is not
executed or otherwise used in some computation, it will be correctly
interpreted during generation of output code.)
Hence in this case, only a restricted set of directories ($popautolib +
$poplocalauto) are searched, instead of the full popautolist. While this
prevents many potentially problematic (i.e. Ved and X) identifiers being
autoloaded unnecessarily, it does means that uses-now will be needed for
a small number of library identifiers which are not syntax/macro, but
which nevertheless can be successfully used at execute-level with Popc.
(The X procedures XptSpecifyResourceInfo and XptSpecifyPopValueTypes are
examples of this.)
... Testing for Popc
---------------------
The definition
lconstant macro POPC_COMPILING = true;
is automatically added to the beginning of every source file. This
enables a file to use
#_IF DEF POPC_COMPILING
...
#_ENDIF
etc, to test for being compiled with Popc (either in or out of Popc
mode).
Occasionally, some syntax and macro code may need to behave differently
when being run in Popc compile mode. In these cases the test
if pop_pas_mode == "popc" then
...
endif
is appropriate.
3.2 Value Generation Phase
---------------------------
After compiling a file, Popc works through the list of assignments to
permanent identifiers, and generates object (assembler) code for each
value assigned. The output assembler file is then translated to an
object file with the operating-system assembler.
Permanent assignments are the only source for code generation.
Assignments to lexical identifiers will be ignored unless they are in
some way referenced by structure(s) assigned to permanent identifier(s).
... Structures Generated by Poplink
------------------------------------
In a Pop-11 program, certain data structures must be unique across the
system as a whole: for example, the dictionary word "abcd" must always
be the same object, wherever referred to. Generation of such structures
cannot be done by individual object modules, since there is no way of
deciding that one module rather than another should do the job.
Thus (as described in Overview above), a linking process is required, to
generate these structures centrally: this is the purpose of w-files and
Poplink. The structures which are always generated by Poplink are:
# dictionary words
# permanent identifiers
# permanent word-identifiers
# anonymous undef values (having a false undefword)
# the unique objects in false, true, and nil
(Poplink also generates other structures when these are the values of
incremental identifiers. See Incremental Identifiers below.)
... Constants vs Variables
---------------------------
A constant identifier can only be assigned a value in one file, and a
(strongly-referenced) constant must always have a value. Poplink treats
a violation of either of these conditions as an error.
On the other hand, Poplink allows some leeway in assigning values to
vars. For example, an assignment for a variable in an object file is
allowed to override one in a library module; also, a variable may be
unassigned (and default to an undef value).
These (and other) considerations mean that, while the Popc/Poplink
system supports inter-file values for constants, it does not support
inter-file values for vars. In practice this means the following:
Except for the Poplink-generated structures listed above, the
only compile-time cross-file references allowed are to the
values of constants. Thus the values of constants are
compile-time accessible between files, but the values of
variables are not.
Note that this restriction does not concern run-time accessing of
variable values; such accesses (i.e. in procedure code), will always be
to variable identifiers (Poplink-generated structures). It concerns only
Popc's generation of object code from the values in the permanent
assignment list -- that is, 'static' references between files resulting
from compile-time executed code.
... Value-Generation Errors
----------------------------
To use Popc successfully, you need to know how the above restriction
operates in practice, and how it relates to errors of the form
NON-GENERATABLE VALUE (optional-qualifying-message)
INVOLVING: <object>
which may occur during Popc's value generation phase.
When presented with a ("full") location containing a value to generate,
Popc does one of three things:
(a) If the value is a simple object (integer or decimal), it
generates the appropriate bit pattern in the location;
(b) If the value is a Poplink-generated structure, OR if there is a
constant identifier in the dictionary whose idval is the object,
it assumes that the structure is generated elsewhere, and merely
outputs a reference to it (that is, outputs its corresponding
external symbol);
(c) Otherwise, it actually generates the structure -- that is,
allocates memory for it in the current output file (and
recursively generates its sub-structures, etc).
Note that (b) includes the case where the object is a named undef record
contained in some constant identifier in the dictionary. Such a value
may have resulted by accessing the value of a constant which has been
declared but not assigned to: this is the way in which constants from
other files are usually dealt with (hence Popc does not need to know the
actual value of the constant).
For example, if baz is a constant procedure defined in another file,
then the execute-level code
constant procedure baz;
define foo = baz(% 1, 2, 3 %) enddefine;
will result in the Popc shadow-value for foo being a closure whose
pdpart is <undef baz>; this will be mapped correctly to the external
symbol for the constant baz.
On the other hand (because of the restriction on variables described
above), Popc will refuse to generate a named undef record that is the
idval of any variable in the dictionary. Thus if baz were instead
declared
vars procedure baz;
the error
NON-GENERATABLE VALUE (undef value of perm variable)
INVOLVING: <undef baz>
would result.
For other structures (and where no suitable constant can be found), case
(c) comes into play, i.e. Popc actually generates the structure. The
following kinds of structure cannot be generated, however:
(1) Proper procedures that have not been compiled in Popc mode;
(2) Currently, instances of shadowclass structures (but note that
shadowkeys are OK);
(3) Currently, XptDescriptor structures;
(4) System devices (user devices are OK);
(5) Processes.
For example, (1) will give an error like
NON-GENERATABLE VALUE (procedure not compiled in popc mode)
INVOLVING: <procedure>
In general, an error of the form
NON-GENERATABLE VALUE (optional-qualifying-message)
INVOLVING: <object>
means that the object is not a generatable structure, and moreover, is
not the value of any constant in the dictionary. Note that a value does
not have to be a generatable structure, providing it can be mapped to a
constant; this enables a structure such as
vars list = [% hd, tl, lmember %];
to be generated, even though the accesses to hd, tl and lmember will
result in the list containing the actual Pop-11 system procedures
(which, of course, are not Popc-mode compiled). Compare, however
vars list = [% hd, tl, member %];
This will give
NON-GENERATABLE VALUE (procedure not compiled in popc mode)
INVOLVING: <procedure member>
because the procedure member is not a constant (unfortunately).
... Restriction on Constant Values
-----------------------------------
Another restriction not covered by the last section is the following:
You cannot assign a constant from another file to be the direct
value of a constant in the current file.
For example, if baz is defined in a file other than the current, the
following is invalid:
constant procedure baz;
constant procedure foo = baz;
and will result in the error
INVALID INITIALISATION FOR PERM CONSTANT foo
This only concerns setting foo directly equal to baz; there is no
problem when baz is some sub-element of a structure being assigned to
foo (that is, when the reference to baz is in a memory location). There
is also no problem when setting a variable equal to a constant, e.g.
vars procedure foo = baz;
is correct. (Once again, the reference to baz will be in a memory
location.)
You can, however, define a constant to have any Poplink-generated
structure as its value. E.g.
constant
XXXX = true,
a_word = "abcd",
baz_id = ident baz,
a_list = [];
are all valid.
(The above restriction arises because of the inability of O/S object
code to set the value of one external symbol equal to another, where the
other is not defined in the current file. Note that it does not apply to
lconstants, because these have scope only in the current file and hence
do not need to define external symbols.)
------------------------------------------
4 Changing Pop-11 Files to Work with Popc
------------------------------------------
Many Pop-11 programs that compile and work correctly in the normal
interactive Poplog system will require changes to compile under Popc.
However, these changes can always be made in such a way that the end
result will work with either system.
The main respects in which programs will require changing can be
summarised as:
Permanent Identifier Declarations
All permanent identifiers must be declared before being
referenced. All declarations must be consistent, both within and
across files.
Use of uses-now instead of uses
uses for libraries that are required at compile-time must employ
uses-now instead.
Compile-Time Executed Code
There are certain restrictions on the code that be executed at
compile-time.
Keeping Compile-Time and Run-Time Code Separate.
Permanent syntax words and macros should not be mixed with
run-time code in the same file.
These areas are dealt with in the following sections.
4.1 Permanent Identifiers: Declarations & References
-----------------------------------------------------
In a file compiled with Popc, all user permanent identifiers must be
declared before being referenced. An undeclared identifier will result
in the error
UNDECLARED IDENTIFIER xxxx
Moreover, any re-declaration of an identifier must be identical to its
existing declaration (in terms of constant/vars and identprops),
otherwise the error
REDECLARING STATUS OF PERM IDENTIFIER xxxx
results.
If an identifier is referenced with weakref in a file, it must be
referenced everywhere in that file with weakref, i.e. all references in
a single file must be either weak or strong. Referring to an identifier
both weakly and strongly in the same file will produce the error
MIXED WEAK/STRONG REFERENCES TO IDENTIFIER xxxx
(Note, however, that Popc does not distinguish between strong and weak
declarations: you may declare an identifier as either -- although it may
generally be clearer to use weak for a weakly-referenced identifiers,
etc.)
Only identifiers actually assigned to at execute-level or referenced in
procedure code are recorded on the output w-file; identifiers which are
declared but not used anywhere are simply ignored.
Both Poplink and Poplibr also enforce the consistency of identifier
declarations across files: all permanent identifiers must have identical
declarations in all files. You cannot declare an identifier as constant
in one file, and vars in another, or as procedure-type in one file, but
untyped in another, etc. (An identifier may, of course, be referenced
weakly in one file and strongly in another.)
... Poplog System Identifiers and popsyslist
---------------------------------------------
You do not need to supply declarations for Poplog system identifiers. As
described in Overview above, Popc automatically extracts declarations
for these from the system w-libraries, that is, .wlb files in the
directory $popobjlib.
However, which of these files are actually inspected is controlled by
the current value of * popsyslist (which contains * popuseslist as a
sub-list). The names of files in $popobjlib just replicate the directory
pathnames in which the corresponding source files reside, e.g.
$usepop/pop/src/ --> src.wlb
$usepop/pop/lib/auto/ --> libauto.wlb
$usepop/pop/x/ved/src/ --> xvedsrc.wlb
Using this name mapping, Popc will search the .wlb files corresponding
to the directories in popsyslist. Thus for example, a program that
normally requires
uses popxlib;
to extend popautolist and popuseslist with the X library directories,
will require the same with Popc. (But note that for this to function
correctly, you must have
uses-now popxlib;
instead. See Use of uses-now Instead of uses below.)
If you have your own w-libraries, Popc can be told to use these for
declaration-extraction with the -wlib option. (You can also prevent the
use of popsyslist in this respect with the -nosys option.) See
HELP * POPC.
... Multi-File Programs
------------------------
The problem of declarations will mainly affect multi-file programs,
which when loaded in the ordinary Poplog system usually rely on
previously-compiled files supplying declarations for later ones. This
technique does not work with Popc since each file is compiled
individually. (This applies even when compiling a number of source files
in a single run, e.g. in
popc -c foo.p baz.p ...
all declarations for baz.p and subsequent files must be supplied as if
each were being compiled alone.)
(However, for special uses Popc provides the -g option, which enables a
group of files to be compiled to a single object module. With this, the
source files are effectively just concatenated, and so identifier
declarations in earlier files are valid for references in later ones.)
... Record and Vector Classes (defclass-declare)
-------------------------------------------------
If you use defclass to define a (permanent) record or vector class in
one file, and wish to use one or more of its associated identifiers in
other files, then you must provide declarations for those identifiers in
the other files.
This can be done using ordinary declarations, e.g. if file 'foo.p'
defines a class
defclass fred {fred_f1 :full, fred_f2 :full};
then file 'baz.p' can make use of the associated procedures by declaring
them appropriately:
constant fred_key;
constant procedure (consfred, destfred, fred_f1, fred_f2);
(Note that +strict is assumed here, i.e. that defclass will default to
constant, with type procedure for all the identifiers except fred_key).
There is, however, one situation in which the above is not sufficient:
namely, where a file (other than that defining the class) uses the
constructor procedure to make an instance of the class at compile-time.
For instance, baz.p might contain something like
lconstant a_fred = consfred(1,2);
In this case, merely having consfred declared is clearly not sufficient;
the proper constructor procedure must actually be available.
In order to achieve this, note that you cannot simply replicate the
ordinary definition of the fred class in baz.p, because that would cause
all the associated identifiers to be multiply-defined at link-time (only
one file may actually define the class). Instead, use defclass-declare:
defclass-declare fred {fred_f1 :full, fred_f2 :full};
This is identical to an ordinary defclass, except that all the
identifiers -- while declared in Popc mode -- have their values assigned
in non-Popc mode. Thus as far as Popc is concerned, they are declared
but not initialised; instead, the real identifiers in the underlying
system are assigned to (making the constructor procedure available for
execution).
(Note also another point concerning this 'instance-constructing' case.
As with any other structure, the instance will be generated by Popc as
described in Value Generation Phase above. You might think the key field
of the structure would somehow be special, but in fact, it follows
exactly the same rules as for any other field: Its value is fred_key --
a structure defined in another file -- and so fred_key must be a
constant. Thus you cannot construct compile-time instances of a class
from another file for which the key identifier is a variable.)
While defclass-declare is essential in the instance-constructing case,
whether you use it as a substitute for simple declarations in other
cases is a matter of taste.
... Other Declarations (declare_incremental and declare_updater)
-----------------------------------------------------------------
Popc provides two special declarations for permanent identifiers which
you may need to use (usually with multi-file programs):
declare_incremental and declare_updater. Both of these declarations are
also available in ordinary Pop-11 (where they essentially do nothing).
The first (declare_incremental) is required where a program needs to
build up some global structure (such as a list) across several files --
that is, in the normal interactive system the structure is initialised
to an empty state in (say) the first file loaded, after which later
files add increments to it.
This technique is supported with Popc, but only for lists, procedures,
and properties. See Incremental Identifiers below for full details.
The second declaration (declare_updater) may be required when a constant
procedure from one file is used (for example) as the pdpart of a closure
in another. E.g.
constant procedure baz; ;;; defined in some other file
define foo = baz(% 1, 2, 3 %) enddefine;
When generating code for the closure foo, Popc has no a priori way of
knowing whether the pdpart procedure baz has an updater or not, and thus
whether foo should have one: the default is to assume not. Hence if baz
does have an updater (and you want foo to have one), then
declare_updater baz;
is needed (following the ordinary declaration of baz).
See * declare_updater below.
... -include Option
--------------------
To make it easy to supply identifier declarations that are not required
for ordinary interactive compilation, Popc provides the -include option.
This automatically #_INCLUDEs a file of declarations in every file
compiled, e.g.
popc -c -include popc_declare.ph foo.p baz.p ...
will #_INCLUDE 'declare.ph' at the beginning of every source file.
This and other initial arguments to Popc can be conveniently specified
for all source files in a given directory by means of a 'popc.args' file
in that directory (see HELP * POPC).
4.2 Use of uses-now Instead of uses
------------------------------------
In Popc compile mode, an ordinary uses statement such as
uses foo, baz, ... ;
does not actually cause any files to be loaded. Rather, it merely
records that the identifiers foo, baz, ... have been 'used'. At link
time, this will force the extraction of the library modules defining the
identifiers even if they have not actually been referred to anywhere in
the current (or any other) file.
This behaviour means, however, that an ordinary uses is inappropriate
for a library which is actually required at compile-time -- that is, one
which (for example) defines syntax words or macros, or which extends
popuseslist (such as LIB * POPXLIB). In these cases, uses-now must be
employed:
uses-now popxlib, Xm, ... ;
This is effectively the same as surrounding an ordinary uses with
normal_compile ... end_normal_compile, i.e. Popc compile mode is
temporarily turned off, and the files are compiled as normal. (In normal
interactive compilation, uses-now is the same as uses.)
4.3 Restrictions on Compile-Time Executed Code
-----------------------------------------------
Almost all run-time executed code inside procedures will work unchanged
with Popc, providing permanent identifiers are declared correctly. The
majority of problems in converting files for Popc will occur with code
executed at compile-time; that is, either execute-level code (code
outside of procedures, lconstant expressions, code inside #_< ... >_#
etc), or syntax and macro procedure code which gets executed when the
syntax word or macro is run during compilation.
(Note that while execute-level code will always be compiled in Popc
mode, syntax/macro code may be compiled either in Popc mode, or in
ordinary mode when from a library.)
The principal problems you will encounter will result from the errors
already described in Value Generation Phase above. In summary, they are:
# You cannot reference the values of permanent variables assigned in
other files (including system ones).
Or rather, you can reference them, but the value generation
phase will not be able to generate their values. Probably the
most frequent problem in this respect is attempting to create
closures of variable procedures, e.g.
define is_a_member =
member(% [a list of words] %)
enddefine;
This will fail because the procedure member is not a constant
(using lmember_= instead would be valid, because that is). The
solution in such cases is to define a proper procedure, i.e.
define is_a_member(item);
lvars item;
member(item, [a list of words])
enddefine;
# You cannot set one permanent constant directly equal to another
(unless the latter's value has already been assigned in the current
file).
For example,
constant procedure foo = hd;
is invalid. In this case, a dummy closure could be created:
define foo = hd(%%) enddefine;
# Popc runs in a Pop-11 that contains neither the Ved nor X parts of
the system.
Thus (with the exception of associated syntax procedures and
macros) you cannot execute any Ved or X procedures at compile
time.
(Note that there are (at least) two X library procedures,
XptSpecifyResourceInfo and XptSpecifyPopValueTypes, which are
not syntax or macro, but which can nevertheless be used at
compile-time since they do not make any 'hard' references to X
facilities (that is, they only refer to them 'by name'). As
described in Use of Libraries above, you need uses-now to load
these procedures.)
# Certain structures are not generatable.
Currently, this involves instances of shadowclass structures
(shadowkeys are OK however), and XptDescriptor structures. To
surmount such problems, generate the structures at run-time,
either with a runtime action or in some other way.
For example, LIB * XptArgList requires a constant zero-length
XptArgList structure (a shadowclass instance). This cannot be
defined as
lconstant empty_arglist = consXptArgList(0);
since the structure is not generatable; instead it must use a
top-level lvar initialised to false, which then caches the
structure the first time it is used:
lvars empty_arglist = false;
define XptArgList(list);
lvars list, size;
if list == [] then
empty_arglist or
(consXptArgList(0) ->> empty_arglist), 0;
...
A similar effect could be achieved with a runtime action:
lvars empty_arglist;
define :runtime_action;
consXptArgList(0) -> empty_arglist;
enddefine;
define XptArgList(list);
lvars list, size;
if list == [] then
empty_arglist, 0
...
4.4 Syntax, Macro & Other Compile-Time Executed Procedures
-----------------------------------------------------------
In this section, compile-time procedure (CTP) means a syntax or macro
procedure, or any other procedure that could be executed in a
compile-time context.
... General
------------
The points in the last section apply to the code planted or generated by
all CTPs, whether lexical or permanent identifiers.
CTPs of any kind should not make any direct references to Ved or X
run-time procedures, or any other facilities that are not present in a
basic Pop-11 system (because such code will not load inside Popc). All
such references should be 'by name', that is:
# For VM code-planting, always use sysPUSH and sysCALL on a quoted
word or word-identifier rather than using sysPUSHQ or sysCALLQ;
# For other contexts, access an object by using sys_current_val
applied to the name as a quoted word/word-identifier.
In fact, references in CTPs to any run-time facilities should generally
use the above -- for example, a particular library run-time procedure
may be loadable in Popc, but referring to it by name rather than
directly will obviate the need to load the procedure at compile-time.
... Permanent
--------------
There are additional factors to bear in mind when writing permanent
compile-time procedures (PCTPs).
If you are making a CTP a permanent identifier, you are doing so
(presumably) because you want it to be usable in more than one file.
Since with Popc, each file is compiled individually, you cannot rely on
the PCTP having been loaded from some previous file (as often happens in
ordinary Pop-11). Thus
(a) the file containing the PCTP definition must either be
autoloadable, or be loaded explicitly with uses-now by every
file that uses it;
(b) as described in Use of Libraries above, this will load the file
in non-Popc mode; hence the file defining the PCTP must be
capable of being loaded (in non-Popc mode) inside Popc.
As usual, (b) means that the file containing the PCTP definition must
not make any direct references to Ved or X run-time facilities, etc.
This applies both to the PCTP itself, and anything else that may be in
the same file. If the PCTP needs to reference these facilities, the
references must always be via quoted words or word-identifiers. (For
examples of this, see LIB * vedset, * XptVal; these are two syntax
constructs which plant calls to Ved and X procedures respectively, but
always using sysCALL on procedure names, never sysCALLQ on the
procedures directly.)
Use of quoted words/word-identifiers in this way is not a problem for
syntax constructs, macros, etc, because such things usually expect to
have to use the names of identifiers, and efficiency issues are not
involved. (For instance, a sysCALL on the name of a constant will result
in the same code as a sysCALLQ on its value.)
On the other hand, run-time code will generally want to make 'hard'
references to identifiers (and not refer to names) for efficiency
reasons. Hence including other, run-time, procedures in the same file as
a PCTP may compromise the latter's ability to load inside Popc.
The above considerations lead to this general rule:
Do not mix permanent syntax and macro definitions (and other
procedures that need to be executed at compile-time) with
run-time code in the same file -- keep the two completely
separate.
A special case of the above comes where a PCTP (such as a syntax word)
plants code for run-time procedure(s) that are specific to itself.
Because they are used only by a particular PCTP, such procedures are
often defined as lconstants in the same file. Although violating the
rule above, this in itself would not necessarily prevent the PCTP
loading in Popc (it would depend on what the lconstant procedure(s)
contain); however, the practice will not work anyway, for another
reason:
As described in Value-Generation Errors above, proper procedures that
have not been compiled in Popc mode cannot be generated in the output
object file. But (when used in a file other than its own), the PCTP file
will be compiled in non-Popc mode; thus all the procedures it contains
will be non-generatable.
To avoid this problem, run-time procedures for which calls are planted
by PCTPs must always be permanent identifiers. Popc will then merely
make the planted calls reference the corresponding identifiers without
attempting to generate the procedures they contain. (Note that even if
Popc could generate lconstant procedures from the (non-Popc-mode
compiled) PCTP file, this would be highly undesirable anyway, since then
every file using the PCTP would have its own private copy of those
procedures.)
Providing the run-time procedures are permanent identifiers, there is
nothing to prevent them being in the same file as the PCTP, assuming
they do not reference Ved or X facilities. However, this is always
undesirable for yet another reason: being in the same object module, the
run-time parts cannot be linked separately from the compile-time parts.
(Not only will linking the run-time parts into a program force the
compile-time parts to be included as well, but the latter will often use
system compiler procedures, which in turn will have to be linked in.)
Thus a linked program could end up containing large amounts of code it
does not actually require -- yet another reason for adhering to the rule
above.
Note that both the example syntax procedures mentioned already (i.e.
LIB * vedset, * XptVal) also contain instances of such 'own' run-time
procedures that are permanent constants defined in separate files
(LIB * vedset_pcompose and * XptValTestTypedArg respectively). For
ordinary Pop-11 use, there is no reason why these procedures should not
simply be defined as lconstants in their respective syntax files; but
they have to be permanent identifiers, and in separate files in order to
work with Popc.
... Lexical
------------
By definition, lexical CTPs (e.g. lconstant syntax and macro) can only
be used in the files they are defined in, that is, can operate only when
Popc is being used to directly compile their files. Hence any lconstant
run-time procedures they employ must also be compiled in Popc mode, etc.
Unless specifically referenced in a non syntax- or macro-expanding
context, lexical compile-time code will not be output on the object file
at all: thus the issue of mixing it with run-time code in the same file
does not apply.
-----------------------------
5 Additional Syntax For Popc
-----------------------------
5.1 Incremental Identifiers
----------------------------
A technique commonly used in multi-file programs is to build up global
data structures across a number of files. In the normal interactive
system, a structure (such as a list) may be initialised to an empty
state in (say) the first file loaded; later files then add increments to
the structure, as appropriate. A permanent identifier containing a
structure created in this way is called an incremental identifier.
In an environment where each file is compiled individually, incremental
identifiers necessitate a global linking process. This is so because an
individual object module can do no more than record its particular
contribution to each structure; the final structures must be assembled
from the various parts when the program is linked.
Popc provides support for incremental identifiers containing one of the
following types of structure:
# lists
# procedures
# properties
The basis of the facility is the declare_incremental declaration, which
declares a permanent identifier as being incremental and of one of the
three types listed above, viz: incremental list, incremental procedure
or incremental property. (Note that the identifier must first be
declared in the normal way, i.e. declare_incremental does not
subsume the basic constant/vars declaration.)
When a permanent identifier is declared incremental, Popc records a
top-level assignment to it as being merely a part of the overall
structure in the identifier. At program link time, various object
modules containing increments for the identifier may have been
incorporated in the program; Poplink then assembles the final structure
from all the parts included.
Note that various system identifiers are incremental, e.g. popautolist,
popuseslist and pop_runtime_actions (lists), popexit (procedure), and
vedgetsysfilepdr (property). When Popc extracts declarations for these
identifiers from system w-libraries, it also recovers the corresponding
incremental declarations, thus allowing user files to define additional
increments to the standard values of these identifiers.
... Syntax of declare_incremental
----------------------------------
declare_incremental [syntax]
This construct has the form
declare_incremental
list [attributes] (perm_identifier, ... ),
procedure [attributes] (perm_identifier, ... ),
property (perm_identifier = propexpr, ... ),
;
The body may contain repeated clauses, each beginning with one
of the keywords list, procedure or property, separated from the
next keyword clause by comma, or terminated by semicolon.
For list or procedure, each keyword clause consists of either a
single perm_identifier name, or a comma-separated list of them
in parentheses. The identifiers may be optionally preceded by a
comma-separated sequence of attributes in square brackets [...],
where each attribute is one of
prec=integer
sublists
writeable
For prec, integer is a positive or negative integer in the range
-16:8000 to 16:7FFF. E.g.
declare_incremental list [sublists,prec= -200] (foo, baz);
(N.B. A negative integer currently requires a space between the
= and the minus sign.)
A property clause consists of either a single
perm_identifier = propexpr
spec, or a comma-separated list of them in parentheses (no
attributes are allowed). In each case, propexpr is any Pop-11
expression which evaluates to a property. E.g.
declare_incremental property (
xxxx = newassoc([]),
yyyy = newproperty([], 8, false, false),
);
The semantics of declare_incremental are explained in the
following sections.
... Assignment Code: Basic Initialisation
------------------------------------------
After its (first) declaration with declare_incremental, the Popc shadow
value of an incremental identifier is set to its corresponding 'empty'
value, namely [] for lists, identfn for procedures, and the evaluated
propexpr for properties.
This allows execute-level code in a file to add to the identifier, i.e.
to add further elements to its 'existing' value (as if it had one). For
example, if incr_list is an incremental list, then the code
[abcd] <> incr_list -> incr_list;
'adds' the word "abcd" to the list. It is important to note however,
that (even though the end result may appear the same), Popc
distinguishes the above from
[abcd] -> incr_list;
which -- by virtue of the fact that it does not access the existing
value of the identifier before assigning to it -- is deemed to provide
the basic initialisation for the identifier.
The basic initialisation for an incremental identifier is like an
ordinary initialisation for a non-incremental identifier (only one file
can make the initialisation, etc). It is 'the part that should always be
present', as opposed to additions by other modules, which are 'optional
parts' (i.e. will be added only if the modules have been incorporated
in the linked program for other reasons).
... Lists
----------
An incremental list identifier must always be a variable (this applies
also to incremental procedures, but not to properties -- see below).
After declaration with declare_incremental list ..., the Popc shadow
value of the identifier is set to []; thus execute-level code that adds
to the identifier, e.g.
declare_incremental list incr_list;
"abcd" :: incr_list -> incr_list;
incr_list <> [abcd] -> incr_list;
will work as expected.
The linked value of an incremental list is a list whose elements are the
element(s) from each object module.
You can use the prec (precedence) integer attribute with different
values in different files to control the order in which elements from
the various object modules will appear in the final list. The increments
from different modules are sorted in numerical prec order, with the
lowest (most negative value) coming first. Note that if prec is not
specified in a particular module then it defaults to 0 (thus negative
values will come before any defaults, and positive values after them).
Elements from each individual module will appear in the final list in
the same order they appeared in the module. However, if the sublists
attribute is specified in a module, then all the elements from that
module will appear as a single sub-list of the final list (rather than
as separate elements).
The writeable attribute controls whether the final list is put into
writeable memory: if writeable is present in any module, the list is
made writeable.
... Procedures
---------------
As with lists, incremental procedure identifiers must be variables.
After declaration with declare_incremental procedure ..., the Popc
shadow value of the identifier is set to identfn; thus execute-level
code that adds to the identifier, e.g.
declare_incremental procedure incr_pdr;
pdr1 <> incr_pdr -> incr_pdr;
will work as expected.
The linked value of an incremental procedure is a procedure which
executes in turn the procedures from each object module. (Currently,
Poplink implements this as a closure
appdata(% procedure_vec, fast_apply %)
where procedure_vec is a full vector containing the component
procedures.)
As with lists, the prec integer attribute controls the order in which
procedures from the various object modules will appear in the final
procedure.
... Properties
---------------
Incremental properties are slightly different from the other two types,
since the identifier itself contains a value (a property procedure)
which is independent of whatever increments are actually supplied to
build the property (its entries). For this reason, the identifier may be
constant or variable, as desired (it may also be procedure-type).
After declaration with declare_incremental property ..., the Popc shadow
value of the identifier is set to the evaluation of the Pop-11
expression supplied (which must, of course, produce a property). Thus
execute-level code that adds to the identifier, e.g.
declare_incremental property incr_prop = newassoc([]);
27 -> incr_prop("abcd");
28 -> incr_prop("efgh");
will work as expected. Note that whereas with list and procedures, a
file is deemed to initialise an identifier if it assigns to it without
accessing it first, in the property case this is done by explicitly
assigning a property to the identifier. For example, if the above
assignments are replaced by
define incr_prop =
newassoc([[abcd 27] [efgh 28]])
enddefine;
etc, then the entries in incr_prop will be taken as its basic
initialisation.
There is no precedence relationship for a property; the increments
generated by each object module are simply a set of entries, which are
arranged inside the property in the usual way.
The property expression supplied to declare_incremental for each
identifier may be any kind of property. However, it must be capable of
evaluation in an arbitrary Pop-11 context (since incremental property
declarations from library modules must be resurrectable in any
environment). This means (for example) that the property's default or
equal_p values can only be standard system values/procedures.
The same expression must be used for an identifier in all modules
(Poplink treats any discrepancy between modules as an error).
5.2 Building W-Libraries
-------------------------
As described in Overview above (and also by Use of W-Libraries in
HELP * POPC), each w-file records which w-libraries were used for
extracting permanent identifier declarations when it was compiled. Thus
every w-file knows which libraries it needs when linked as part of a
program.
When compiling a set of source files with which to build a new
w-library, you cannot (of course) rely on this mechanism to deal with
identifier references between the source files of the new library (for
the fairly obvious reason that the w-library cannot be used for
extracting declarations until after it is built).
As described in earlier sections, therefore, you must supply explicit
declarations for all permanent identifiers referenced across the source
files of the library. However, to enable each w-file that references
other library parts to record that it 'uses' this library, the syntax
construct library_declare_section is provided: this informs Popc that a
set of identifiers belong to a particular library (even though it
doesn't yet exist).
Essentially, library_declare_section is a 'forward' declaration for
w-libraries. It is also required in the more complicated situation of
building a whole collection of libraries which use each other
recursively (as with many of the Poplog libraries). In this case, the
Popc -wlib option can be used to make available for
declaration-extraction those libraries that have already been built,
while library_declare_section is used for those that have yet to be
built (including the one currently being compiled).
library_declare_section [syntax]
This construct has the form
library_declare_section w-library-name-string
statement-sequence
end_library_declare_section;
where statement-sequence is any sequence of execute-level
statements. Its effect is to mark all permanent identifiers
declared inside statement-sequence as belonging to the w-library
given by (the quoted string) w-library-name-string.
w-library-name-string follows the same rules for specifying
w-library names to Popc, Poplink and Poplibr, as described in
Object & Library File Arguments in HELP * POPC.
Note that if w-library-name-string contains an environment
variable/logical name, it will NOT be expanded when the name is
stored on w-files. This is important, since it means environment
variables will remain unexpanded until the w-libraries are
actually used at link-time, allowing their locations to change.
(For example, all Poplog libraries are referenced by names
starting with (at least) $usepop/, and others as $popautolib/,
$popliblib/, etc.)
5.3 Other
----------
declare_updater [syntax]
Where a permanent identifier contains an undef procedure defined
in some other file, this declaration informs Popc that the
procedure has an updater. The format is
declare_updater perm_identifier, perm_identifier, ... ;
It is usually required only when a constant procedure from one
file is used as the pdpart of a closure in another. E.g.
constant procedure baz; ;;; defined in some other file
declare_updater baz; ;;; say it has an updater
define foo = baz(% true %) enddefine;
When generating code for the closure foo, Popc will generate an
updater for it in the normal way (i.e. to call the updater of
baz). Without the declare_updater, baz is assumed not to have an
updater, and so the closure foo will not be given one either.
(Note that declare_updater declarations are not required for
system procedures, since the information is extracted
automatically from system w-libraries.)
In ordinary Pop-11, this declaration merely checks that each
perm_identifier contains a procedure with an updater.
normal_compile [syntax]
This construct allows code to be executed in non-Popc mode while
compiling a file with Popc. Its format is
normal_compile
statement-sequence
end_normal_compile;
It locally sets pop_pas_mode to its usual value of false, and
then does
pop11_exec_stmnt_seq_to("end_normal_compile") -> ;
i.e. execute statements until the closer is reached. (In
ordinary Pop-11, this is therefore effectively a no-op.)
------------------
6 Program Linking
------------------
6.1 Exporting of Identifiers and Sections
------------------------------------------
One of the functions performed by Poplink is to generate word records
for all quoted words used in a program, and to arrange them in the
Pop-11 dictionary.
In the normal Pop-11 system, when you declare a permanent identifier it
automatically becomes associated with the word corresponding to its name
(either at top-level or inside a section). This makes the identifier
accessible from the dictionary, either for further program compilation,
or for use with valof.
When Poplink links a program it assumes that in general, no more
compilation will take place. Hence by default, the only permanent
identifiers 'exported' to the dictionary are those for which the
corresponding quoted word was used somewhere in the program. (This
allows valof to be used on such words; note however, that it does not
cope with the case of a program constructing words at run-time with
consword.)
Exporting of other identifiers must be requested explicitly, in one of
two ways:
(1) By section, with the -s option to Poplink (see HELP * POPC);
(2) By individual identifier, either with uses-by_name in program
files, or by Poplink options -ubn and -idexp etc. (See the
description of uses-by_name in HELP * uses.)
A section is generated in the section tree either when an explicit -s
option is given for it, or when one or more identifiers in that section
are exported.
6.2 Run-Time Autoloading of Identifiers
----------------------------------------
In addition to using valof on quoted words and word identifiers, some
programs and facilities also rely on being able to autoload library
identifiers at run-time (i.e. before accessing them with valof). In a
standalone linked program this is probably undesirable, since it
requires that the corresponding library source directories and files be
present in the program's execution environment. It also requires the
incorporation into the linked program of sys_autoload, together with the
whole Pop-11 compiler and Poplog Virtual Machine.
Apart from those parts directly connected with the Pop-11 compiler, all
system and library code that uses valof makes only weak references to
sys_autoload (including valof itself). If sys_autoload is only weakly
defined, these facilities will not attempt to use it. Thus, if you do
require your program to perform run-time autoloading, you must include
uses (sys_autoload, pop11_compile);
somewhere within the code.
Without this, run-time autoloading will not occur. You must then ensure
that all library identifiers to which valof may be applied are included
at link-time, and exported to the dictionary. The best way to do this is
with uses-by_name statements in your program.
For example (as described in Class Apply Of Descriptors in
REF * XptDescriptor), the general apply mechanism for the XptDescriptor
datatype constructs the name "XpttypeApply" when given an XptDescriptor
to apply, and then calls the apply procedure got from valof applied to
the name; thus (e.g), applying a Widget will call XptWidgetApply. In the
normal system this would be autoloaded, but autoloading cannot happen
without sys_autoload being present. Hence to apply a Widget, your
program must force the inclusion of the procedure with
uses-by_name (XptWidgetApply);
6.3 Weakly-Referenced Identifiers
----------------------------------
If at any point in the linking process all the references to a given
identifier are weak (made with weakref), Poplink will not search for
that identifier in libraries. For all identifiers that remain weak at
the end, Poplink 'plugs' references to them by generating appropriate
dummy structures.
To avoid generating the same dummy structure repeatedly, only one dummy
is produced for each different class of identifier, i.e. one for all
untyped ordinary identifiers, one for procedure-type ordinary
identifiers, one for active variables of each multiplicity M, and so on.
For constants, the dummies generated are anonymous undef records of the
appropriate type -- that is, all constants of a particular identifier
class are set to have their idval equal to the same undef record.
For variables, a single dummy identifier is generated for each class.
The idval of each of these identifiers is initialised to the appropriate
constant undef record (although you should never rely on a weak variable
having any particular value, because dummy program assignments to it may
alter the initial value).
6.4 testdef
------------
In the ordinary system (as described in REF * IDENT), the expression
testdef idname
is equivalent to the truthvalue of the expression
isdefined(ident weakref idname)
However, when a program is compiled and linked with Popc/Poplink, this
is so only in relation to the state at link-time. In order to make such
tests as efficient as possible, Popc compiles them to zero/non-zero
tests on the values of special external symbols, which are assigned
values by Poplink after the 'strengths' of the identifiers have been
determined at link-time.
Hence the truthvalues of testdef are permanently frozen, i.e. if an
identifier is weak at link-time, but then becomes defined at run-time, a
Popc-compiled testdef for it will remain unaffected.
6.5 Entry Procedure
--------------------
The -e option to Poplink specifies the name of the entry procedure to be
called on image startup; if omitted, it defaults to $-Pop$-Main (as for
the -entrymain argument to * MKIMAGE). The entry procedure must be
defined as a procedure-type constant.
On startup, arguments in poparglist0 and poparglist are set up as
described in General System Startup in REF * SYSTEM.
Saved image arguments (starting with +) are interpreted only if syssave
and/or sys_lock_system has been linked into the program (these will be
incorporated only if a strong reference occurs for either one -- both
bring in sysrestore automatically). You can use syssave or
sys_lock_system to save an image on top of your program image; such a
saved image will of course only restore into your program, not into
normal Poplog. If one or more + arguments are present, the saved
images(s) are restored and control is passed to the last one (i.e. the
syssave or sys_lock_system that created it will return true).
If no saved images are present, the specified entry procedure is called
(from inside setpop). Before doing this, or before handing control to a
saved image, special flag arguments in poparglist beginning with % are
processed in the normal way.
6.6 Embedding Pop in C
-----------------------
Popc-compiled code may be linked into and called from a C program. The
linking aspect is controlled by the Poplink options -exmain and -exlink,
which provide for either ordinary Pop linking with external startup
(-exmain), or independent external linking (-exlink). See External
Startup & Linking in HELP * POPC for details.
The interface for calling Pop procedures from C is the same as for
external callback, described in REF * EXTERNAL. Use the callback
function pop_get_ident to access procedures from Pop you wish to call
from outside. Note that you will generally need to specify uses-by_name
for such procedures, to ensure that they are exported to the dictionary
(otherwise pop_get_ident will not be able to access them).
... Initialising Pop
---------------------
Before any callback functions are called, the Pop system must be
initialised by calling pop_sys_init (this function is in a library
supplied by Poplink):
#include "callback.h" /* $popexternlib/callback.h */
int pop_sys_init(unsigned plog_nwords, unused);
As described in Callback and Prolog in REF * EXTERNAL, the Poplog Prolog
area cannot be expanded during a call from external code. When Prolog
routines are incorporated into a standalone-linked C program, similar
restrictions apply; moreover, the Prolog area cannot be located in its
normal position on the callstack, and must be allocated initially in
ordinary memory. The plog_nwords argument enables you to specify the
size of the area in words; it effectively assigns the (afterwards fixed)
value of pop_prolog_size. If plog_nwords is 0, the normal default size
is used.
The unused argument is reserved for future use, and should be passed as
0.
After initialisation, interrupt is set to sysexit; hence by default, any
mishaps occurring in Pop will cause image exit. However, a callback
function called from outside Pop always sets PEF_CATCH_ABEXIT_NEXT in
*pop_external_flags, so if you reset interrupt to setpop in your Pop
code, any mishaps or abnormal exits will simply result in the callback
function returning a 0 result.
... Unix Signals
-----------------
In a system where it has full control, Pop normally assigns handlers for
all Unix signals (so as to implement Ctrl-C interrupts, timers, child
process control, etc, etc).
A controlling C program may, however, wish to have its own signal
handlers, and not want Pop to interfere with them. The C variable
sigset_t * _pop_exclude_sigset;
is therefore provided; if set to a pointer to a signal set (see e.g.
UNIX * sigsetops), then * sys_reset_signal will not change the
underlying handler for any signal in the given set.
Needless to say, if you use this to prevent Pop setting handlers for
things like SIGALRM and SIGVTALRM, you can expect the corresponding Pop
facilities such as sys_timer to stop working. In terms of modularity
Unix signals are a total disaster, and there is no general solution to
the problem.
(No corresponding problem exists in VMS, where handlers for conditions
and timer interrupts etc are specified in a modular way.)
----------------
7 Miscellaneous
----------------
7.1 Allocation of External Symbols
-----------------------------------
When Popc generates object code, it allocates one or more global
external symbols to each permanent identifier. An identifier whose full
pathname is
$-foo$-baz$-grunge
will have associated external symbol(s) of the form
prefix.foo.baz.grunge
where prefix is 1-3 characters, e.g.
i.foo.baz.grunge (identifier record, when required)
c.foo.baz.grunge (value of a constant)
xc.foo.baz.grunge (execute label of a constant procedure)
uc.foo.baz.grunge (constant procedure updater)
xuc.foo.baz.grunge (execute label of constant procedure updater)
(N.B. On systems where '.' is not allowed in symbol names, '__' is used
instead.)
--- C.all/ref/popc
--- Copyright University of Sussex 1994. All rights reserved.Author: John Gibson Dec 1994