Peter Elderon from the IBM compiler development team will be presenting on what's new in Enterprise PL/I for z/OS at SHARE in Anaheim, March 9 to 14.
15284: PL/I - What's New
Register for SHARE in Anaheim today!
Check out all the information about the latest release of PL/I for AIX. You can get a summary of the release as well as all the announcement details.
See what's new with PL/I for AIX V3.1
including enhancements to leverage the latest POWER7 hardware and interoperability with the latest middleware.
For all the details about the PL/I for AIX, V3.1 announcement, see IBM PL/I for AIX , V3.1 delivers support for the latest IBM POWER7 Systems architecture as well as many functional improvements and usability enhancements
Check out all
the information about the latest release of Enterprise PL/I for z/OS. You can
get a summary of the release as well as all the announcement details.
Check out all
the information about the latest release of Enterprise PL/I for z/OS.
You can get a summary of the release as well as all the announcement
See what's new with Enterprise PL/I for z/OS, V4.3 including enhancements to
leverage the latest z/OS. For all the details about the Enterprise
PL/I for z/OS, V4.3 announcement, see IBM Enterprise PL/I for z/OS V4.3 delivers performance improvements and usability enhancements. (Author: Stella Zhou, Carol Hu)
The history of PL/I stretches back to the 1970s when IBM, at the prompting of the SHARE user group, delivered the first PL/I compiler so that users would have a language with the combined strengths of Fortran and COBOL. IBM then delivered three releases of OS PL/I Version 2 in the 1980s and PL/I for MVS and VM in the early 1990s. All of these releases were based on a common code base that gradually became old, plus both hard and expensive to enhance and maintain.
However, with the advent of the PC, IBM built a completely new PL/I compiler that was shipped first on IBM® Operating System/2® (OS/2) and then ported to Microsoft Windows, the IBM® AIX® operating system, and the mainframe. With this compiler and the 12 consecutive years with a new release of the IBM® z/OS® version, IBM has improved optimization of PL/I programs, enhanced their exploitation of IBM® System z® architecture, addressed many customer requirements, and introduced numerous application modernization features.
IBM continues to have a strong commitment to the PL/I language, particularly given its widespread use in many business-critical applications. The current family of IBM PL/I implementations consists of the Enterprise PL/I compiler for the mainframe and the PL/I for AIX compiler, both of which share a common, nearly identical front-end code base, which ensures portability between those platforms.
We are opening this forum to create more direct communication between users of our PL/I compilers and the IBM compiler development organization. We hope you'll find the content informative and interesting, and we look forward to your contributions through questions, comments, and ideas.
Modified on by XifangZhang
Enterprise PL/I for z/OS V5.1 documentation is refreshed to include the following enhancements:
Enhancements in usability:
- The compiler now flags unreachable ASSERT UNREACHABLE statements with a different message than it flags other unreachable statements.
- The compiler now expands in the AGGREGATE listing typed structures that are member of other structures.
- The attributes listing now shows the contents of the VALUE attribute for CHARACTER and BIT constants of length 256 or less and also for numeric PICTURE constants.
New or changed statements and conditions:
- The ASSERT statement now supports a new ASSERT COMPARE statement that provides a more information-rich way to test an assertion that the actual value of an expression compares correctly with an expected value.
- The new ASSERTION condition will be raised when an ASSERT statement fails and the ASSERT(CONDITION) compiler option is in effect.
- The PROCEDURE statement and ENTRY statement now support the EXTERNAL attribute as an optional argument.
New or changed built-in functions, pseudovariables, and subroutines:
New built-in functions:
Updated built-in functions:
- The LOWERCASE and UPPERCASE built-in functions now accept a second optional argument, so that you can specify a code page that all characters will be converted to their lowercase equivalent or uppercase equivalent. All A-Z/a-z values and Ä-unlaut/ä-umlaunt values will be converted to their equivalents.
- The REPATTERN and TIMESTAMP built-in functions now can be used to obtain the current date and time in the z system format of YYYY-MM-DD HH:MI:SS.999999.
- The ROUNDDEC built-in function has been renamed as ROUNDAWAYFROMZERO.
For details, see Summary of Changes (Changes in GC27-8930-00, April 2017).
Compiler option enhancements:
- The new ASSERT compiler option controls whether ASSERT statements call a default library routine that will raise the ASSERTION condition or a routine provided by the user.
- The new CASE compiler option controls whether some names will be shown in uppercase or in the same format as they appear in the source program.
- The CMPAT(LE) and CMPAT(V1) have been restored in the CMPAT compiler option.
- The RULES compiler option has these new suboptions:
- LAXINTERFACE | NOLAXINTERFACE
- MULTIENTRY | NOMULTIENTRY
- MULTIEXIT | NOMULTIEXIT
- UNREFCTL | NOUNREFCTL
- UNREFDEFINED | NOUNREFDEFINED
- UNREFENTRY | NOUNREFENTRY
- UNREFFILE | NOUNREFFILE
- UNREFSTATIC | NOUNREFSTATIC
- YY | NOYY
- The NOELSEIF suboption of the RULES compiler option now also flag ELSE statements immediately followed by an IF statement that is enclosed in a simple DO-END.
- The CICS preprocessor output now includes a listing of all the CICS options in effect when the preprocessor run.
Compiler and Run-Time Migration Guide
A number of new/updated compiler messages have been added to Messages that are introduced with V5R1.
Messages and Codes
New/updated Compiler Informational Messages:
IBM2831I I, IBM3000I I, IBM3019I I - IBM3021I I, IBM3024I I. For details, see Compiler Informational Messages (1000-1076, 2800-2999, 3000-3100).
New/updated Compiler Error Messages:
IBM2465I E - IBM2475I E. For details, see Compiler Error Messages (1226-1499, 2400-2599).
New/updated Compiler Severe Messages:
IBM2300I S - IBM2310I S. For details, see Compiler Severe Messages (1500-2399).
New/updated Condition Codes:
430-436. For details, see Condition codes 1 through 500.
Along with the PL/I for z/OS V5.1 Knowledge Center refresh, the V5.1 PDF files are also refreshed to include the updates above. You can find the latest V5.1 PDF files from the Enterprise PL/I for z/OS library page at http://www-01.ibm.com/support/docview.wss?uid=swg27036735
Note: Only the English versions are refreshed. The Japanese versions will not be refreshed until the next product GAs.
Pl/I 4.5 added a new option to help you check the correctness of your code when it runs, and I will describe it in a future blog. But first I would like to write
about some existing features (some new and some quite old) that can help you check the correctness of your code (and, yes, compile times checks of your code are even
better, and I will also talk about them in a future blog).
One of the newest features that can help you check the correctness of your code (and simultaneously document the code as well) is the ASSERT statement. We introduced this in the 4.3 release. It is similar to C assert macro, but in PL/I, it is a statement with 3 forms:
ASSERT TRUE( <test-expression> ) TEXT( <display-expression> );
ASSERT FALSE( <test-expression> ) TEXT( <display-expression> );
ASSERT UNREACHABLE TEXT( <display-expression> );
where <test-expression> is an expression that should have the attributes BIT(1) and where the <display-expression> in the optional TEXT clauses is a string whose value you want to display or log (or whatever) when the assertion fails. For example, the statement
ASSERT TRUE( order_count > 0 ) TEXT( 'count of orders must be positive' );
claims that when this statement is hit then the value of the variable order_count will be positive. If it is not positive, the generated code will then pass to a routine (which you
must supply) the specified string along with 3 pieces of information that identify where the assertion is in your code, namely
the name of the package containing the statement
the name of the procedure containing the statement
the line number of the statement
Note that you must supply the routine that will be called; however, the Language Reference Manual contains samples. This gives you the flexibility to do whatever you want when an assertion fails. For example, in the compiler, we have assertions that are caught by these routines which we then use to write messages to your listing (and the messages contain the 3 pieces of information listed above in order to help us in identifying the underlying compiler problem).
ASSERT TRUE and ASSERT FALSE essentially provide the same function, but sometimes it is easier to write a test expression that should be false (and hence to use ASSERT FALSE) than to write the negated version of the test expression (which you would have to do if we provided only ASSERT TRUE).
ASSERT UNREACHABLE is a little different in that it does not test an expression - instead it asserts that your code will never reach this statement. It is very useful as the
OTHERWISE clause in a SELECT statement.
To help check the correct the correctness of your program, you can, for example,
add ASSERT statements to the beginning of all your procedures to check the values of any input parameters
add ASSERT statements after any CALL statements to check the values of any output parameters
add ASSERT statements after all function invocations to check the values returned
This will make it much more likely that testing will uncover any interface mismatches beyond simple prototype mismatches.
Moreover, if you are concerned about the performance impact of these statements (although the cost of not detecting a failed assertion in production code may be much greater than the savings in performance from compiling them out), you can easily remove them when you build the production version of your code by compiling with the IGNORE(ASSERT) compiler option.
When you use INLIST rather than a long list of comparisons OR'ed together, your code is easier to read and understand, but it is also easier for the compiler to understand and hence to optimize.
For example, for an invocation of INLIST such as
inlist( x, 2, 3, 5, 7, 11, 13, 17, 19 )
if x is a FIXED BIN(31) variable, the compiler will generate a branch table to determine the result.
And in general, if x is FIXED BIN(p,0) with p <= 31 or FIXED DEC(p,0) with p <= 9, if all the values to test are similarly nice constants, then the compiler will generate a branch table to speed up the evaluation.
If x is CHAR(1) and all the values to test are CHAR(1) constants, then the compiler will generate a very speedy table look-up.
But if all the arguments are CHAR(2) or CHAR(4) (or WCHAR(1) or WCHAR(2)), a series of compares is generated (since the values are unlikely to be "close" and any tables generated to speed up the evaluation would be huge).
However, the new z13 hardware introduced some new vector string handling instructions that can help here.
For example, suppose you had some code that looked like
dcl countryCode char(2);
if inlist( countryCode,
'AT', 'DE', 'CH', 'NL', 'DK', 'FI', 'SE', 'NO' ) then;
Before the z13, the compiler would have generated code such as
5810 1000 L r1,COUNTRYCODE(,r1,0)
4800 1000 LH r0,_shadow1(,r1,0)
A70E C1E3 CHI r0,H'-15901'
A784 0026 JE @1L13
A70E C4C5 CHI r0,H'-15163'
A784 0022 JE @1L13
A70E C3C8 CHI r0,H'-15416'
But under OPT(3) and ARCH(11), the ARCH option that indicates your code will run on a z13, four vector instructions and one branch do it faster and more simply!
5810 1000 L r1,COUNTRYCODE(,r1,0)
4100 0002 LA r0,2
E700 1000 0037 VLL v0,r0,_shadow1(r1,0)
E720 E000 0006 VL v2,+CONSTANT_AREA(,r14,0)
E700 2000 1082 VFAE v0,v0,v2,b'0001',b'0000'
E700 0001 2021 VLGV r0,v0,1,2
EC08 000B 007E CIJE r0,H'0',@1L4
Similarly, if the set of values to test has more than 8 elements, the compiler can use these new vectors instructions to search for a value via repeated compares of up to 8 values.
Analagous code will also handle INLIST with
4 or more CHAR(4) values
8 or more WCHAR(1) values
4 or more WCHAR(2) values
And, as you may have already noticed, the minimum number of test values when multiplied by the size of an individual value is 16, the same as the size (in bytes) of a z13 vector. When there are fewer test values, the set of repeated simple compares is faster, and the compiler will still generate them.
So, in summary, INLIST not only gives you more expressive PL/I language, but it makes it easier for the compiler to turn your programs into better performing code.
There are many times when you may want to test if a value is equal (or not equal) to one of a set of values. You can do this easily with a SELECT statement or an IF statement as in the following code
IF (EXPLAN.ACCESSTYPE = 'I ' |
EXPLAN.ACCESSTYPE = 'N ' |
EXPLAN.ACCESSTYPE = 'DX' |
EXPLAN.ACCESSTYPE = 'IN' |
EXPLAN.ACCESSTYPE = 'MH' |
EXPLAN.ACCESSTYPE = 'MI' |
EXPLAN.ACCESSTYPE = 'MU' |
EXPLAN.ACCESSTYPE = 'NR' |
EXPLAN.ACCESSTYPE = 'P ' |
EXPLAN.ACCESSTYPE = 'MX')
& ( EXPLAN.MATCHCOLS = 0 )
But the new INLIST built-in function makes it easy for you to simplify this code. This function returns a bit(1) value indicating if the value of an expression is equal to any of the values of the other arguments to the function.
More precisely, inlist( x, y1, y2, ..., yn ) is equivalent to (x = y1) | (x = y2) | ... | (x = yn), and inlist requires at least 3 arguments and a maximum of 63 test arguments (although this maximum count is arbitrary and can be increased if some user has a need for a greater number of test values).
This function would allow you to convert the above code into the much simpler
IF INLIST( EXPLAN.ACCESSTYPE,
& ( EXPLAN.MATCHCOLS = 0 )
It is also especially good for simplifying code that tests if a value is not in a set of values. So, for example, this piece of code
IF (KEYWORD ^= 'LVL=') & (KEYWORD ^= 'SID=')
& (KEYWORD ^= 'SET=') & (KEYWORD ^= 'TOP=')
& (KEYWORD ^= 'BEG=') & (KEYWORD ^= 'END=')
& (KEYWORD ^= 'BWN=') & (KEYWORD ^= 'EWN=')
& (KEYWORD ^= 'MBR=') & (KEYWORD ^= 'DSN=')
could be written as the more readable
IF ^ INLIST( KEYWORD,
Also, not only does this function make it easier for you to write simpler, clearer code (and that is always a good thing), the compiler can do a much better job of optimizing such code (and I will talk about that in my next post).
Finally, this built-in function became officially available with the new 4.5 release of Enterprise PL/I, and while it appears first in the documentation for that release, it is also available to users of the 4.3 and 4.4 releases who have current service applied.
While the ASSERT statement discussed in the previous post is relatively new to PL/I (since it was introduced only with the 4.3 release), there is another, much older statement than can be used in a similar fashion: the SELECT statement.
In particular, if you code a SELECT statement without an OTHERWISE clause, if none of the WHEN clauses are true, then the ERROR condition will be raised with an ONCODE and error message indicating that no WHEN clause in the SELECT is true.
For example, if you code
WHEN( order_count > 0 );
then this statement will behave very much like the statement
ASSERT TRUE( order_count > 0 );
However, the SELECT statement will compile with any release of the PL/I compiler, and this statement does not require that you provide a routine that handles the failure. The ASSERT does require that you provide such a routine. That requirement is a both a strength of the ASSERT statement (because it gives you greater flexibility) and a weakness (because it forces you to do a little more work). An additional plus for the ASSERT statement is that it is more self-documenting. I would probably recommend that you use the ASSERT statement in situations like the above, but its advantages are not so great that I would suggest you go through all your old code and change all such SELECT statements into ASSERT statements.
However, you will still have SELECT statements in your code (they are much easier to read than multiply nested IF statements), and I would recommend that they should generally also not have an OTHERWISE clause. For example, if you are processing a record type that has 4 possible values, you could write
SELECT( record_type );
WHEN( 1 ) ...
WHEN( 2 ) ...
WHEN( 3 ) ...
This code would be perfectly ok - as long as the record_type has a valid value and, more importantly, as long as no new record types value are defined. If a new value is defined, then if you do not find and update this code, it will quietly execute, but it will probably be incorrect and cause problems that are hard to resolve.
However, if you had written the statement without an OTHERWISE clause as
SELECT( record_type );
WHEN( 1 ) ...
WHEN( 2 ) ...
WHEN( 3 ) ...
WHEN( 4 ) ...
Then this code will raise an ERROR if record_type contains an invalid value or if it contains a new valid value. This is much safer code.
The one form of a SELECT statement where I would not recommend omitting the OTHERWISE clause is when the SELECT statement is defining a 3-way branch, as in code such as
WHEN( amount_due > 0 ) ...
WHEN( amount_due = 0 ) ...
In this case, you should code an OTHERWISE clause, and the compiler will try to generate code for this statement by doing the compare once and then executing two conditional branches.