Iron Spring PL/I Compiler

Programming Guide

Version 1.3.0


Introduction

PL/I is a powerful programming language suitable for a wide range of problems. PL/I has been used to write operating systems and compilers, simulations, real-time programs, mathematical software, spreadsheets, GUI programs, and for general computing.

PL/I is widely used on mainframes and midrange systems today, but since the demise of Digital Research, Inc. has not been easily available on personal computers. Iron Spring Software was founded in 2007 with the intention of bringing the flexibility, reliability, and portability, and security of PL/I to a wider market.

Iron Spring PL/I is the leading cross-platform PL/I compiler supporting both Linux and OS/2. Both native and cross-compilation are supported.

This version of Iron Spring PL/I is still missing some features of full PL/I. The section Restrictions lists many of the missing features.

Summary of Changes for 1.3.0

Summary of Changes for 1.2.0

Summary of Changes for 1.1.0
Summary of Changes for 1.0.0

Trademarks
Arca Noae is a trademark of Arca Noae.
Gnu is a trademark of the Free Software Foundation.
IBM is a trademark of IBM, Inc.
Intel is a trademark of Intel Corporation.
Iron Spring is a trademark of Iron Spring Software.
Linux is a registered trademark of Linus Torvalds.
VAX is a trademark of Hewlett-Packard, Inc.
WATCOM is a trademark of Sybase, Inc. and its subsidiaries.


Restrictions

STREAM INPUT
GET DATA is not yet implemented.
C and E format codes and FLOAT pictures not implemented for GET EDIT.
Due to a difference in data formats Edit-directed stream input may not be compatible with mainframe PL/I. See note on
stream input compatibility.
The COPY option of the GET statement, the NAME condition, and the DATAFIELD builtin functions are not supported.
RECORD I/O
The statements REWRITE, LOCATE, DELETE, and UNLOCK, and are not supported for non-ISAM files.
For ISAM files (Linux only) the statements LOCATE and UNLOCK, and the builtin functions ONKEY, and SAMEKEY are not supported.
For better integration with 'ld' on Linux, the external procedure name 'main' is reserved and should not be used.
Interlanguage
PL/I procedures can call functions coded in other languages as long as they use the "SYSTEM" or "CDECL" calling convention. The "OPTLINK" and other calling conventions are not supported. Programs in other languages can call PL/I procedures with restrictions. The main program must be PL/I. See Program Linkage for more information.
Preprocessor
The only preprocessor statements fully implemented by the built-in preprocessor are %INCLUDE and the listing control statements %PAGE, %SKIP[(n)], %PRINT, %NOPRINT, and %REPLACE. %PROCESS (*PROCESS) is implemented incompatibly from the IBM compilers. All other preprocessor statements will generate a diagnostic.
See Preprocessor for more information.
The beta version of the full preprocessor is described in Iron-SpringPLIPreprocessor.pdf.
COMPLEX attribute is not yet fully supported.
GENERIC attribute is not implemented. If anyone wants to use GENERIC, please let us know.
DEFAULT statement is recognized but ignored.
DISPLAY
The EVENT option of the DISPLAY statement is not currently supported.
The FETCH and RELEASE statements are not currently implemented.
Arithmetic builtins
The TRUNC arithmetic builtin function is not implemented for FLOAT data in the current version of the compiler.
COMPLEX pseudovariable
The COMPLEX pseudovariable (not builtin function) is not supported. This corresponds with the IBM Enterprise PL/I compilers. The REAL and IMAG builtins can be used instead. This is a permanent restriction.
Builtins PLICKPT, PLICANC, PLIREST, PLITEST
These builtins are not implemented. This is a permanent restriction.
PLISRTx
The sort builtin function PLISRTD is supported. PLISRTA, PLISRTB, and PLISRTC are not currently implemented. It is anticipated that versions of these functions, and possible interfaces to one or more sort products will be added in the future.
Aggregate Results
Builtin functions will not return agregate results in the current version. This is similar to Subset G support for builtins. PL/I procedures may return non-adjustable arrays, but procedures returning structures are not yet supported.
GRAPHIC data
GRAPHIC data support will not be included in this compiler. This is a permanent restriction. A future version will include WIDECHAR data for UTF-16 support. The features not supported include the GRAPHIC attribute and the GRAPHIC and MPSTR builtin functions.
LIKE
The LIKE attribute is implemented with one restriction. If a structure is declared as DECLARE a LIKE b; and some elements of "b" have the INITIAL attribute, the initialization is not performed for "a". This will be fixed in a future version.
UNION
The UNION (CELL) attribute may not be used in adjustable structures. <-- NOTE: is this still true? -->
Array cross sections
Array cross sections are not currently supported, but will be in an upcoming release.

Other restrictions
This list may not be exhaustive at the present time. The compiler error message 995 ("Unimplemented feature xxx") and the run-time condition "UNIMPLEMENTED" are used to flag unimplemented features. Except for "permanent restrictions" noted above, these will be removed in future releases. Some infrequently-used conversions may still be only partially implemented.

Compiler Limits

Compiler Limits
Maximum number of dimensions15
Maximum number of levels in a structure 15
Maximum level number in a structure255
Maximum number of picture characters in a picture
(after expanding all repetition factors)
511
Maximum length of a CHAR or BIT string
(after expanding all repetition factors)
~32000
Maximum nesting depth of %INCLUDE files4
Maximum precision of FIXED DECIMAL data18
Default precision of FIXED DECIMAL data5
Maximum precision of FIXED BINARY data31
Default precision of FIXED BINARY data15
Maximum precision of FLOAT DECIMAL data20
Default precision of FLOAT DECIMAL data6
Maximum precision of FLOAT BINARY data64
Default precision of FLOAT BINARY data52
Minimum/maximum scale factor-128 / 128
Maximum length of an internal or
external label
31

Internal Data Representations

Internal Data Representations
FIXED BIN, precision 7 or less
UNSIGNED FIXED BIN, precision 8 or less
BYTE
FIXED BIN, precision 15 or less
UNSIGNED FIXED BIN, precision 16 or less
WORD
FIXED BIN, precision 31 or less
UNSIGNED FIXED BIN, precision 32 or less
DWORD
FIXED DEC, any precision
Intel x87 BCD format
TBYTE
FIXED DEC OPTIONS(IBM)ceil( (prec+1)/2) ) bytes
FLOAT BIN, precision 23 or less
Intel x87 short floating-point
REAL4
FLOAT BIN, precision 49 or less
Intel x87 long floating-point
REAL8
FLOAT BIN, precision>50
Intel x87 extended floating-point
REAL10
FLOAT DEC, precision 7 or lessREAL4
FLOAT DEC, precision 15 or lessREAL8
FLOAT DEC, precision>15REAL10
PTR, OFFSETNEAR PTR
CHARACTER(n)n bytes
BIT(n) ALIGNED(n+7)/8 bytes
BIT(n) UNALIGNEDn bits
VARYING strings will be preceded by a two-byte length prefix
which will force WORD alignment.
TASK24 bytes aligned on DWORD boundary
EVENT16 bytes aligned on DWORD boundary
EVENT data is always initialized.

Documentation

Complete documentation for Iron Spring PL/I will be available as part of a future release. In the meantime, documentation for the IBM "PL/I for MVS and VM Compiler 1.1" is available
online.

Compiler Differences

The reference implementation for this compiler is IBM PL/I for MVS and VM 1.1. Most of the differences are in the area of Input/Output. See the section
Input and Output for a description of the input/output facilities.

See Restrictions for information on major language features that are not currently implemented.

Character representation
Multiple alternate characters can be used to represent OR and NOT. Either the single or double quote can be used as a string delimiter.
DECIMAL FLOAT
At the present time, DECIMAL FLOAT data is simulated using BINARY FLOAT. This is similar to S/370 PL/I implementations that simulate both DECIMAL FLOAT and BINARY FLOAT using the hardware Hexadecimal floating point formats.
FIXED BINARY
FIXED BINARY data with precision 7 or less is stored in a single byte.
E Format
E Format output will print four digits for exponent instead of two. Output field widths may need to be adjusted.
STRING
The STRING builtin function and pseudovariable restrict the range of data allowed as arguments. This should not affect most normal use of STRING. See the section Builtin Functions and Pseudovariables for the definition of STRING.
DATE, TIME, DATETIME
Most Linux systems maintain the date and time in UTC. These builtins return the value supplied by the system. It is possible to instruct the OS to maintain the date and time in localtime, if desired.
ONCODE
The ONCODE builtin function returns only the base value of the error code. For example, IBM PL/I can return codes 600 to 639 for CONVERSION. This compiler will return 600 in all cases. See list of oncodes.
Additional builtins
See the section Builtin Functions and Pseudovariables for the definitions of all supported builtin functions.
ATTENTION
The ATTENTION condition is enabled by default. It is raised by pressing CONTROL/C while running a text-mode application or otherwise sending XCPT_SIGNAL_INTR/SIGINT to the PL/I process. If no ON-Unit is established for ATTENTION, the implicit action is the same as for ERROR: print an error message and raise the FINISH condition. On normal return from an established ON-Unit, execution continues unaffected. Note that ATTENTION will be raised only in the initial task/thread
RETURNS keyword
Procedure and Entry statements (not ENTRY declarations) may specify "RETURNS( CHARACTER(*) [VARYING] )", or "RETURNS( BIT(*) [VARYING] )". The length of the returned string is determined by the entry declaration in the calling program, e.g.:
DECLARE a ENTRY RETURNS( CHARACTER(20) );.
IBM compilers require an explicit length and will not allow '*'.
EXTERNAL Attribute and Program Linkage
The EXTERNAL attribute has been enhanced to allow the provision of a non-PL/I name visible to procedures outside the PL/I program. The syntax is:
DECLARE x EXTERNAL( 'environment_name' )
See Program Linkage. for more details.


Running the Compiler

The "plic" command is used to invoke the compiler.

