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/