On 2/3/2011 10:41 AM, Johanson, Adam wrote:
         Then, I told myself that the whole point of the exercise was to make the code 
more readable, so a branch to a return-to-caller label every now and then didn't really 
defeat the purpose and actually _did_ help things. IMHO, rather than seeing a big nested 
IF statement and chasing down the end point just to figure out that you're gonna return 
to the caller is a bit more involved than realizing, "Oh, I'm just getting out of 
Dodge."

Yup. You can find analogs in other languages. For example SIGNAL and EXIT in
REXX. Programs can become unnecessarily complex if you require every condition
to be surfaced back up through a deeply-nested logic hierarchy.

Depending on your program design, an "signal/exit jump from anywhere" can be
problematic when using a stack for (at least) saving/restoring registers. If you
"signal/exit" with the stack pushed several levels deep, your current registers
might not be appropriate for the "signal/exit" routine and, if the stack is
needed for any processing after that, you'll need a way to unwind the stack to
the appropriate level or else things might go horribly wrong--for example when
the "signal/exit" routine itself tries to return to the original caller.

The easiest way to avoid this is to never branch to a "signal/exit" label from
an inner routine. Or put another way, each stack level has its own "signal/exit"
routine.

Another way we've handled the "signal/exit jump from anywhere" is to save the
appropriate stack control and registers in some easily addressable non-stack
storage from which they are loaded/copied as one of the very first things the
"signal/exit" routine does.

FWIW, when writing code from scratch it is often possible to avoid "signal/exit"
branches entirely by enclosing the logic within two nested simple DOs as shown.
We have used this technique quite effectively in some very large and complex
programs. (This is only an example. Many other variants of this approach are
possible.)

| DO LABEL=MainLine            Do for MainLine routine
|
|   ************************************
|   * Process MainLine Logic Here      *
|   ************************************
|   DO LABEL=MainLineLogic       Do for MainLine logic
|     .
|     .(some deeply nested code here discovers an error)
|     LHI   R15,ErrorCode1         Load error code
|     LEAVE MainLineLogic          Go perform error processing
|     .
|     .(another error is discovered)
|     LHI   R15,ErrorCode2         Load error code
|     LEAVE MainLineLogic          Go perform error processing
|     .
|     .(this can be a very large and complex routine)
|     .
|     XR    R15,R15                Set return code = 0
|     LEAVE MainLine               Exit the routine
|   ENDDO , MainLineLogic        EndDo for MainLine error trap
|
|   ************************************
|   * Process MainLine Errors Here     *
|   ************************************
|   SELECT CHI,R15,EQ            Select error code
|   WHEN ErrorCode1              ErrorCode1
|     (whatever)                   Issue msg, set retcode/rsn, etc.
|   WHEN ErrorCode2              ErrorCode2
|     (whatever)                   Issue msg, set retcode/rsn, etc.
|   OTHRWISE ,                   Unknown Error Code
|     J     *+2                    Logic error. Force abend here.
|   ENDSEL ,                     EndSel error code
| ENDDO , MainLine             EndDo for MainLine routine


         And you've probably come across it in the SHARE presentations that 
you've seen, but if you're going to do this, download Ed Jaffe's FLOWASM exit. 
It makes life easier.

I'm glad you found it useful. 8-)

--
Edward E Jaffe
Phoenix Software International, Inc
831 Parkview Drive North
El Segundo, CA 90245
310-338-0400 x318
edja...@phoenixsoftware.com
http://www.phoenixsoftware.com/

Reply via email to