Brief: Binding options for called programs are a key element of the Integrated Language Environment (ILE). Different types of binding allow the application designer to determine when program calls are resolved-at compile time or at run time. By making the right trade-offs, you will be able to design more efficient, more modular programs. This article explores the binding options and the commands that support them.
The Integrated Language Environment (ILE) that was announced on February 16, 1993 provides the basis for a new generation of AS/400 languages. ILE implementations are currently available or announced for C, RPG, COBOL, and CL. For the majority of users, the significance of ILE will not be realized until the general availability of ILE RPG, concurrent with V3R1 later this year.
This article concentrates on features that are inherent to ILE rather than a specific language implementation. The article begins with some general ILE concepts (for more information, see "The Integrated Language Environment," MC, May 1993), but the main focus is ILE's binding facilities.
Binding is concerned with improving CALL performance and giving programmers the flexibility to decide what type of CALL to use. Prior to ILE, most AS/400 languages supported only one type of call-an external or dynamic call. ILE languages and the underlying ILE functions support dynamic calls and two types of bound calls. As you'll see, there are pros and cons to each type of binding.
In general, ILE binding facilities make it more practical to write AS/400 applications in a very modular fashion. The result is smaller, less complex, more maintainable application components. To give you an understanding of how ILE evolved, let's begin with a look at the AS/400 language implementations.
Three programming models now coexist on the AS/400. The program models and their characteristics are illustrated in 1. Keep in mind that the functions of the Original Program Model (OPM) or the Extended Program Model (EPM) are not lost. ILE brings the best features of OPM and EPM into one evolutionary model at a lower level of the operating system. This allows traditional languages and new languages to work and perform well together.
Three programming models now coexist on the AS/400. The program models and their characteristics are illustrated in Figure 1. Keep in mind that the functions of the Original Program Model (OPM) or the Extended Program Model (EPM) are not lost. ILE brings the best features of OPM and EPM into one evolutionary model at a lower level of the operating system. This allows traditional languages and new languages to work and perform well together.
Survival of the Fittest
Let's start with the OPM programming model. Typically, you have a small number of source members against which you run the CRTxxxPGM command, where xxx is the source type (e.g., RPG or CBL). The result of running CRTxxxPGM is a *PGM object. You can call these programs from the command line or from another program. This type of call is referred to as an external call (CALLX internally on the system) or a dynamic call. Experience shows that this type of call is slow, forcing programmers to create applications using a small number of very large programs to reduce the number of external calls.
From a performance standpoint, the underlying problem with using external calls is that the calls are done dynamically. For example, when you call from program A to program B, the system has to resolve the reference to program B by searching the library list. The resolution and other checking performed at run time are very expensive. In ILE, you still have this external call mechanism and you also have a new mechanism referred to as a bound call which is made available through binding.
The bound call has much better performance than an external call since it shifts the overhead of resolving the reference from run time to compile time. The overhead occurs once when the program is created, rather than every time the application is run. ILE RPG reduces the overhead of the RPG fixed-logic cycle when using a bound call. Only the parts of the cycle that are used are initialized.
This gives programmers the flexibility to structure their applications in a modular format without having to pay a penalty in performance. The many benefits to modularity include better maintainability and well-tested reusable parts.
One often-overlooked benefit of modular programming is convenience among a team of programmers working on a single application. If you have a team of programmers and your application is only broken up into two or three parts, chances are that programmers are going to have contention for the parts. This contention forces developers to make copies of parts, change the copies, and then dual-maintain the changes back into the original. This type of activity is error-prone and time-consuming. ILE lets you avoid this situation.
Modules and Binding
One of the great things about ILE is that if you don't want to change, you typically don't have to-you can mix dynamic calls and bound calls within a single application. You can design your applications to take advantage of the strengths of each type of call. ILE provides new objects and commands to help you manage the elements, and each ILE language provides specific support for both dynamic and bound calls.
A module (object type *MODULE) is a new AS/400 object created using the Create xxx Module (CRTxxxMOD) commands (as with the CRTxxxPGM commands, xxx represents the language of the source member). The resulting *MODULE object is a nonexecutable, intermediate representation of the source member. The biggest difference between a *MODULE object and a *PGM object is that you cannot execute a module. However, you can use commands such as copy, delete, and display on *MODULE objects.
What good is a module if you can't execute it? A module is literally the building block for ILE programs. Once you have one or more *MODULE objects, you use the Create Program (CRTPGM) command to create your ILE program.
CRTPGM takes the *MODULE objects that are specified on the command and statically binds them together to form a *PGM. Essentially, static binding- known as linking on some systems-is the process of resolving the symbols between the modules and physically combining the *MODULE objects together to form a *PGM. This type of static binding is also referred to as bind by copy.
In ILE RPG you can choose to dynamically call a program using the CALL op code or perform a bound procedure call using the CALLB op code. You can even mix and match dynamic calls and bound calls within the same program object.
The two-step, program-creation process is illustrated in 2. In this example, the CRTRPGMOD command is run against RPG source members A and B to create two modules. The CRTPGM command is run against the two modules to create an executable program that has physical copies of the two modules within it.
The two-step, program-creation process is illustrated in Figure 2. In this example, the CRTRPGMOD command is run against RPG source members A and B to create two modules. The CRTPGM command is run against the two modules to create an executable program that has physical copies of the two modules within it.
For those of you who do not wish to use a two-step, program-creation process, the new CRTBNDxxx commands can create an ILE program directly from your source. The CRTBNDxxx commands cannot be used to bind multiple modules together. They simply camouflage the create module step for programs containing a single module.
Within each source member, you can use a combination of dynamic and bound calls. ILE RPG implements bound calls with the CALLB op code. To retain backwards compatibility, any program called with the CALL op code will be dynamically called. This retains the call function exactly as it exists in prior versions of RPG. CALLB specifies a bound call that will be resolved at compile time. A bound call is in the range of an order of magnitude faster than an external call.
Procedures
A more subtle concept than bound calls is a procedure. A procedure defines a callable portion of code for ILE languages. In nontechnical terms, a procedure is a sequence of steps used to solve a problem. Generically, you can think of an ILE procedure as a set of high-level language (HLL) statements that perform a particular task. Technically, you need to make distinctions about how procedures are implemented in each HLL.
In ILE RPG, a procedure has a one-to-one correspondence with a module object- in other words, every module is turned into a procedure. In other languages, a module may contain multiple procedures. For example, ILE C equates the C concept of a function to the ILE procedure concept. An ILE C module can contain many procedures, and each one can be called independently.
Any call from one procedure to another (whether the procedure is a C function within the same module or is from a stand-alone RPG module) within an ILE program object is a bound call. Therefore, you will use the CALLB op code when an ILE RPG procedure calls another ILE RPG procedure within the same program. You can also use CALLB to call procedures written in other languages.
Understanding procedures will help you to grasp how multiple languages work together in ILE and particularly how service programs are implemented. I'll discuss service programs in more detail later in this article.
Using Multiple Languages
One other useful feature is the ability to bind (using CRTPGM) module objects created in one language with module objects created in another language. 3 shows an example of how an RPG source member, a C source member, and a CL source member can be combined to create one ILE program. The ability to use different languages together in a seamless and efficient manner opens the doors to many different possibilities.
One other useful feature is the ability to bind (using CRTPGM) module objects created in one language with module objects created in another language. Figure 3 shows an example of how an RPG source member, a C source member, and a CL source member can be combined to create one ILE program. The ability to use different languages together in a seamless and efficient manner opens the doors to many different possibilities.
For example, your application may be predominantly written in RPG, but may benefit by having some functions (procedures) written in C. Now you can have your RPG modules bound to a C module and you can call the C functions using the CALLB op code. Another very good use of this feature would be to bind your CL driver programs directly in with your RPG programs. You can call your RPG procedures using the new Call Procedure (CALLPRC) command in your CL module. This eliminates the external call overhead between CL programs and RPG programs.
You always had the ability to use multiple languages together within an application. However, the external call overhead was high and each language had its own exception handling, storage management, and so on. Using different languages together in ILE does not mean you can ignore the fact that you are using different languages. However, it does reduce the complexity considerably since the languages share a common interface and can use the same memory management and exception management.
In addition, each ILE language has added features that make it easier to work with other languages. For example, module names can only be 10 characters long since this is the size of the name parameter on the CRTPGM command. However, the CALLB op code in ILE RPG allows you to specify names up to 255 characters long. This allows you to call ILE C procedures that have names longer than 10 characters. Remember that procedures in C do not correspond to modules but rather to the C language construct of a function. Therefore, only the module object's name is required to conform with the 10-character limitation.
Another example is the ability to have a return value on the CALLPRC instruction in ILE CL. This allows ILE CL to use the return values from calls to functions (such as in C).
Service Programs
The obvious question at this point is, "What do I do if I have a generic procedure which I want to bind into many different programs? Do I have to statically bind this procedure (*MODULE) to all the different programs, and thus have multiple copies all over the place?" The answer is no; if you had to bind by copy one procedure into many different programs, it would become a maintenance nightmare.
There is a second type of binding mechanism called bind by reference. Bind by reference allows you to call procedures in a dynamic fashion. (The resolution occurs at program activation time-explained in greater detail later.) This capability is made available through a call to a procedure in a service program. A service program (*SRVPGM) is similar to an ILE program in that it is an object created by binding one or more modules together. However, unlike a *PGM, you cannot call a *SRVPGM. You can only call the procedures that are within the service program. The only way to use a service program is by having it bound by reference to an ILE program.
Creating a service program follows the same steps as creating an ILE program. 4 shows an example that has two RPG source members against which you run the CRTRPGMOD command to create modules. Then the CRTSRVPGM command is run against MY_MOD1 and MY_MOD2 to create the service program MY_SRV.
Creating a service program follows the same steps as creating an ILE program. Figure 4 shows an example that has two RPG source members against which you run the CRTRPGMOD command to create modules. Then the CRTSRVPGM command is run against MY_MOD1 and MY_MOD2 to create the service program MY_SRV.
5 shows how to bind by reference to the service program created in 4. The service program, MY_SRV, is bound by reference to the ILE program, MY_PGM, by specifying it on the BNDSRVPGM option of the CRTPGM command. (This option is not available on the CRTBNDxxx commands.) However, unlike a *MODULE object which is copied into the final program object, only the information about the functions and data defined in the service program is stored in the program (MY_PGM). This allows you to have one physical copy of your code made into a service program and referred to by many different programs within your application, as illustrated in 6.
Figure 5 shows how to bind by reference to the service program created in Figure 4. The service program, MY_SRV, is bound by reference to the ILE program, MY_PGM, by specifying it on the BNDSRVPGM option of the CRTPGM command. (This option is not available on the CRTBNDxxx commands.) However, unlike a *MODULE object which is copied into the final program object, only the information about the functions and data defined in the service program is stored in the program (MY_PGM). This allows you to have one physical copy of your code made into a service program and referred to by many different programs within your application, as illustrated in Figure 6.
In this case, if there were a bug in the code that was used to create service program FRED, all that you would have to do is recreate service program FRED and replace it on your system. Then programs ONE, TWO, and THREE and service program JOE would automatically pick up the corrected version of FRED. You would not be required to rebind program ONE, TWO, or THREE or service program JOE unless you change the exports (we'll look at exports later) from service program FRED.
Design Considerations
From a performance perspective, bind by reference is a little more expensive than bind by copy. The actual call itself using bind by reference versus bind by copy is virtually the same. However, when you call the program that your service program is bound to (activate your program), the exports are resolved. Thus, resolution only occurs once at program activation and not every time you call the procedures in your service program.
When designing your application, you have to try to strike a balance between maintainability (which is usually easier if you use service programs) and application start-up performance. If you have a program which is bound by reference to a large number of service programs, there is going to be a noticeable activation-time penalty. However, once your program has been activated, then any call to a procedure in a service program is a bound call, which is very fast.
From an application design perspective, there are many benefits to using service programs. One of the benefits is having a single copy of the executable code on the system (as is illustrated in 6). Another advantage is the ease with which you can maintain any code that is in a service program. With service programs, you just replace the old service program with the new one, and the applications that call it utilize the new version without any recompilation. (Dynamic calls to programs accomplish the same ease of maintenance, but their performance overhead is much greater.)
From an application design perspective, there are many benefits to using service programs. One of the benefits is having a single copy of the executable code on the system (as is illustrated in Figure 6). Another advantage is the ease with which you can maintain any code that is in a service program. With service programs, you just replace the old service program with the new one, and the applications that call it utilize the new version without any recompilation. (Dynamic calls to programs accomplish the same ease of maintenance, but their performance overhead is much greater.)
Another advantage of using service programs within your application is the ability to control resources such as memory and file usage. The ability to control these types of resources involves the concept of activation groups. Activation groups will not be discussed in detail in this article; but suffice it to say that data management resources can be scoped to activation groups, and a service program can run in its own activation group.
Scoping of resources defines how they are shared. A simple example is file overrides. Normally, the extent of a file override is to the call level, so that only programs running lower in the invocation stack of a job are affected. In ILE, file overrides can be isolated to activation groups so that programs running in one activation group of a job are not affected by file overrides created in another activation group within the same job. This gives application designers the flexibility to scope resources in whichever manner they choose, and still get the performance benefit of using bound calls.
Imports and Exports
With any ILE module object, there is the concept of imports and exports. An import is either data or procedures that are referred to in one module or program and defined in another module or program. An export is either data or procedures defined in a module or program and made available to other modules or programs. Thus, for every import there must be a corresponding export.
For C programmers, the concept of imports and exports is well understood due to language constructs such as the extern keyword and static data. For other languages-such as RPG-these concepts were not intrinsic in the language, although they are available in some of the enhancements made in the ILE languages.
7 illustrates how imports and exports work in ILE RPG. The RPG source member GL has a bound call (CALLB) to procedure ACT_REC. Procedure ACT_REC is not defined in module GL, but is only referenced. So procedure ACT_REC is said to be imported in module GL. When you create module ACT_REC from source member ACT_REC, the procedure ACT_REC is available to be used by other modules and is said to be exported from module ACT_REC.
Figure 7 illustrates how imports and exports work in ILE RPG. The RPG source member GL has a bound call (CALLB) to procedure ACT_REC. Procedure ACT_REC is not defined in module GL, but is only referenced. So procedure ACT_REC is said to be imported in module GL. When you create module ACT_REC from source member ACT_REC, the procedure ACT_REC is available to be used by other modules and is said to be exported from module ACT_REC.
At CRTPGM time, the import for procedure ACT_REC in module GL is resolved to the export of procedure ACT_REC in module ACT_REC. In other words, a matching export (procedure ACT_REC) is resolved for the import in GL.
For a simple bound call such as the previous example, imports and exports are only concerned with resolving references to another procedure. However, imports and exports are critical to understanding how service programs work. As mentioned earlier, when you bind by reference to a service program, only the information about functions and external data is copied into the program object-this information defines the service program's export information.
8 illustrates how the export information from a service program is stored in the program object which is bound by reference to the service program. In this example, the imports MOD_A and MOD_B are internally resolved when modules MOD_A and MOD_B are statically bound together at compile time. The imports PROC_1, PROC_2, and PROC_3 are found in service program MY_SRVPGM, and the information about them is stored in program MY_PGM. When program MY_PGM is called, the service program MY_SRVPGM is activated. In other words, when MY_PGM is called, the service program MY_SRVPGM is externally resolved.
Figure 8 illustrates how the export information from a service program is stored in the program object which is bound by reference to the service program. In this example, the imports MOD_A and MOD_B are internally resolved when modules MOD_A and MOD_B are statically bound together at compile time. The imports PROC_1, PROC_2, and PROC_3 are found in service program MY_SRVPGM, and the information about them is stored in program MY_PGM. When program MY_PGM is called, the service program MY_SRVPGM is activated. In other words, when MY_PGM is called, the service program MY_SRVPGM is externally resolved.
The externally resolved import information is stored in the program object. If the service program that is resolved at run time has different exports than it did when it was created, you will receive a signature-violation exception. A signature is a similar concept to a level-check. Every service program is given a signature, unless you explicitly specify that you don't want one generated.
When the user of a *SRVPGM specifies the service program name on the CRTPGM command, the signature of the service program is copied into the program as illustrated in 8. When the program is activated for the first time, the signature stored in the *PGM is checked against the signature of the *SRVPGM. If the signatures differ, a signature-violation exception is raised.
When the user of a *SRVPGM specifies the service program name on the CRTPGM command, the signature of the service program is copied into the program as illustrated in Figure 8. When the program is activated for the first time, the signature stored in the *PGM is checked against the signature of the *SRVPGM. If the signatures differ, a signature-violation exception is raised.
Selective Exporting
The service program examples in this article assume that every procedure was exported from the *SRVPGM. There is a mechanism available through a binder language that lets you specify which procedures and data you want to export from a service program. In essence, the binder language allows you to define an interface to your service programs. The binder language is a simple language which follows CL syntax rules.
An example of the binder language that could be used when the service program in 8 is created follows:
An example of the binder language that could be used when the service program in Figure 8 is created follows:
STRPGMEXP PGMLVL(*CURRENT) + LVLCHK(*YES) EXPORT SYMBOL('PROC_1') EXPORT SYMBOL('PROC_2') EXPORT SYMBOL('PROC_3') ENDPGMEXP
The PGMLVL option allows you to specify multiple levels of exports that you wish to support. In this example, *CURRENT is specified to indicate the current list of exports to be used. The LVLCHK option allows you to specify whether or not you wish to have the system check the binding signatures. In this case, *YES is specified, which indicates that signature-checking is enabled. The remainder of the binder language specifications list the exports for the service program. The binder language statements are executed by specifying EXPORT(*SRCFILE) on the CRTSRVPGM command.
Specifying an export list is the preferred method of specifying which procedures and data are to be exported from a service program. The other method is to specify EXPORT(*ALL) on the CRTSRVPGM command. This causes all external data and procedures to be exported from the service program.
The reason that using a binder language specification is better than using EXPORT(*ALL) is that the signature for the service program is generated based on the exports and the position of the exports. By using an export list, you have much better control of the signatures that are going to be generated, and you have the ability to support multiple versions of exports.
Binding Directory
If your ILE application consists of a large number of modules and service programs, specifying them on the CRTPGM and CRTSRVPGM commands would become very tedious and error-prone. There is a mechanism called a binding directory which solves this problem. A binding directory is a new system object of type *BNDDIR. Binding directories contain the names of modules and service programs that you may need when you create your ILE program or service program.
An important benefit of using a binding directory is that a module or service program will only be bound-either statically for a module or by reference for a *SRVPGM-into your program or service program if it provides an export that matches an unresolved import. The following commands are used with binding directories:
CRTBNDDIR Create Binding Directory
DLTBNDDIR Delete Binding Directory
ADDBNDDIRE Add Binding Directory Entry
RMVBNDDIRE Remove Binding Directory Entry
DSPBNDDIR Display Binding Directory
WRKBNDDIR Work with Binding Directory
WRKBNDDIRE Work with Binding Directory Entry
The entries that you add to your binding directory do not need to exist on the system, since they are only names that will be used later at program or service program creation time.
Bringing It All Together
To summarize, there are two types of calls in ILE: dynamic calls and the much faster bound calls. Dynamic calls are external calls made to programs. Bound calls are calls made to procedures within an ILE program or to procedures in a service program.
ILE programs or service programs are created by binding modules and service programs together. There are two types of binding-bind by copy (static binding) and bind by reference (dynamic binding). Bind by copy is the mechanism where modules are physically copied into a program or service program object. Bind by reference is the mechanism where information about the exports in a service program is stored in the program or service program object and is resolved during program activation time.
I hope that this information helps you see some of the benefits that can be derived from using ILE. However, binding is only one part of ILE, and there are many other benefits to be derived from using ILE. I'll explore some of these features in an upcoming article.
Glen Sakuth is a computer engineer working in the AS/400 Languages Architecture and Design Control Group at the IBM Toronto Lab. He was one of the early developers involved with the C/400 compiler. He then became one of the principle designers and programmers of the CUBE-3 back-end used by all ILE languages. He is currently the architect for the ILE C/400 compiler. He is a frequent speaker at COMMON conferences and can be reached through Internet at
V3R1 Follow-up: An Introduction to Program Binding
Figure 1 AS/400 Program Models
Original Program Model (OPM) o RPG/400, COBOL/400, PL/I, CL, and BASIC. o Single entry point into a program. o Single scoping of variables (only global variables). o Access to data only through declared variables (no pointers). o Dynamic call binding. Extended Program Model (EPM) o Pascal and EPM C/400. o Features similar to those of ILE, only not as well integrated with each other or the system. Integrated Language Environment (ILE) o ILE C/400, ILE RPG/400 (also known as RPG IV), ILE COBOL/400, ILE CL. o Multiple external procedures or functions (multiple entry points). o Nested scoping of variables (global, local, block). o Access to data through variables and pointers. o Static and automatic data. o Dynamic storage allocation. o Optimized code generation. o Consistent exception model.
V3R1 Follow-up: An Introduction to Program Binding
Figure 2 Combining Modules with Static Binding
UNABLE TO REPRODUCE GRAPHICS
V3R1 Follow-up: An Introduction to Program Binding
Figure 3 Creating an ILE Program with Three ILE Languages
UNABLE TO REPRODUCE GRAPHICS
V3R1 Follow-up: An Introduction to Program Binding
Figure 4 Creating a Service Program
UNABLE TO REPRODUCE GRAPHICS
V3R1 Follow-up: An Introduction to Program Binding
Figure 5 Binding by Reference a Service Program to a Progra
UNABLE TO REPRODUCE GRAPHICS
V3R1 Follow-up: An Introduction to Program Binding
Figure 6 The Same Service Program Used by Many Programs
UNABLE TO REPRODUCE GRAPHICS
V3R1 Follow-up: An Introduction to Program Binding
Figure 7 Imports and Exports for a Simple RPG Program
UNABLE TO REPRODUCE GRAPHICS
V3R1 Follow-up: An Introduction to Program Binding
Figure 8 Imports and Exports for a Program
UNABLE TO REPRODUCE GRAPHICS
LATEST COMMENTS
MC Press Online