The syntax of the plic command is shown. The case of the option switches is significant. Lower-case options have parameters, upper-case do not.


   plic [<options>] <input files> [-o <output file>]

        <options> = <output option>    [<include options>]         [<listing options>]
                    [<source margins>] [<character substitutions>] [<version info>]
                    [<error option>]   [<misc options>]
                    [<optimization option>]

        Options may be entered in any order.
        The Linux shell requires that options containing () be enclosed in double quotes (").  

        <output option> = -S | -C | -L
                          -S = generate assembler (symbolic) output.
                          -C = generate compiled (object) output.
                          -L = generated linked (EXE or DLL) output.
                               (this option not currently implemented.}
                          -N = generate statement number table to provide
                               information for run-time error messages

        <include options> = -i<directory>
                            where <directory> is the absolute or relative path 
                            to a directory to be searched for %INCLUDE files.
                            This option may be used more than once on the command line,
                            and directories will be searched in the order listed.

        <listing options> =  -l[siaxgmov]
                            one or more of [siaxgmov] may be entered, in any order as -lsx
                            -ls = list source
                            -li = list insource
                            -la = list attributes
                            -lx = list cross-reference
                            -lg = list aggregates
                            -lm = list generated code in a format similar to an
                                       assembly listing 
			    -lo = list procedure map (statement offset table)
			    -lv = list additional warning messages           
                            Currently, the insource listing is not available.

        <source margins> = -m(start[,end])
                           This option defines the first and last positions of each
                           input line that contain input for the compiler.  If this
                           option is omitted the source is assumed to be the entire line.
                           This option is included for compatibility with mainframe compilers
                           which would use, for example, -m(2,72).

        <character substitutions> = -cn(<list>) and/or
                                    -co(<list>)
                            This option defines up to four characters each to be used as
                            substitutions for the NOT(¬) [-cn()] and/or OR(|) [-co()]
                            operator IN ADDITION TO the defaults.  The caret (^) is
                            a metacharacter for the OS/2 command processor; if the
                            caret is to be used, code two consecutive carets,
                            for example -cn(^^).

        <version info> = -V
                         The compiler prints version and copyright information on stderr.

        <error option> = -e<option>
                         This option sets the errorlevel returned by the compiler for warning
                         and error messages.  Normally compiler returns 4 if only warnings were
                         issued, and 8 for any errors.
                         -ew tells the compiler to return 0 if only warning messages were issued.
                         -es tells the compiler to return 0 if any errors or warnings were issued.
                           This option is useful when the compiler is run from a script or makefile.
                         -ed tells the compiler to display messages on stderr as well as stdout.         

        <misc options> = -d<option>
                         <option> is a character string, with or without enclosing quotes.
                         -dLIB tells the compiler it is compiling a standard run-time library procedure.
                         -dELF causes the compiler to generate ELF object files.
                         -dOMF causes the compiler to generate OMF object files.
                         The default is to generate the standard object format for the host OS;
                         for OS/2 -dOMF is the default.

        <optimization option> = -O enables optimizations

        <input files> and <output files> are absolute or relative path names.  Only one input and one
                         one output file are currently allowed.  If the output file is omitted the name
                         is generated.  For example, PLIC -S abc.pli will create a file named abc.asm.

Linking PL/I Programs

The "Readme" file for your environment -
Linux or OS/2 contains information on linking PL/I programs.


Compiler input

Compiler input is a standard text file [ENV(TEXT)]. Conventionally, the source program might have the extension ".pli" or ".pl1", but this is not required. Lines are delimited by Newline characters ('0A'x). There is no limitation on line length, although individual tokens such as character-strings are limited to approximately 32,000 characters. Other than newlines, characters lower in the collating-sequence than spaces ('20'x) are ignored. This means, therefore, that at the present time they should not be coded in character string constants. To compile a character string constant containing one or more of these characters concatenate the hexadecimal value, for example:

declare two_line_constant char(24) static initial( ('Line one' || '0D0A'x || 'Line two.') );
This type of expression is resolved at compile-time and does not generate any additional code.

The compiler character set is Codepage 850. The default representations for the OR character is '|', '7C'x, and for NOT '¬', 'AA'x. The corresponding unicode characters are U+007C [bar, vertical], and U+00AC [NOT SIGN]. The character or characters specified above under Running the compiler, the options -cn and -co can be used to indicate additional encodings used to represent these characters.

String data can be delimited by either the single (') or double (") quote characters. The same character must be used to begin and end the string. The character not used as a delimiter may be part of the string. If portability is a concern, use only the single-quote to delimit strings. The character used as a delimiter can be included in the string by specifying it twice in succession, for example, 'Let''s dance' will be compiled as Let's dance, """Fine"", he said" as "Fine", he said.

Source lines are logically continued from the right margin of one line to the left margin of the next, with no whitespace assumed between the two character positions. (see the -m compiler option). Any characters outside the margins, as well as any characters lower than space in the collating sequence, are ignored. For example:


         Left Margin            Right Margin                  Line End
             |                       |                            |
             "This is a continued charsome stuff outside the margin
 extra stuff acter string"
will be compiled as This is a continued character string.

If a line ends with a word or a number with no trailing blank, and another word or number begins in the left margin of the next line the two will be considered a single token. A future release will cause words and numbers, but not strings, operators, etc., to break at the end of a line.

INCLUDE files are identical in format to the source file. Currently INCLUDE files must have either no extension or the extension ".inc" or ".cpy". The compiler option -i, which can be repeated multiple times, provides a list of directories to be searched, in the order written, to locate an INCLUDE file. The current working directory is always searched last if the file is not found sooner. INCLUDE files use the same list of alternate characters as the source, and must use the same margins, although a future release will allow these to be overridden for each individual file.


Compiler output

If any of the listing options (-lx) is specified, compiling a program xxx.pli will create a source listing file named xxx.lst.

Compiler output currently consists of the optional source, assembler, statement offset table, aggregate map, and symbol attribute, and cross-reference listings, and an optional object file or assembler file of the compiled program.

All pages of the listing have a heading similar to the following showing the date and time of compilation and the compiler version used. The title is taken from the first line of the source file. The subheading identifies the "Source Listing", the "Assembly Listing", the "Procedure Map", the "Aggregate Listing", and the "Symbol Table and Cross-Reference Listing."


Mar  5, 2010  10:28     /* _pli_Sig                                     */                          
0.8d                                                Source Listing           ...         Page 1     

Source listing:
If the compiler option "-ls" is specified a source listing will be generated.

Here are a few lines from a listing: The letters in blue are a key to the descriptions below, and are not part of the listing.


A   B  C
  340 
  341  %include DSA;
  342
  D     E   F 
341 (INF104)Processing include file DSA.
G

    1  /********************************************************************/
    2  /*                                                                  */

'B' is the line number. The source file and each %include file are numbered starting from line one. A future compiler version will also provide a file number for each include file to be used on error messages.

'C', the next 100 characters of the listing, show the input line. If the line is longer than 100 characters additional unnumbered lines will display the overflow.

The line following line 342 above shows the format of error, warning, and information messages. 'D' is the source line number. 'E' is the severity and message number (xxxyyy) 'xxx' is 'INF' for information-only messages as shown, 'WRN' for warnings, and 'ERR' for errors. 'yyy' is a unique message identifier for this error. 'F' is the message text.

Currently messages generated by the parser appear intermixed in the source listing, while code generator messages appear at the end of the source, or intermixed in the object code listing if one is produced. In a future version all messages will appear at the end. Due to parsing considerations the error message may appear one line before or after the line it references.

Assembly listing:
If the -lm option is specified, a listing of the generated code will appear after the source listing. This is similar in format to the listing generated by ALP. The major difference is that ALP displays immediate operands in big-endian order. This listing will always display immediate operands as they are stored in memory, i.e. little-endian. Note that the assembly code shown is for illustrative purposes and may or may not be assembleable by any particular assembler.

Here is a small sample of the assembly listing:
                                       ; Stmt 14, Line 17 (DO)
00000313 89 65 [CC]                      mov dword ptr -52[ebp],esp
00000316 8B 75 [D8]                      mov esi,dword ptr -40[ebp]
00000319 8B 3E                           mov edi,dword ptr 0[esi]
0000031B 83 FF 00                        cmp edi,00h
0000031E 90 90 90 90 90 90               db 90h,90h,90h,90h,90h,90h
00000324 8B 65 [CC]                      mov esp,dword ptr -52[ebp]
                                        ; Stmt 15, Line 18 (Assignment)
00000327 8B 75 [D8]                      mov esi,dword ptr -40[ebp]
0000032A 8B 3E                           mov edi,dword ptr 0[esi]
0000032C 89 7D [D4]                      mov dword ptr -44[ebp],edi
                                        ; Stmt 16, Line 19 (DO)
0000032F 89 65 [C8]                      mov dword ptr -56[ebp],esp
00000332 8B 75 [D4]                      mov esi,dword ptr -44[ebp]

Aggregate listing:
The -lg option causes a map of each aggregate (structure and array) to be printed. For example, the mapping of an aggregate describing the output of the TIME() builtin from the following declaration will appear as shown.

  dcl   1 rTime,
          5 Hours             pic'99',
          5 Minutes           pic'99',
          5 Seconds           pic'99',
          5 Hundredths        pic'999';

 Def Name            Lvl Dims       Off         Len       Tot Len
  40 RTIME            1             0           9           9 
     HOURS            2             0           2           2 
     MINUTES          2             2           2           2 
     SECONDS          2             4           2           2 
     HUNDREDTHS       2             6           3           3 
'Def' is the line number of the declaration, 'Lvl' is the normalized level number, 'Off' is the offset of the member from the base of the structure. 'Off' will be shown as "B.b" for bit members, for example a one-bit field beginning in bit 1 of byte 0 will be shown as 0.1. 'Dims' is the number of dimensions for array elements. 'Len' is the member length, again as "B.b" for bit members. 'Tot Len' is the total length of the member, for non-adjustable arrays this will be the array length. Adjustable lengths or bounds will cause 'Len' and or 'Tot Len' to be displayed as "*".

Statement offset listing:
The -lo option causes a procedure map to be printed listing the assembled offset of each statement, its starting line number, and the statement type. Here is the procedure map for the procedure containing the code shown above:

  Offset  Line Statement type             Offset  Line Statement type             Offset  Line Statement type           
      08     2 PROCEDURE                ENV
      23    10 Assignment                     35    11 PUT                            E4    12 PUT                      
    0180    13 Assignment                   0197    14 PUT                          0246    15 PUT                      
    02C3    16 PUT                          0313    17 DO                           0327    18 Assignment               
    032F    19 DO                           0343    20 PUT                          03BF    21 Assignment               
    03C8    22 END                          03D0    23 PUT                          0420    24 Assignment               
    0429    25 END                          042E    27 END                      
'Offset' is the location of the beginning of the statement relative to the start of the compilation unit (zero). 'Line' is the starting line number of the statement. 'Statement type' describes the type of statement.

Symbol table and cross-reference listings:
If the compiler options "-la" or "-lx" are specified, the attribute list (symbol table) or cross-reference listing is printed as shown below. "-la" causes the variable attributes to be printed, "-lx" causes the cross-reference listing to be generated. The example below shows the results of specifying "-lax".

H                    I    J                                                     K
ADDR                 124  Builtin
BSW                  309  Entry Unaligned                                       [ Code+'13C0'x ]
                            265 
BYTE                 332* Builtin
                            737   738 
C                     77  Char(1) Unaligned                                     [ DSA-'51'x(1) ]
                            266   278   300   321   346   362   370   446   452   453 
                            478   507   510   512   514   516   531   553   557   566 
C4                   109  Char(4) Unaligned Based                               [ +'00'x(4) ]
                            297   298   533   615   679 
C4                   327  Char(4) Unaligned Parameter                           [ Loc @DSA+'08'x(4) ]
                            723   724   725   726   727 
CONDS                 97  (9) Char(6) Var Unaligned Static Init()               [ Static+'12'x(8) ]
                            407   635 
DSA_BELOW_EBP         27  Unaligned Structure In(PLI_DSA)                       [ +'00'x(32) ]
                            635   636   642   643   644   646   647 
DSA_CHC               33  Ptr Aligned In(PLI_DSA.DSA_BELOW_EBP)                 [ +'08'x(4) ]
                            643 
PLI_DSA              340  Unaligned Based Structure Level(1)                    [ +'00'x(40) ]

'H' is the name of the data element. If the name is longer than twenty characters it will appear on a line by itself, with the remaining data shown on the next line. 'I' is the line number of the declaration of this item. Undeclared data, like 'BYTE' above show the line number where this element is first used, followed by '*'.

Each generation of data will be listed separately. The two occurrences of 'C4' above represent two data elements with the name 'C4' in two different blocks. Usage of each is cross-referenced separately.

'J' lists the data attributes, which should be self-explanatory. This field may occupy more than one line.

'K' shows the address of the item in []. The addresses are displayed as follows:

Code labels:
Code+'oooo'x. 'oooo' is the hexadecimal offset from the first code byte. See 'BSW' above.
STATIC data:
Static+'oooo'x(ll). 'oooo' is the offset from the first data byte. 'll' is the (decimal) length of the data element. The length of UNALIGNED BIT elements is shown as (x.y) where 'x' is the number of whole bytes, and 'y' is the number of additional bits. For an array, 'll' is the length of one member. See 'CONDS' above.
AUTOMATIC data:
DSA-oooo'x(ll). 'oooo' is the offset from the start of the DSA containing the data. Register EBP always points to the current DSA. Lexically containing DSAs (static scope) are chained from offset -8 in the contained DSA. All offsets are negative. See the file 'DSA.INC' distributed with the library. The variable 'C' above is an example of AUTOMATIC data.
BASED data:
+'oooo'x(ll). 'oooo' shows the offset from the beginning of the BASED variable or structure. See the first occurrence of 'C4' above.
Data addressed via locator:
Loc @DSA±'oooo'(ll). This is the address format for parameters, adjustable data, and CONTROLLED data. 'oooo' is the offset of the locator/descriptor which points to and describes the data. See the second 'C4' above.

If there were any errors or warnings, a message showing the number of them appears at the end of the listing.

Object output:
Compiling a program xxx.pli with the -C option will create an object file xxx.obj for OMF-format output, or xxx.o for ELF. The layout of the generated code is described under "Program Structure" below.

Assembler output:
Compiling a program xxx.pli with the -S option will create an assembler file xxx.asm in a format suitable for the IBM ALP assembler or the open-source JWASM assembler. Other assemblers will most likely not be able to assemble this file correctly.

The compiler generates assembler labels for level-1 EXTERNAL data and for each EXTERNAL ENTRY. Most assemblers have a list of reserved words which cannot be used for labels, typically including all register names (e.g. EAX), all instruction mnemonics (LEA), and most pseudo-ops (ORG). Using any of these as PL/I external labels will cause an assembly error.

A few lines of assembler output are shown:


; Stmt 2, Line 3 (PUT)
 .code
 sub esp,64 
 mov word ptr 0[esp],1 
 mov dword ptr 12[esp],ebp
The source statement and line number that generated each piece of code, along with the statement type is shown as indicated. The line number ties this code to the source listing.
Program Structure
The generated program consists of a single code segment(.text) identified by the label '_pli_code', a single static initialized data segment(.data) identified by '_pli_data', a single static unitialized data segment (.bss, '_pli_bss'), an unnamed stack segment for OS/2, and zero or more external common segments.

External segments are generated for each variable or structure declared EXTERNAL, as well as for each EXTERNAL FILE. If the variable has the INITIAL attribute the data is always generated, otherwise the declared amount of storage is reserved. Here is the generated code for an uninitialized external structure named 'abc':


ABC segment dword common 'ABC'
_ABC equ $
 org _ABC+0Ch
ABC ends
Here is the code for the initialized external segment 'sysprint':

SYSPRINT segment dword common 'SYSPRINT'
_SYSPRINT equ $
 dd 0
 dd 0
 dd _SYSPRINT+12
 dw 8
 db 'SYSPRINT'
SYSPRINT ends
The linker combines identically-named external segments. The resulting length will be the length of the largest such segment. All declarations of EXTERNAL data should be identical in length. It is permissible to have only one declaration of EXTERNAL data have the INITIAL attribute, and all procedures declaring it will reference that data. If the initial values of the elements differ between declarations the results are unpredictable. Warning: The Gnu linker has a bug linking addresses declared with the INITIAL attribute in external data. Only one such declaration can have the INITIAL attribute. The linker will add together the values of all initialized addresses.

Compiler Work File:
The compiler creates a temporary work file named PLI-xxxxxxxx-yyyyyyyy-zzzzzzzz.tmp, in the current working directory on OS/2, or the /tmp directory on Linux. Normally this file is deleted at the end of compilation. Errors in the compiler causing certain traps may prevent automatic deletion of this file. If it not automatically removed it may be deleted manually.


Syntax Chart Format

In the syntax charts that follow, the characters '-', '.', '+', 'v', '^', '|', '<', and '>' are used only for drawing the chart, and are not part of the syntax. Other punctuation such as parentheses, semicolons, commas, etc. must be entered as shown.Words shown in CAPITALS are PL/I keywords. Words shown in lower-case represent names chosen by the programmer or variable information. (The actual case of the word in the program is not significant.) '>>' identifies the beginning of a diagram, and '><' represents the end. Entities appearing in '|' data '|' represent a syntax fragment that is expanded later. Elements on the main line of the diagram as '--- stuff ---' are required. Elements appearing above or below the main line represent choices. Elements shown all below the line are optional with no default. Where one element is shown above the line it represents the default if none of the choices is specified.


The PROCEDURE and ENTRY and PACKAGE statements

PROCEDURE statement.

PROCEDURE-+--------------------+-+-----------------+-+-----------------+-+-------------+-+------------------+-- ; ><
          |                    | |                 | |                 | |             | |                  |
          +-| ( parameters ) |-+ +-| returns-opt |-+ +-| options-opt |-+ +- RECURSIVE -+ +-| external-opt |-+
ENTRY statement.

ENTRY--+--------------------+-+-----------------+-+-----------------+-+-------------+-+------------------+- ; ><
       |                    | |                 | |                 | |             | |                  |
       +-| ( parameters ) |-+ +-| returns-opt |-+ +-| options-opt |-+ +- RECURSIVE -+ +-| external-opt |-+ 
Other keywords defined by The IBM PL/I for MVS and VM compiler 1.1 and various other compilers are accepted for source compatibility, but are otherwise ignored.


|parameters|

  +------- , ----------------+
  v                          |
|-.- parameter-description --+----|
Each parameter-description supplies the attributes for one parameter, therefore the entry expects as many arguments as there are items in the list. A parameter-description is a list of data attributes that describes that parameter. For example: FIXED BINARY(7), CHARACTER(8), POINTER, or ENTRY would all be valid parameter descriptions.


|returns-opt|

|-- RETURNS ( returns-description ) ---|
The returns-description describes the attributes of the data which will be returned by this entry if it is invoked as a function. If the returns-opt is omitted the attributes are determined by the first character name of the first or only name prefix — I thru N: FIXED BINARY(15), other: FLOAT DECIMAL(6).


|options-opt|

                +--- , --------+
                |              |
                +--------------+
                v              |
|---- OPTIONS ( . entry-option +  ) ---|
Each entry-option is one of the following:
FROMALIEN
Indicates that this entry may be called from a non-PL/I program. A FROMALIEN procedure should be considered to be non-nested — that is, it may not access any AUTOMATIC data in the containing procedures, if any. Also, any condition-handling established in a previously-called procedure will be ignored on entry to the FROMALIEN procedure.

LINKAGE( linkage-type )
'linkage-type' indicates the type of calling sequence with which this entry will be invoked. If LINKAGE is omitted, a standard PL/I convention will be used. (see Run-time Considerations). Currently the only valid linkage type is SYSTEM. This can be used to call functions written in C, including OS/2 API functions, but is also a valid convention among PL/I procedures, with some restrictions.
For source compatibility with IBM PL/I for OS/2, specify 'OPTIONS( BYVALUE LINKAGE(SYSTEM) )'. BYVALUE is assumed by this compiler and the keyword is ignored, but IBM PL/I defaults to BYADDR unless otherwise specified.

LINKAGE(OPTLINK) will be implemented in a future version to specify the "Optlink" calling convention that uses registers for some arguments.

MAIN
Indicates that this is the initial or 'main' procedure. Iron Spring PL/I allows only one OPTIONS(MAIN) procedure to be included in an executable program.

REENTRANT
This is included for compatibility only. All code compiled by Iron Spring PL/I is reentrant.

RECURSIVE is supported for compatibility only. It indicates that this entry may call itself, either directly or indirectly through a chain of calls. Iron Spring PL/I assumes all entries are potentially recursive.


|external-opt|

|-- EXTERNAL --+----------------------+---|
               |                      |
               +- ( external-name ) --+
The external-name is a character string, enclosed in quotes, representing the name by which this entry will be known to external callers. External 'external-name's beginning with the string "_pli" are reserved for use by the run-time library. If the 'external-name' is not specified, the default external name is an uppercase translation of the first label on the external procedure or entry statement. For example, x: y: PROCEDURE; will use "X" as the external name for the linker.

ENTRY Declarations
The following attributes can be used in the declaration of an external entry:

DECLARE-- decl-name --ENTRY-+--------------------+-+-----------------+-+-----------------+-+------------------+-- ; ><
                            |                    | |                 | |                 | |                  |
                            +-| ( parameters ) |-+ +-| returns-opt |-+ +-| options-opt |-+ +-| external-opt |-+
Parameters, returns-opt, and external-opt are described under "PROCEDURE and ENTRY Statements" above. An entry declared without a parameter list — DECLARE a ENTRY; will accept any number of arguments with no conversion. An entry declared with an empty parameter list — DECLARE a ENTRY(); does not allow any arguments.

|options-opt|

                +--- , --------+
                |              |
                +--------------+
                v              |
|---- OPTIONS ( . entry-option +  ) ---|
The following options are valid for entry declarations:
ASSEMBLER (abbreviated ASM)
Indicates that the entry is a non-PL/I procedure (assembler, C, etc.) The compiler generates code to save and restore the x87 Floating-point control word, since programs in different languages may change this unpredictably.
LINKAGE( linkage-type )
'linkage-type' is as described above under "PROCEDURE and ENTRY Statements".

PACKAGE statement.

PACKAGE-+-------------------------------------+----+-------------------------------------+--+-------------------+- ; ><
        |                                     ^    |                                     ^  |                   ^
        |               +----- , ------+      |    |                +---- , ------+      |  |                   |
        |               |              |      |    |                |             |      |  |                   |
        |               v              |      |    |                v             |      |  |                   |
        +- EXPORTS ( -+-+- procedure --+-+- )-+    +- RESERVES ( -+-+- variable --+-+- )-+  +- OPTIONS(options)-+
                      |                  ^                        |                 ^
                      |                  |                        |                 |
                      +------- * --------+                        +------- * -------+

A package is a block which can immediately contain only declarations, DEFAULT statements, and PROCEDURE blocks. The package forms a name scope that is shared by all declarations and procedures it contains. Some or all of the level-1 procedures can be exported and made known outside the package as external procedures.

EXPORTS
Specifies that all [EXPORTS(*)] or the named procedures are to be made known outside the package. Procedures not exported are known only within the package. If the EXPORTS keyword is not specified, EXPORTS(*) is assumed.
RESERVES
This keyword is not used by the Iron Spring compiler, and is treated as comments only due to differences in handling initialized and uninitialized EXTERNAL data.
OPTIONS
This keyword is not used by the Iron Spring compiler, and is treated as comments only for compatibility with other compilers.

All declarations with package scope (outside of procedures within the package) must have STATIC, BASED, CONTROLLED, or EXTERNAL storage class, AUTOMATIC data is not allowed outside of procedures.


Input and Output Statements

In the following discussion, delimiter signifies either a linefeed character (ASCII '0A'x), or a carriage-return/linefeed combination (ASCII '0D0A'x).

Iron Spring PL/I can read and write:

  1. Standard text files, with lines of text terminated by delimiters.
  2. Files of fixed-length records with or without delimiters.
  3. Files of variable-length records where the length of each record is indicated by a two-byte binary prefix, with or without delimiters.
  4. Files containing arbitrary streams of binary data with no record boundaries.

File Declarations
The general form of a file declaration is shown below. As usual, keywords can be specified in any order. Many keywords may be specified either in the declaration or in the OPEN statement for the file. If they are specified in both, they must not conflict. For example, a file declared INPUT cannot be opened as OUTPUT or the UNDEFINEDFILE condition will raised when the file is opened.

>>-DECLARE-name-+------+-+--------+-+--------+-+----------------------------------+-+------------------+-><
                |      | |        | |        | |                                  | |                  |
                +-FILE-+ +-RECORD-+ +-INPUT -+ +-ENVIRONMENT(environment options)-+ +-other attributes-+
                         |        | |        |
                         +-STREAM-+ +-OUTPUT-+
                                    |        |
                                    +-UPDATE-+

RECORD and STREAM Files
  • STREAM indicates that the file is a continuous stream of data items in character form. STREAM files are read or written without regard to record boundaries. The PL/I statements GET and PUT are used to process STREAM files.
  • RECORD indicates that the file is logically divided into records which are composed of one or more data items in any form. A record is always read or written as a unit. The PL/I statements READ, WRITE, REWRITE, and LOCATE are used to process RECORD files.

INPUT, OUTPUT, and UPDATE
  • INPUT indicates that the file is to be read only. The file must exist or the UNDEFINEDFILE condition will be raised when the file is opened.
  • OUTPUT indicates that the file is to be written only. If the file does not exist, it will be created when opened. If it does exist, it will be deleted and a new file created, unless the ENVIRONMENT option APPEND is specified.
  • UPDATE indicates that the file is going to be both read and written.

ENVIRONMENT Options
The ENVIRONMENT attribute allows specification of implementation-dependent options for a file. Iron Spring PL/I allows coding of all ENVIRONMENT attributes allowed by IBM PL/I for MVS and VM. Only attributes actually used are described here.
  • Record format keywords: D, DB, F, FB, FS, FBS, V, VB, VS, VBS, U.
    One of these keywords is coded to indicate the type of records to be processed. Only the D, F, V, and U are significant, the others are provided for compatibility. F indicates that the file consists of records having all the same length. V or D indicate that the records in the file can have different lengths.

    The options TEXT, CRLF, LF, and VARLS provide additional information about the external representation of F, V, and D files.

    Record format U allows reading and writing files without regard to formatting or record boundaries. When reading INTO a character string, the amount of data available up to the maximum length of the string is returned. ENDFILE is raised only on the next read after the last byte has been returned. See the sample program 'readu.pli' for an example of using ENV(U) to read a file with no predetermined maximum record length. Writing FROM a character string writes n bytes of data from the string, where n is the current or constant length of the string.

  • BLKSIZE(n) specifies the size of the buffer used to transfer data to or from the file or device. If this keyword is not specified, a default value is used.
  • RECSIZE(n) specifies the uniform record length for F files, or the maximum record length for V or D files. If this keyword is not specified, the default is LINESIZE. The RECSIZE value describes only the data portion of the record, exclusive of delimiters, length prefixes, etc.
  • TEXT, CRLF or LF specifies the presence of record delimiters. If none of the three options is specified, no delimiters will be written on output or expected on input. TEXT indicates that the default record delimiter for the system will be used: CRLF for OS/2 or LF for Linux. The TEXT option should be used in preference to either CRLF or LF unless a specific delimiter is required, since code specifying TEXT is portable. LF indicates that a single newline character (ASCII '0A'x) delimits records in the file (Unix convention). CRLF indicates that a carriage-return/linefeed combination (ASCII '0D0A'x) is used as a delimiter (OS/2, DOS convention). The delimiters will be written as specified on output, but either is always acceptable for input.
  • VARLS specifies that records in the file will be written with a FIXED BINARY(15) prefix containing the length of the following data.
  • APPEND specifies that an INPUT or UPDATE file will have data appended to the end if it already exists. When the file is opened, the current file position will be set to the end of the file.

Other Attributes

STREAM files default to ENV(V TEXT) unless other attributes are specified. RECORD ENV(V) files default to ENV(V VARLS).

Standard Files
Unless overridden by ENVIRONMENT or OPEN, the standard files SYSIN and SYSPRINT default to stdin and stdout with the following attributes respectively.
SYSINENV(V TEXT RECSIZE120))
SYSPRINTPRINT ENV(V TEXT RECSIZE(120)) LINESIZE(120) PAGESIZE(60)
If stdin and stdout are the current terminal(VIO) window, these files default to ENV( BLKSIZE(0) ).
Interactive options

INDEXED Input/Output

See Using Indexed Files for information about indexed (ISAM) files. (currently Linux only)


DISPLAY statement

The DISPLAY statement writes text to stderr and optionally accepts a reply.

>>-DISPLAY (expression)--+----------------------------------------+----------- ; --><
                         |                                        |
                         +-REPLY(char-ref)-+-------------------+--+
                                           |                   |
                                           +-EVENT(event-ref)--+
"expression" is converted to a character string and written to stderr. If the "reply" option is not specified, this is followed by a newline. If the "reply" option is specified it must be a character reference which will receive the response from stderr.

The "event" option is currently ignored, and the statement is executed synchronously; control does not return until the response has been read. If the "display" expression ends with a '$' neither the '$' nor the newline will be displayed. This allows output from multiple display statements to appear on one line.


Multitasking

The Compiler currently supports the "multitasking" API similar to IBM PL/I for MVS and VM. "Multithreading", as supported by IBM Enterprise PL/I, will be implemented in a future release. Iron Spring PL/I will then continue to support both feature sets.

The multitasking API adds a TASK data attribute, the TASK, EVENT, and PRIORITY options of the CALL statement, and the PRIORITY builtin function and pseudovariable. The EVENT attribute and the builtins/pseudovariables COMPLETION and STATUS, and the WAIT statement, which IBM does not consider part of the multitasking feature, are also documented here.

Tasks constitute a hierarchical structure with the initial task of the procedure, the "major task", at the root. If the major task "A" attaches tasks "B" and "C", these are then subtasks of "A". If subtask "B" attaches task "D", "D" is then a subtask of "B" and so on. When a task terminates, any subtasks which are not complete will be terminated. Otherwise a task will terminate when any of the following occurs: Execution of a STOP statement in any task will terminate the major task and all subtasks.

Multitasking considerations:


IBM-format packed decimal data

IBM mainframe PL/I stores FIXED DECIMAL data as"packed decimal", big-endian two digits per byte with a trailing sign. Signs 'A'x, 'C'x, 'E'x and 'F'x indicate positive numbers; 'B'x and 'D'x are negative. Packed decimal numbers can be up to 16 digits.

Iron Spring PL/I supports this format for compatibility purposes, for data transfer to and from mainframe systems. To declare a packed decimal field the attributes are FIXED DECIMAL(p[,q]) OPTIONS(IBM) where p and q are the precision and scale as usual. The field will occupy ceil((p+1)/2) bytes with byte alignment only. OPTIONS(IBM) may be specified on a major or minor structure to indicate that all FIXED DECIMAL data in the structure is in packed format.

Use of IBM-format packed decimal data for other than input and output is not recommended, because this data is converted by a library procedure each time it is used. Move packed decimal data to and from standard FIXED DECIMAL fields (without "OPTIONS(IBM)") before using in computations.

Example:

DECLARE x FIXED DEC(7) STATIC INIT( 443322 ) OPTIONS(IBM);
will generate a four byte field containing '0443322C'x.


Builtin Functions and Pseudovariables

Iron Spring PL/I implements the following builtin functions. Functions flagged with an '*' are described below, all others are as described in the IBM PL/I for MVS and VM PL/I Language Reference. Some builtins may have temporary restrictions on arguments which are not documented here. Aggregate arguments are not allowed. d>
Arithmetic builtins
ABSDECIMALIMAGPRECISION
ADDDIVIDEMAXREAL
BINARYFIXEDMIN*ROUND
CEILFLOATMODSIGN
COMPLEXFLOORMULTIPLYTRUNC
CONJG
String-handling builtins
*AFTER *CENTRERIGHT LOW *SEARCH 
*BEFORE CHAR *LTRIM *STRING 
BIT *COPY MAXLENGTH TRANSLATE 
BOOL HIGH REPEAT *TRIM 
*CENTERLEFT INDEX *REVERSE UNSPEC 
*CENTERRIGHT *LEFT *RIGHT VERIFY 
*CENTRELEFT LENGTH *RTRIM 
Mathematical builtins
ACOS COS EXP SIND 
ASIN COSD LOG SQRT 
ATAN COSH LOG10 TAN 
ATAND ERF LOG2 TAND 
ATANH ERFC 
SIN TANH 
Array-handling builtins
ALLHBOUNDPROD
ANYLBOUNDSUM
DIM*POLY
Condition-handling builtins
ONCHARONCOUNTONSOURCE
ONCODEONFILE ONLOC
Storage control builtins
ADDRCURRENTSTORAGEPOINTERVALUE
*ALLOCATEENTRYADDRSIZE
ALLOCATIONNULLSTORAGE
BINARYVALUEPOINTERSYSNULL
CURRENTSIZEPOINTERADD
Event builtins
COMPLETIONSTATUS
Input-output builtins
COUNTLINENO
Integer manipulation builtins
*IAND*INOT*ISLL
*IEOR*IOR*ISRL
*IBSW
Miscellaneous builtins
*BYTE  *CS  *HEXIMAGE  *SIGNED 
*COLLATE *DATE  PLIRETV  *TIME 
*COMPARE *DATETIME *RANDOM  *UNSIGNED 
Builtin subroutines
PLIDUMP*PLIFREEPLIRETC
*PLIFILL*PLIMOVE*PLISRTD

Additional Builtin Functions and Pseudovariables

This section lists only builtins in Iron Spring PL/I either not present in IBM PL/I for MVS and VM or implemented differently. The definitions are taken from IBM PL/I for OS/2 [OS2], IBM Enterprise PL/I [ENT], VAX PL/I [VAX], Multics PL/I [Multics], or ANSI PL/I [ANSI].

AFTER(s,c) AFTER returns a nonvarying string containing all the characters or bits in the string value of "s" that follow the first occurrence of the string value of "c". If "c" does not occur within "s", if "s" is the null string, or if no characters or bits in "s" follow "c", the result is the null string. If "c" is the null string the result is "s". In the current version "s" and "c" must be or convert to character strings. [Multics][ANSI]
 
ALLOCATE(n) ALLOCATE allocates n bytes of storage and returns a pointer to the first byte. [OS2][ENT]
 
BEFORE(s,c) BEFORE returns a nonvarying string containing all the characters or bits in the string value of "s" that precede the first occurrence of the string value of "c". If "c" does not occur within "s", if "s" is the null string, or if no characters or bits in "s" precede "c", the result is the null string. If "c" is the null string the result is the null string. In the current version "s" and "c" must be or convert to character strings. [Multics][ANSI]
 
BYTE(n) BYTE returns a character string of length one equivalent to the following:
SUBSTR( COLLATE(), MOD(n,256)+1, 1 ) [ENT][VAX]
 
CENTERLEFT(x,y[,z])  CENTERLEFT returns a character string of length y, with character string x centered (or one position to the left of center), padded on the ends with the character z as needed. Y must be able to be converted to a FIXED BINARY value. Z should be a single character. If z is omitted, a space will be used. [OS2][ENT]
CENTRELEFT is a synonym for CENTERLEFT.
 
CENTERRIGHT(x,y[,z])  CENTERRIGHT returns a character string of length y, with character string x centered (or one position to the right of center), padded on the ends with the character z as needed. Y must be able to be converted to a FIXED BINARY value. Z should be a single character. If z is omitted, a space will be used. [OS2][ENT]
CENTRERIGHT is a synonym for CENTERRIGHT.
 
COLLATE COLLATE Returns a character string of length 256 containing all 256 possible character values. [OS2][ENT][VAX]
 
COMPARE(x,y,z) COMPARE compares "z" bytes at locations pointed to by "x" and "y". It returns 0 if the strings are identical, <0 if x<y, and >0 if x>y. [OS2][ENT]
 
COPY(x,y) COPY returns a string consisting of "y" concatenated copies of string "x". [OS2][ENT][VAX]
 
CS(p,q,x) CS executes a hardware "CMPXCHG" instruction and returns a FIXED BINARY(31) value indicating whether or not the exchange was accomplished. q is a pointer expression which yields the address of a FIXED BINARY(31) variable q' to be set by the exchange, p is a pointer expression which yields the address of a FIXED BINARY(31) variable p' which is to be compared with q', and x is an expression which yields the new FIXED BINARY(31) value to be set. If p' and q' are equal x replaces q', and the CS builtin returns 0 (success). If they are unequal q' replaces p' [to set up for another call to CS] and the builtin returns 1 (failure). This provides an atomic "compare and swap" operation for thread or process synchronization. [ENT]
 
DATE, DATETIME, TIME IBM PL/I for MVS & VM 2.3 states "The time zone and accuracy are system dependent." These builtins return the system date and time as determined by the operating system, usually either UTC or local time.
 
HEXIMAGE(x,y[,z]) HEXIMAGE returns a character string that is the hexadecimal representation of 'y' bytes of storage at location 'x'. If 'z' is omitted, the length of the returned string is 2*y. If 'z' is present it must be CHARACTER(1) nonvarying, and the value of 'z' is inserted between every set of eight characters in the output string. In this case the length of the returned string is (2*y) + floor((y-1)/4). [OS2][ENT]
 
IAND(x,y...) IAND returns a FIXED BINARY(31) value representing the logical AND of its arguments. All arguments are converted to FIXED BINARY(31). [OS2][ENT]
 
IBSW(n) IBSW returns a FIXED BIN(32) UNSIGNED value obtained by swapping the bytes in its argument, n. The argument is converted to UNSIGNED FIXED BINARY(32) using the normal rules of PL/I and the bytes are swapped to reverse the endianness. This builtin is not available with any other PL/I compiler.
 
IEOR(x,y) IEOR returns a FIXED BINARY(31) value representing the logical EXCLUSIVE OR of its arguments. All arguments are converted to FIXED BINARY(31). [OS2][ENT]
 
INOT(x) INOT returns a FIXED BINARY(31) value representing the logical NOT of its argument. The argument is converted to FIXED BINARY(31). [OS2][ENT]
 
IOR(x,y...) IOR returns a FIXED BINARY(31) value representing the logical OR of its arguments. All arguments are converted to FIXED BINARY(31). [OS2][ENT]
 
ISLL(x,n) ISLL returns a FIXED BINARY(31) value representing x logically shifted left n bits. All arguments are converted to FIXED BINARY(31). The result is undefined if n is greater than 31 or less that 0. [OS2][ENT]
 
ISRL(x,n) ISRL returns a FIXED BINARY(31) value representing x logically shifted right n bits. All arguments are converted to FIXED BINARY(31). The result is undefined if n is greater than 31 or less that 0. [OS2][ENT]
 
LEFT(x,y[,z])  LEFT returns a character string of length y, with character string x left-justified and padded on the right with the character z as needed. Y must be able to be converted to a FIXED BINARY value. Z should be a single character. If z is omitted, a space will be used. [OS2][ENT]
 
LTRIM(x[,y]) LTRIM returns the character value of 'x' with all leading characters in the character value of 'y' trimmed. If 'y' is omitted a string consisting of a single blank character is used. If 'y' is the null string no trimming occurs. If 'x' is the null string a null string is returned. [Multics][VAX]
 
MAXLENGTH(x) MAXLENGTH returns the maximum length of the string value of "x". "x" must be computational; if it is not a string it is converted to a CHARACTER string before the length is calculated. This is different from the LENGTH builtin that converts BINARY arguments to BIT strings, but is consistent with IBM Enterprise PL/I. [OS2][ENT][VAX]
 
PLIFILL(x,y,z) PLIFILL moves "z" copies of a single byte "y" to a location pointed to by "x" with no conversion, padding, or truncation. No checking is done. [OS2][ENT]
 
PLIFREE(p) PLIFREE frees storage at location "p" allocated by the ALLOCATE builtin. No checking is done as to the validity of the area being freed. [OS2][ENT]
 
PLIMOVE(x,y,z) PLIMOVE moves "z" bytes to the location pointed to by "x" from the location pointed to by "y". No checking is done. [OS2][ENT]
 
POLY(x,y) POLY forms a polynomial expansion from its arguments. x must be a one-dimensional array (vector), y may be a vector or a scalar element. the result is x1 + x2*y1 + x3*(y1*y2)...
If y is a scalar, its value is reused, giving x1 + x2*y + x3*y**2 ...
If there are more elements in y than x, the excess will be ignored. If there are fewer, the last element will be reused as needed. x and y must be computational, and will be converted to FLOAT. The result is FLOAT with the base and mode determined by the rules for expression evaluation, and the precision of the longest argument.

This is an IBM-only builtin function, and various IBM compilers function differently; in particular, several only allow y to be a scalar.
 

RANDOM[(s)] RANDOM returns a pseudo-random number between 0e0 and 1e0 as a FLOAT BINARY(52) value. "s" is an optional seed to start or restart the sequence. The value of "s" will be converted to FIXED BINARY(31). If "s" is omitted the number returned is based on the seed generated by the previous call, or 1 if a seed was never specified. "s" must be greater than zero and less than 2,147,483,646 or the ERROR condition will be raised. RANDOM maintains one seed per program, regardless of the number of tasks.
RANDOM uses the "multiplicative congruential method" (compatible with IBM Enterprise PL/I) to generate values:
   seed(x) = mod( 950706376 * seed(x-1), 2147483647 )
   random(x) = seed(x) / 2147483647

[OS2][ENT]
 
RANK(c) RANK returns a FIXED BINARY(15) result. "c" is a character string of length one. RANK(c) is equivalent to the following:
INDEX( COLLATE(), c ) - 1 [ENT][VAX]
 
REVERSE(s) REVERSE returns a nonvarying string containing all the characters or bits in the string value of "s" in reverse order. In the current version "s" must be or convert to a character string.[OS2][ENT][Multics][ANSI][VAX]
 
RIGHT(x,y[,z])  RIGHT returns a character string of length y, with character string x right-justified and padded on the left with the character z as needed. Y must be able to be converted to a FIXED BINARY value. Z should be a single character. If z is omitted, a space will be used. [OS2][ENT]
 
ROUND(x,y)  ROUND returns the value of x rounded in the y'th digit. If y is zero the last digit is rounded. The result has the same base, scale, and mode as x.

If x is FLOAT, ROUND functions the same as in IBM Enterprise PL/I with the option "USAGE(ROUND(IBM))", that is, it is a no-op, and x is returned.
 

RTRIM(x[,y]) RTRIM returns the character value of 'x' with all trailing characters in the character value of 'y' trimmed. If 'y' is omitted a string consisting of a single blank character is used. If 'y' is the null string no trimming occurs. If 'x' is the null string a null string is returned. [Multics][VAX]
 
SEARCH(x,y) 'x' and 'y' are converted to character strings. SEARCH returns the position in the character value of of 'x' of the first matching character in the character value of 'y'. Characters in 'y' are taken left to right, and the position of the first match in 'x' is returned. If 'x' or 'y' is the null string or if none of the characters in 'y' occurs in 'x', zero is returned. [ENT][Multics][OS/2][VAX]
 
SIGNED(x[,p[,q]]) 'x' is converted to SIGNED FIXED BINARY(p,q). If p is omitted it defaults to the maximum size of a signed fixed binary value. If q is omitted it defaults to zero. [ENT][OS2]
 
STRING(x) The STRING builtin function returns a string that is the concatenation of all elements of 'x'. Note that this is not identical to the STRING builtin from IBM PL/I for MVS&VM 2.3 for all arguments.
  • If 'x' is a scalar bit-string value, the result is x.
  • If 'x' is a scalar value of any other computational type, the value is converted to 'char(*)' and the converted value is the result. The '*' means "exactly enough characters to represent the value of x when it has been converted to a 'character' value".
  • If 'x' is an aggregate value whose components are unaligned, nonvarying, bit-string variables, the result is a scalar bit-string value that is the concatenation of all of the components of the value of 'x'.
  • If 'x' is an aggregate value whose components are unaligned, nonvarying, character-string or numeric-pictured variables, the result is a scalar character-string value that is the concatenation of all of the components of the value of x.
  • If none of these cases apply, then the reference is invalid.
The STRING pseudovariable assigns a string to 'x' as if 'x' were a string scalar. 'x' must designate one of the following:
  • A scalar string variable that is 'nonvarying'.
  • An aggregate variable whose components are all string variables of one type (either 'character' or 'bit', but not a mixture of 'character' and 'bit') and are all 'nonvarying' and 'unaligned'
If the total number of bytes or bits occupied by the variable designated by 'x' is m, then the pseudo-variable is interpreted as follows:
  • First, the value assigned to the pseudo-variable is converted to 'bit(m)' or 'char(m)', depending on the type of 'x'.
  • Second, if 'x' is a scalar, the converted value is assigned to it directly; otherwise, the converted value is assigned in such a way that the concatenation of the components of the aggregate variable 'x' are identical to the given converted value.
[Multics]
 
TRIM(x[,y[,z]]) TRIM returns the character value of 'x' with all leading characters in the character value of 'y' and all trailing characters in the character value of 'z' trimmed. If 'y' and/or 'z' are omitted a string consisting of a single blank character is used. If 'y' or 'z' is the null string no trimming at the corresponding end occurs. If 'X' is the null string a null string is returned. [OS2][ENT][VAX]
 
UNSIGNED(x[,p[,q]]) 'x' is converted to UNSIGNED FIXED BINARY(p,q). If p is omitted it defaults to the maximum size of an unsigned fixed binary data value. If q is omitted it defaults to zero. [ENT][OS2]

The PLISRTx builtins

Iron Spring PL/I provides builtin functions to perform a sort function. These are similar, but not exactly identical, to the corresponding routines supplied by IBM PL/I. Only the function PLISRTD is currently available to perform a sort of data supplied by a user-written procedure and passing sorted data to a second user-written procedure. Iron Spring PLISRTD performes an in-memory sort, so sufficient storage must be available. Full documentation of PLISRTD is available in the document Iron-SpringSortRoutine.pdf.


User-callable PL/I Library Procedures

Most of the procedures in the PL/I runtime library (prf.lib or libprf.a) are for use by the compiler and are not user-callable. The following, however, may be useful:

gmtime converts a calendar time (seconds since January 1, 1970) to date and time in a tm structure.

DECLARE gmtime ENTRY( PTR, PTR )
               RETURNS( PTR )
               EXT( '_pli_GMTime' );
DECLARE pTm   PTR;
DECLARE pTime PTR;
pTm = gmtime( addr(time), addr(tm) );
time
a fixed bin(31) field containg the calendar time.
tm
a tm structure which will contain the result.
pTm
a pointer which will contain the address of the tm field as passed, or sysnull if an error occurred.
The definition of tm can be found in the library include directory as "tm.inc".
No check is made that the area supplied is long enough to contain all the data.
Note that this procedure is the equivalent of unix' gmtime_r.

mktime converts time and date from a tm structure to calendar time.

DECLARE mktime ENTRY( PTR )
               RETURNS( FIXED BIN(31) )
               EXT( '_pli_MKTime' );
DECLARE pTm   PTR;
DCL     time  FIXED BIN(31);
time = mktime( addr(tm) );
time
a fixed bin(31) field which will contain the calendar time, or -1 if an error occurred.
pTm
The address of a tm structure containg the date to be converted.
The definition of tm can be found in the library include directory as "tm.inc".
The last three fields- wday, yday, and isdst- are ignored.

osdelete deletes a file by name.

DECLARE osdelete ENTRY( CHAR(*) VAR )
                 RETURNS( FIXED BIN(31) )
                 EXT( '_pli_OSDelete' );
DECLARE rc FIXED BIN(31);
RC = osdelete( filename );
filename
a character string variable or constant containing the name of the file to be deleted.
rc
a FIXED BIN(31) data element which will contain the error code from DosDelete.
0 indicates no error.
2 indicates that the file was not found.
for other errors see the OS/2 Operating System Control Program Guide and Reference.

osgetenv returns the address of the value of a system environment variable.

DECLARE osgetenv ENTRY( PTR )
                 RETURNS( PTR ) )
                 OPTIONS( LINKAGE(SYSTEM) )
                 EXT( '_pli_OSGetEnv' );
DECLARE (p,q) PTR;
DECLARE EnvVarName char(16);
EnvVarName = 'TZ' || '00'x;
q = addr(EnvVarName);
p = osgetenv( q );
q
a pointer to the null-terminated nonvarying name of an environment variable whose value is to be returned.
p
a pointer which will contain the address of the null-terminated nonvarying value of the named environment variable, or sysnull() if the variable was not found;

osgetprocinfo returns information about the current process and thread.

DECLARE osgetprocinfo ENTRY( PTR )
                 RETURNS( FIXED BIN(31) )
                 OPTIONS( LINKAGE(SYSTEM) )
                 EXT( '_pli_OSGetProcInfo' );
DECLARE ret_len FIXED BIN(31);
DECLARE pProcInfo PTR;
ret_len = osgetprocinfo( pProcInfo );
A structure PLIProcInfo.inc is declared in lib/include/pliprocinfo.inc for use with this procedure. Note that the structure is declared BASED, the caller should declare a STATIC or AUTO structure "like" PLIProcInfo.

before calling osgetprocinfo, the caller should set pi_len to the length of the data to be returned, from 4 to 40 bytes. pProcInfo in the call may be ADDR(your_procinfo_structure), or a pointer to the structure. On return, the structure will be filled in up to the value of pi_len, and the length of data supplied will be returned.

pi_pid and pi_tid will be available for both Linux and OS/2. pi_uid and pi_gid will be available only for Linux.

 dcl    1 PLIProcInfo         based,
          5 pi_len            fixed bin(31),      /* Size of area    00*/
	  5 pi_pid            fixed bin(31),      /* Process id      04*/
	  5 pi_tid            fixed bin(31),      /* Thread id       08*/
	  5 fil1              char(20),           /* Reserved        0C*/
	  5 pi_uid            fixed bin(31),      /* User id         20*/
	  5 pi_gid            fixed bin(31);      /* Group id        24*/

oskillthread Kills a thread using DosKillThread [OS/2] or SIGTERM [Linux].

DECLARE tid FIXED BINARY(31);
         DECLARE kill_thread ENTRY( FIXED BINARY(31) )
                             OPTIONS( LINKAGE(SYSTEM) ) 
                             EXT( '_pli_OSKillThread' ); 
         CALL kill_thread(tid);                       
tid
the thread id of the thread to be terminated.

syscall performs a Linux system call.

DECLARE syscall ENTRY
                RETURNS( FIXED BINARY(31) )
                EXT( '_pli_Syscall' );

Syscall is called with a variable number of arguments, and returns either a FIXED BINARY(31) value or a POINTER, depending on the specific call. The first argument is always the call number. A description of Linux system calls is beyond the scope of this Guide. Zero to six additional arguments for the call may be provided, all either FIXED BINARY(31) or POINTER. The kernel returns a single result or an errno. If the call returns an error, the result is the negative value of "errno". If a POINTER result is expected use the PTRVALUE builtin to convert from FIXED BINARY to POINTER.

sysid returns a pointer to a varying string indicating the operating system the caller is currently running on.

DECLARE sysid ENTRY
                RETURNS( POINTER )
                OPTIONS( LINKAGE(SYSTEM) )
                EXT( '_pli_Sysid' );

The returned value points to a varying character string that may have any declared length. This The string is guaranteed contain

tempnam generates a unique name for a temporary file.

DECLARE tempnam ENTRY( CHAR(*) VAR, CHAR(*) VAR, CHAR(*) VAR )
                RETURNS( CHAR(260) VARYING )
                EXT( '_pli_Tempnam' );
DECLARE temp_filename CHAR(260) VARYING;
temp_filename = tempnam( sDir, sPfx, sSfx );
sDir
a VARYING CHARACTER string identifying the directory in which the file is to be located. If this is the null string the file will be located in the current working directory.
sPfx, sSfx
VARYING CHARACTER strings containing the prefix (sPfx) and the suffix (sSfx) to be attached to the generated name. Either or both may be the null string. The generated name will be:
   'sPfxpppppppp-tttttttt-uuuuuuuusSfx'
where 'pppppppp' is the hex value of the current process id (pid), 'tttttttt' is the hex value of the current thread id (tid), and 'uuuuuuuu' is a unique number within this process. If another file with this name already exists, the unique number is incremented until the generated name does not confilct with an existing file.
temp_filename
In the example this represents a VARYING CHARACTER string which will contain the generated unique name.

Mutex semaphore procedures. The following procedures implement "mutex" ( mutual exclusion) semaphores:

_pli_mutex_init
Initialize a mutex. calling sequence is
declare <name> entry(pointer, fixed bin(31)) 
             options( linkage(system) ) 
	     external( '_pli_mutex_init' );
call <name>(phMutex,init);
where <name> is the declared name of the entry whose external environment name is '_pli_mutex_init'. 'phMutex' is a pointer to a FIXED BINARY(31) data item where the handle assigned to this mutex is to be returned, and 'init' is a fixed bin(31) initial value for the mutex. 'init' is currently ignored for OS/2. For Linux 'init' should be 0 for an unowned mutex and 1 for owned. A mutex must be initialized before it can be used.

_pli_mutex_destroy
Free a mutex. The calling sequence is
declare <name> entry(pointer) options( linkage(system) ) external( '_pli_mutex_destroy' );
call <name>(phMutex);
where <name> is the declared name of the entry and 'phMutex' is a pointer to a FIXED BINARY(31) data item where the handle assigned to this mutex is stored. A mutex should be destroyed when it is no longer needed. The mutex should not be active when destroyed.

_pli_mutex_wait
Acquire a mutex. The calling sequence is
declare <name> entry(pointer) options( linkage(system) ) external( '_pli_mutex_wait' );
call <name>(phMutex);
where <name> is the declared name and 'phMutex'is a pointer to a FIXED BINARY(31) data item where the handle assigned to this mutex is stored. If the mutex is not owned by another caller this task is given ownership and continues execution. If it is owned, this task waits until the mutex is posted. An attention interrupt will also terminate the wait and raise the ATTENTION condition.

_pli_mutex_timedwait
Acquire a mutex but only wait a specified time. The calling sequence is
declare <name> entry(pointer,pointer) options( linkage(system) ) external( '_pli_mutex_timedwait' );
call <name>(phMutex,pTimespec);
where <name> is the declared name, 'phMutex'is a pointer to a FIXED BINARY(31) data item where the handle assigned to this mutex is stored, and 'pTimespec' is a pointer to a structure defined by 'timespec.inc'. If the mutex is not owned by another caller this task is given ownership and continues execution. If it is owned, this task waits until the mutex is posted. If the time expires while waiting the wait is terminated; an attention interrupt will terminate the wait and raise the ATTENTION condition.

_pli_mutex_post
Release a mutex. The calling sequence is
declare <name> entry(pointer) options( linkage(system) ) external( '_pli_mutex_post' );
call <name>(phMutex);
where <name> is the declared name and 'phMutex'is a pointer to a FIXED BINARY(31) data item where the handle assigned to this mutex is stored. Post is called by the task owning the mutex to release ownership and wake up any waiting tasks.

hexdump generates a hex/character dump of a selected area of storage.

DECLARE hexdump ENTRY( PTR, FIXED BIN(31), CHAR(80) VAR )
                EXT( '_pli_Hexdump' );
call hexdump( pArea, lArea, sTitle );
pArea
a pointer to the area to be dumped.
lArea
the length of the area to be dumped, may be STG(area) if the area is a structure.
sTitle
the title to be used to identify this snapshot. sTitle="abc" will use the heading "DUMP OF AREA 'abc'".


Preprocessor Facilities

This section describes the preprocessor facilities currently built in to the compiler. The beta version of the full preprocessor is described elsewhere.

The current built-in preprocessor is limited to a subset of the IBM preprocessor statements. The listing control statements %PAGE, %SKIP, %PRINT, and %NOPRINT, the %INCLUDE statement, and the %REPLACE statement are described below.

%INCLUDE: The %INCLUDE statement instructs the compiler to read an external file of text and insert its contents in place of the %INCLUDE. The syntax is: "%INCLUDE <filespec>;".
<filespec> is any valid file specification. If no file extension is coded the compiler will search for files having the extensions ".cpy", ".inc", or no extension. The search path for the file is specified on the PLIC command (see Running the compiler).

When running on Linux, the compiler will first search for an exact case match; if not found the first matching file in any case will be included. Examples of the %INCLUDE statement are:


    %INCLUDE ABC; [will include "ABC", "ABC.CPY", or "ABC.INC" in your search path -
               on Linux, if no file ABC[.x] is found, will include the first
               found of Abc[.x], abc[.x], etc.]
    %INCLUDE "def.pli"; [will include "DEF.PLI" in your search path]
    %INCLUDE "c:\pli\includes\ghi.pl1"; [will include the named file only]

Nested includes are allowed, that is, the included file may itself contain %INCLUDE statements. The maximum depth of nesting is four levels.

%REPLACE: The %REPLACE preprocessor statement provides limited text substitution capability. The syntax is "%REPLACE <identifier> BY <constant>;".
<identifier> is any word which would be valid as an identifier in a PL/I program. PL/I keywords are not allowed. Multiple %REPLACE statements may specify the same identifier, with the most recently-occurring one being in effect.
<constant> is any valid character-string, bit-string, or arithmetic constant.

The scope of the %REPLACE statement is between its occurrence in the input stream to the end of the source program, including any included files. The effect is to substitute the value of <constant> in the program anywhere <identifier> appears.

For example, the following sequence of statements:

 %REPLACE abc by 1;
     put edit(3+abc)(f(5));
will result in the value 4 being output by the PUT statement.

The %REPLACE statement above is equivalent to the following, but does not require a full preprocessor scan:

 %DECLARE abc CHARACTER;
     %abc = '1';

%PRINT: The %PRINT statement turns on (enables) printing of the source listing. The %PRINT statement itself will not be printed if the listing was previously disabled.

%NOPRINT: The %NOPRINT statement turns off (disables) printing of the source listing. The %NOPRINT statement itself will be printed if the listing was previously enabled.

%PAGE: The %PAGE statement causes a page eject in the source listing after printing of the %PAGE statement, if the listing is enabled.

%SKIP: The %SKIP[(n)] statement will print 'n' blank lines (n=1 to 9) in the source listing following the printing of the %SKIP statement, if the listing is enabled. If 'n' is omitted, the default is one line.

%CONTROL: %CONTROL is a listing control statement from the IBM PL/I Checkout compiler. It is recognized and ignored.


Run-time considerations

Accessing unprocessed command arguments
Iron Spring PL/I converts command-line arguments passed by the Linux shell into a varying character string for compatibility with other PL/I compilers. To access the unprocessed argc and argv information include the member "argc.inc" from the PL/I runtime library. For OS/2, COMMAND.EXE provides only the program name and the unparsed argument string only as arguments.
     /* %include argc.inc includes the following declarations */
     dcl 1 argc_s             ext( '_pli_argc' ),
          5 argc              fixed bin(31),      /* Arg count        */
          5 ppargv            ptr,                /* **argv           */
          5 ppenv             ptr;                /* **envp           */
         
     dcl arg_addr        (0:1)ptr        based;   /* *argv[]          */
Run-time messages
All run-time messages are written to stderr:

See Debugging for SNAP and PLIDUMP output.

The MAIN Procedure
The symbol '_pli_Main' is an alias for the first entry point of the main procedure. The actual entry point of a PL/I program is the runtime library function '_pli_Start', which has the alias 'main' for compatibility with C.

The MAIN procedure is called with one argument: a CHARACTER(*) VARYING string containing the [possibly edited] image of the command line that invoked the program. Since a "command line" as such doesn't exist for Linux, this parameter is manufactured by concatenating all of the "argv" values, separated by spaces.

The sample program 'numwrd.pli' is an example of a program that uses the command-line.

Memory utilization
Generated PL/I code does not modify STATIC storage except during program initialization. All code is reentrant unless the user program modifies STATIC storage in such a way as to invalidate this.

PL/I programs use the stack for activation records for procedures and BEGIN-blocks, including all AUTOMATIC storage. The heap is used for all allocated BASED and CONTROLLED storage, for control information for error-handling, and for file information and buffers.

For OS/2 the stack size is taken from the value stored with the executable, either by the linker, by EXEHDR, or a default minimal value of 16K bytes. Stack storage is allocated when the program is loaded, but is not committed until actually used. Stack probes are employed to prevent traps when more than 4K is requested at one time. Linux allows all available memory below the address where the program is loaded to be used for stack, and doesn't require stack probes.

For OS/2 the heap size is currently fixed at 1MB (1024K), although a future release will allow this value to be respecified at run time. In the interim, if a larger heap is required, the library procedure INIT can be re-assembled specifying a larger value for the data item 'def_heap_size'. Linux allows all available memory above the end of the BSS section to be used for the heap.

The layout of the stack frame for one procedure or Begin-block is illustrated below.


                    ^
                    | |                                          |
                  +08 |  Arguments (see program linkage below)   |  Higher addresses
                      +------------------------------------------+        |
                  +04 |  Calling procedure's EIP                 |        |
                      +------------------------------------------+        |
               EBP+00 |  Calling procedure's EBP                 |        |
                      +------------------------------------------+        |
                  -04 |  PL/I control information                |        |
                    | |  [see library include file 'dsa.inc'     |        |
                    v |   for layout (subject to change)]        |        |
                      |           ...                            |        v
                      +------------------------------------------+  Lower addreses
                      |  AUTOMATIC non-adjustable data           |
                      |  for this block                          |
                      |           ...                            |
                      +------------------------------------------+
                      |  AUTOMATIC adjustable data               |
                      |           ...                            |
                      +------------------------------------------+
                      |  Temporaries, argument lists             |
                                  ...

Program Linkage
The standard PL/I calling sequence passes all arguments by reference, that is by passing their addresses and, optionally, the addresses of their descriptors. This means that changes to the values of parameters in the called procedure are reflected back to the caller. Descriptors are passed for structure, array, and string arguments; normally descriptors are not passed for arithmetic arguments, although PL/I runtime procedures may require a descriptor. The library include file 'desc.inc' shows the layout of descriptors for various types of data [subject to change in future releases]. When the argument is a constant, an expression, or the data type doesn't match the corresponding parameter, a dummy argument is created and placed on the stack; the called procedure is passed the address of the dummy. In this case, changes to the values of parameters change the dummy argument and don't modify the actual argument.

Descriptors for non-adjustable data are normally stored in STATIC storage, descriptors for adjustable data are created during block initialization and stored in the current stack frame. Descriptors for expressions whose attributes are not known at compile-time (e.g. SUBSTR(s,1,j) will be created on the stack as required.

All arguments are pushed on the stack right to left (leftmost arguments in lower addresses). The last [highest-addressed] argument passed points to an area on the stack to receive a possible result. The caller cleans up the argument list following return.

The called program is responsible for saving registers EBP, and EBX, ESI, and EDI. It is the caller's responsibility to save any other registers used. If the called entry is specified with OPTIONS(ASM), the caller saves and restores the 80x87 FPU control register. The calling program passes the size of the argument list in DWORDs in AL.

Arguments for 'SYSTEM' and "OPTLINK' are passed by value. All arguments, normally limited to arithmetic data, pointers, and nonvarying strings, are evaluated and the values are passed to the called procedure; descriptors are not used. Changes to the values of parameters do not cause changes in the values of the corresponding arguments. Returned values from function procedures may be returned on the stack or in EAX or ST(0).

Two other pieces of information are also passed to a called PL/I procedure. The static backchain is passed in ESI, and the address of the Program Global Table (PGT) is passed in EDI. These are passed for both "system" and PL/I linkage unless the called entry is specified with OPTIONS(ASM). The PGT is a vector table containing the addresses of some required runtime routines. The static backchain is the EBP value of the block which lexically contains the called procedure. This allows the called procedure to reference automatic data belonging to containing blocks. This is not necessarily the same as the EBP value of the caller. For example, consider the following:


  A: PROC;
    CALL B;
    B: PROC;
      CALL B;
      C: BEGIN;
         END;
      END B;
    END A;
A is always the 'containing' block of B, regardless of whether B is called from A or from itself recursively. B is the containing block of block C. Using the static backchain, C can address AUTOMATIC data from B and A, and so on.

The stack layout for a standard PL/I call is illustrated below.

 

                      +--------------//--------------------------+                  Higher addresses
                      |  Storage for returned value              |                        |
                      +------------------------------------------+ <--------+             |
                                                                            |             |
 (optional, locator/  +------------------------------------------+          |             |
  descriptor pair     |  Address of descriptor                   | --->     |             |
  for returned value) +------------------------------------------+          |             |
                      |  Address of data                         | ---------+             |
                      +------------------------------------------+                        |
                                                                                          |
 (optional)           +-------------//---------------------------+                        |
                      |  Dummy arguments                         |                        |
                      |            ...                           |                        v
                      +------------------------------------------+                  Lower addreses

 (optional, locator/  +------------------------------------------+
  descriptor pair     |  Address of descriptor                   | --->
  for arguments       +------------------------------------------+
  requiring a         |  Address of data                         | --->
  descriptor -        +------------------------------------------+ <------+
  one per argument)                                                       |
                                                                          |
 (optional)           +-------------//---------------------------+        |
                      |  Saved values of 'live' registers        |        |
                      |  not saved by the called procedure       |        |
                      |             ...                          |        |
                      +------------------------------------------+        |
                                                                          |
                      +------------------------------------------+        |
                      | Argument list, one address per argument. |        |
                      | This address points to either the data   |        |
                      | or the address of the locator/           | -------+
                      | descriptor pair for the data.            |
                      | The last (highest addressed) argument    |   -or-  
                      | identifies the returned value.           | -------> argument
         ESP ----->   +------------------------------------------+
         

EXTERNAL data (not EXTERNAL ENTRY) is not shared between executable and DLL code, nor among procedures in different DLLs. For example, data declared DECLARE a EXTERNAL POINTER; will identify different "a"s in procedures linked into in the executable, in DLL "1", and DLL "2". Within the executable or any one of the DLLs all occurrences of that declaration will identify the same "a". Each level-1 external data declaration generates a segment for the linker.


Oncodes


                  Oncode
Condition         Value   
finish              4
error               9
name               10
record             20
transmit           40
key                50
endfile            70
undefinedfile      80
endpage            90
pending           100
stringsize        150
overflow          300
fixedoverflow     310
zerodivide        320
underflow         330
size              340
stringrange       350
area              360
attention         400
storage           450
condition         500
check             510
subscriptrange    520
conversion        600


Efficient Programming

  1. Avoid use of the ENTRY statement. The PACKAGE statement is a more efficient way of grouping multiple externally-callable entry points in a single compile unit. If you do have a procedure with multiple entries, try to minimize the number of RETURN statements.
  2. Avoid use of data declared FIXED DECIMAL OPTIONS(IBM).
  3. Any use of the GET and PUT statements, including GET STRING and PUT STRING, in a statically-linked program causes library conversion and format-processing code to be included. If there are only have one or two GET or PUT statements, consider using record I/O and formatting the data yourself. The PL/I runtime library itself does not use any stream I/O.
  4. Read and write as much data as possible with a single GET or PUT statement. Instead of:
    
      PUT LIST(a);
      PUT LIST(b);
    use:
      PUT LIST(a,b);
    
  5. Use "adjustable" strings and arrays only when necessary.
  6. The SUBSCRIPTRANGE condition is intended for debugging and it's use in a production program is discouraged.
  7. Use the SIZE and STRINGSIZE prefixes only when necessary as they can generate a lot of code. Apply the prefix to the smallest piece of code necessary (procedure, block, or single statement).
  8. When possible provide your own checks to prevent computational conditions such as FIXEDOVERFLOW for FIXED DECIMAL data rather than relying on an ON-unit.
  9. Aggregate asssignment to very large AUTOMATIC arrays, with the initial attribute or in an assignment statement, may exceed the compiler's available storage. Use the "PLIFILL" builtin if possible or initialize them in a loop.
  10. The compiler "-N" option (statement offset table) requires 8 bytes per executable statement plus 10 bytes, plus the length of the external procedure or package name.
  11. Array assignments by default generate inline code. For large arrays it is more storage-efficient to code a loop and assign the elements individually. Note the the compiler -O option will use a single block move instruction to assign an entire array if the source and target attributes match.
  12. Minimize references to self-defining structures (REFER option) and iSub-defined arrays, since each reference involves a subroutine call.

Debugging PL/I programs

Debuggers
PL/I code has no special requirements for a debugger. On OS/2, the WATCOM® debugger may be used to debug PL/I programs; on Linux gdb works well.
Source-level debugging is not yet available, but since the compiler can generate assembly-language output, debugging at the assembly level is straightforward.

Sometimes it may be desirable to compile-in a breakpoint to be activated when a specific location is reached or condition occurs. The following statement inserted in your source will activate a breakpoint when it is reached when running under control of a debugger:

 *PROCESS DBG(INT3); 
If the resulting executable is not run under a debugger, the interrupt will be ignored. Nevertheless, INT3 traps should not be left in a production program.

SNAP traceback
The PL/I statement "ON CONDITION(xxx) SNAP..." will generate a traceback on stderr when the specified condition is raised. Beginning with the procedure generating the traceback (_pli_Trace) and working backwards, the trace lists the address of the active entry point of the procedure, the address of the statement in the calling procedure that called that entry, and the name of the entry point if available.

PLIDUMP
The PL/I statement "CALL PLIDUMP( "<options>", "<title>" ); will generate debugging output on stderr when executed.
"title" is a character string used as the
title of the dump.
"options" provide one or more dump options:
'S' = terminate after dump
'C' = continue after dump
'T' = print traceback
'B' = dump automatic storage.
'H' = dump static storage (not yet implemented).
'F' = dump opened file information (not yet implemented).

Options may be combined and may appear in any order, for example "TCB" means: print trace, continue after snap, and dump all automatic storage. Case is not significant. Unrecognized options are ignored.

The stack is dumped from the current procedure (_pli_Dump) [lowest address] to the PL/I startup code (_pli_Init) [highest address]. Storage for each procedure is dumped in three sections (see sample segment of PLIDUMP output below): Temporaries (argument lists and storage used during expression evaluation), Data (AUTOMATIC variables and other non-temporary user data in the stack frame), and DSA (system data in stack frame). The layout of the DSA is given in lib\include\dsa.inc. This is subject to change in future releases.

If the dump is issued as result of a system exception or trap, trap information will appear immmediately before the DSA of the procedure in which the exception occurred. The line beginning "System Code" will display the system error code and description.

Each data line includes the actual address of the first byte, the hex offset (±EBP for DSA and data, +ESP for temporaries), up to sixteen bytes of data in hex as actually stored in memory (no byte-swapping), and the ASCII representation of the same data.

The DSA display provides additional formatted information regarding the ON-conditions enabled for this block and identifies runtime-library procedures.

Sample PLIDUMP output

***error dump***

Address  Caller   Entry name
           ... omitted from the example ...

OS/2 System Trap Information
  System Code=C0000005 XCPT_ACCESS_VIOLATION
  Exception Address=000100D7
  EAX 44000B00  EBX 00020000  ECX 00000000  EDX 44000B00
  ESI 00000000  EDI F8070200
  DS  0053      ES  0053      FS  150B      GS  0000    
  CS:EIP 005B:000100D7  SS:ESP 0053:00048600  EBP 00048628
  EFLAGS 00012216

0001868A 00027402 FTPRO
 
Temporaries at 003C80C4
003C80C4 ( 000000) 74823C00 5C863C00 98823C00 3C813C00 |t.<.\.<...<.<.<.|
003C80D4 ( 000010) 53003C00 F4803C00 5C863C00 A8A2021C |S.<...<.\.<.....|
           ... omitted from the example ...
003C83B4 ( 0002F0) 3C843C00 00000000 3C843C00 C4833C00 |<.<.....<.<...<.|
003C83C4 ( 000300) 64140500                            |d...|            

Data at 003C83C8
003C83C8 (-000074) 786D0500 00843C00 3E350400 E4C34300 |xm....<.>5....C.|
003C83D8 (-000064) 00000000 30823C00 0083E180 B8813C00 |....0.<.......<.|
003C83E8 (-000054) 0000E181 E4C34300 00000000 B5570400 |......C......W..|
003C83F8 (-000044) 31000000 10843C00 786D0500 80853C00 |1.....<.xm....<.|
003C8408 (-000034) 12720200 600F3C00 01001500 00001B00 |.r..`.<.........|
003C8418 (-000024) E0320500                            |.2..|            

DSA at 003C841C
Enabled: CONV FOFL OFL UFL ZDIV 
003C841C (-000020) C8833C00 0000E180 E4C34300 00000000 |..<.......C.....|
003C842C (-000010) 8A860100 31000000 48843C00 786D0500 |....1...H.<.xm..|
003C843C ( 000000) 80853C00 02740200                   |..<..t..|        

00026C79 00010123 PH10
 
Temporaries at 003C8444
003C8444 ( 000000) 48843C00 11000000 42000000          |H.<.....B...| 
           ... omitted from the example ...

Using the procedure map
The procedure map can be used in conjunction with a runtime error message, SNAP output, or PLIDUMP to determine the line in the program causing the error.

If the error occurs in your PL/I program the runtime error message will identify the source line causing the error, if the procedure map was generated at compile time. If the error occurs in a library procedure this information is less meaningful [in a future release the message will identify the source line where the library procedure was invoked]. Given the SNAP output or corresponding PLIDUMP information and the link map the offset in your code that called the library procedure can be computed. This offset can then be used with the procedure map to determine the line number which originally caused the error. The offset in the map is the offset in the program of the first machine-language instruction for the source line identified by line. The computed offset between that shown in the map and the offset for the next line will identify the appropriate line number. Program initialization code is included in a separate area of code labeled "Initialization" following the END statement of the procedure or BEGIN-Block for which the initialization is being performed.

  Offset  Line Statement type             Offset  Line Statement type             Offset  Line Statement type           
    3261  1187 PROCEDURE                B2D
    3276  1192 IF                           3285  1192 RETURN                       3294  1193 Assignment               
    329D  1194 IF                           32AA  1194 DO                           32AA  1195 Assignment               
    32AE  1196 Assignment                   32B7  1197 END                          32B7  1198 DO                       
    32ED  1199 IF                           3304  1199 LEAVE                        330C  1200 END                      
    3311  1201 IF                           331B  1201 Assignment                   3324  1202 RETURN                   
    3332  1203 END                          3337  1203 Initialization               3347  1214 END                      


version 1.3.0, 15 Apr 2024
http://www.iron-spring.com