Writing Reentrant Assembler Code

Some programs run so often we take special efforts to make sure that only one copy of the program needs to be loaded into memory, no matter how many users are running it concurrently. That’s what reentrant programming is all about. We can do this by dynamically allocating storage for all the parts of our program that might change. Each user gets their own copy of dynamic storage.

Want to get your assembler program to run in the Language Environment?  It needs to be reentrant.  Future posts will cover COBOL calling Assembler and Assembler calling COBOL in the Language Environment, but to follow along, you first have to learn how to  write assembler reentrantly.

Some programmers write all their programs in a reentrant style. There’s not a lot of extra overhead. Want to give it a try?  I’ve posted a video about how it works here and in the assembler video course. There’s also a sample reentrant program you can start with here.  The video explains in detail how the program works.

 

 

12 Comments

Filed under IBM Mainframe Assembler

12 responses to “Writing Reentrant Assembler Code

  1. Niemand Thuis

    All GRT (good, right and true) but besides z/OS system level, e.g., SVCs, exits, STCs, (E)LPA programming and the CICS OTE, where in userland is reentrancy necessary (“… no matter how many users are running it concurrently.”)?
    As for LE-compliant HLASM, the overhead of enclave creation / destruction may well be the ‘deal breaker’ where ILC (interlanguage communication) is not required.

    • Good points.
      – I do know a company that adopted reentrancy as a standard approach for their base system, but I viewed it as a stylistic choice. I’ll ask the designer what advantages, if any, he thought it added.
      – I think you’re right about LE Compliant Assembler – not needed unless you’re making multiple interlanguage calls – or want to standardize some of the library calls among the languages you’re using. In my case, it’s an interesting corner of the world to explore, and I did want to write about LE briefly, so I thought I’d better add something on reentrancy.
      Many thanks for taking time to respond, Niemand. It’s great to hear from you – keep me straight.

    • Re-reading the article, I see what you mean about “no matter how many users are running it concurrently”. Not exactly what I intended. Probably should have said “We use it in cases where many users might run it concurrently.” Thanks, again.

  2. Steve Comstock, retired Assembler Programming lecturer, advocates reentrant coding as simply good practice for optimum performance. In his “Applications Assembler Programming for z, A Technical Discourse”(*), he advises:
    Quote: “If there are instructions and data in the same cache line and any of the data is changed, both caches must be refreshed. This can cause a big performance hit. So the coding guidelines include:
    –> code in a reentrant style
    reentrant code by its very nature separates instructions and data and never causes cache refresh due to storing into itself.” End quote.

    Works for me!
    Reentrant coding isn’t really all that difficult, and it’s good a discipline to help create “clean” code. And don’t we all want our code to be as clean as can be? 😉

    (*) http://web.archive.org/web/20120813154951/http://www.trainersfriend.com/Papers/Assembler.Coding.1.pdf

    • Thanks for weighing in, David. And thanks much for the great link – we can all benefit from reading it. I’m going to spend some time with it.I haven’t heard anyone talk about the effect of reentrant programming on the cache before, but it makes sense.

  3. Jay

    Thanks for this great article. There are times when we have to rely on EX instruction which will break reentrancy. How do you find alternates to that. For example I would need to write a generic parser that
    parse a sysin card that contains keyword and value pair.

    NAME NAME1
    OCCUPATION ACCOUNTANT
    AGE 30

    Usually we can use TRT to find the next blank character from the start and calculate the length and move the value for that specific length using EX and target instruction being MVC.
    How do you see any alternate code without EX involved.

  4. Andi Tepper Klorman

    I see your example uses the STORAGE macro to obtain DSA storage. I’ve never used that – is it more efficient than GETMAIN? Years ago, when I started writing reentrant programs, I got bit by the inefficiencies of GETMAIN (eq when the subroutine was called over a million times) and had to program around it by doing my own dynamic storage management.

    • I think that efficiency of these two macros is a thorny question, so I won’t weigh in on that. I believe STORAGE OBTAIN may be easier to use and more flexible. You might check this out: https://share.confex.com/share/115/webprogram/Handout/Session7511/BostonSHAREVSM.pdf

      Anyone else have a strong opinion?

    • @Andi: I tried STORAGE OBTAIN years ago, but soon went back to using my favorite: SVC 120 (aka “GETMAIN RU,LV=…”) preceded by something to set R0 equal to the length required (LA, LHI, etc). For me, I just like the simplicity of the resulting code.
      Steve Myers (2016) vigorously contends that GETMAIN is better (uses less CPU), because STORAGE references low-core memory (causing a cache fault). He says IBM claims STORAGE is “more efficient” (no citation given), but also says he found it uses about 10% more CPU:
      http://ibmmainframeforum.com/assembler/topic11023.html
      http://ibmmainframes.com/about65698.html
      I’ve never seen anything in any IBM docs that say the STORAGE macro is “more efficient”. They just say you need it if you’re doing fancy stuff like AR mode or managing memory in a different address space.
      I know some people insist on using *every* new thing IBM comes up with, regardless of whether it’s needed (or even appropriate). So, they’ll use stuff like AMODE 64, BAKR, BRANCH RELATIVE (“baseless” coding), *all* the time, even if there’s absolutely no need for it and actually ends up being worse. I’d rather find something simple that just does what I need and stick with it. In rare cases, if I don’t like the code a Macro generates (GETMAIN, GET, PUT for example), I may code it as a comment and use my own code instead, but that’s about it.
      For the subroutine that gets called “over a million times” (by the same caller, in the same unit of work, I presume), my solution would be force the caller to pass a workarea as one of the parameters. Not hard for the caller to do, and it completely eliminates any GETMAIN/FREEMAIN performance issue.

      • [GETMAIN vs STORAGE OBTAIN, cont’d]
        My method for coding a GETMAIN (SVC 120) relies as much on the MVS Diagnosis Reference as it does the Assembler Services Reference:
        http://ibm.com/docs/en/zos/2.1.0?topic=descriptions-svc-120-0a78
        Referring to the above, you can readily see that all you need to conditionally allocate a 72 byte Save Area from Subpool 0 is:
        Set R0=72 (len), Set R15=0 (subpool), Set R1 = 0 (reserved), SVC 120
        Thus, my GETMAINs look like this:
        𝙻𝙰 𝟶,𝟽𝟸
        * 𝙶𝙴𝚃𝙼𝙰𝙸𝙽 𝚁𝙲,𝙻𝚅=(𝟶) <= note: this is a comment
        𝚂𝚁 𝟷𝟻,𝟷𝟻
        𝚂𝚁 𝟷,𝟷
        𝚂𝚅𝙲 𝟷𝟸𝟶

        Rather than this mess:
        𝙶𝙴𝚃𝙼𝙰𝙸𝙽 𝚁𝙲,𝙻𝚅=(𝟶)
        + 𝙲𝙽𝙾𝙿 𝟶,𝟺
        + 𝙱 𝙸𝙷𝙱𝟶𝟶𝟶𝟹𝙲 𝙱𝚁𝙰𝙽𝙲𝙷 𝙰𝚁𝙾𝚄𝙽𝙳 𝙳𝙰𝚃𝙰
        +𝙸𝙷𝙱𝟶𝟶𝟶𝟹𝙵 𝙳𝙲 𝙱𝙻𝟷'𝟶𝟶𝟶𝟶𝟶𝟶𝟶𝟶' 𝙵𝚕𝚊𝚐𝚜. @𝙻𝟽𝙲
        + 𝙳𝙲 𝙰𝙻𝟷(𝟶) 𝚁𝙴𝚂𝙴𝚁𝚅𝙴𝙳
        + 𝙳𝙲 𝙰𝙻𝟷(𝟶) 𝚂𝚄𝙱𝙿𝙾𝙾𝙻
        + 𝙳𝙲 𝙱𝙻𝟷'𝟶𝟶𝟶𝟶𝟶𝟶𝟶𝟶' 𝙼𝙾𝙳𝙴 𝙱𝚈𝚃𝙴 @𝙶𝟾𝟼𝟶𝙿𝟹𝟶
        +𝙸𝙷𝙱𝟶𝟶𝟶𝟹𝙲 𝙳𝚂 𝟶𝙷
        + 𝙻 𝟷𝟻,𝙸𝙷𝙱𝟶𝟶𝟶𝟹𝙵 𝙻𝙾𝙰𝙳 𝙶𝙴𝚃𝙼𝙰𝙸𝙽 𝙿𝙰𝚁𝙼𝚂
        + 𝚂𝚁 𝟷,𝟷 𝚉𝙴𝚁𝙾 𝚁𝙴𝚂𝙴𝚁𝚅𝙴𝙳 𝚁𝙴𝙶 𝟷
        + 𝚂𝚅𝙲 𝟷𝟸𝟶 𝙸𝚂𝚂𝚄𝙴 𝙶𝙴𝚃𝙼𝙰𝙸𝙽 𝚂𝚅𝙲

Leave a Reply