Skip to content
PYD ENTERPRISES
Mainframe8 min read

Building control-flow graphs for PERFORM THRU, GO TO and ALTER

COBOL has no functions, only paragraphs and ranges of paragraphs, and control can leave a range by any door it likes. A rule extractor that gets this wrong attributes rules to the wrong branch.

Nandini SubramanianCTO, PYD ENTERPRISES
On this page · 6 sections
  1. 01Paragraphs are not functions
  2. 02Modeling ranges as regions with an armed exit
  3. 03Leaving a range and never coming back
  4. 04GO TO DEPENDING ON
  5. 05ALTER, the hardest case
  6. 06Why the precision matters

Paragraphs are not functions

A COBOL paragraph is a label followed by statements. PERFORM 2100-BASE-FEE executes that paragraph and returns to the statement after the PERFORM. PERFORM 2100-BASE-FEE THRU 2100-EXIT executes every paragraph from the first label to the end of the second, in physical source order, and returns only when control reaches the end of 2100-EXIT.

Unlike a function call, nothing stops code inside the range from jumping out of it with GO TO, and nothing stops ordinary sequential execution from falling into a paragraph that is also the target of a PERFORM. The same paragraph can be executed as part of three different ranges and by fall-through from the paragraph above it. A control-flow graph that treats paragraphs as functions misses all of that.

CLMADJ04.cbl (procedure excerpt)COBOL
1 2000-ADJUDICATE.
2 PERFORM 2100-EDIT THRU 2199-EDIT-EXIT
3 IF CLM-REJECTED
4 GO TO 2900-WRITE-RESULT
5 END-IF
6 PERFORM 2300-PRICE THRU 2399-PRICE-EXIT.
7 2100-EDIT.
8 IF CLM-SVC-DATE > CLM-RECEIVED-DATE
9 SET CLM-REJECTED TO TRUE
10 GO TO 2199-EDIT-EXIT
11 END-IF.
12 2110-EDIT-PROVIDER.
13 IF PRV-STATUS NOT = 'A'
14 SET CLM-PENDED TO TRUE
15 GO TO 2800-PEND
16 END-IF.
17 2199-EDIT-EXIT.
18 EXIT.

Modeling ranges as regions with an armed exit

PYD ENTERPRISES' CFG works at statement level and models each PERFORM ... THRU as a call into a region: the set of paragraphs from the start label to the end label in source order. The end of the last paragraph in the region gets a return edge to the statement after the PERFORM. Fall-through between paragraphs inside the region is an ordinary sequential edge.

In the excerpt, 2100-EDIT falls through into 2110-EDIT-PROVIDER, which is inside the range, so the provider edit runs on every claim that passes the date edit. The GO TO 2199-EDIT-EXIT stays inside the range and is safe. The GO TO 2800-PEND leaves the range: control never reaches the end of 2199-EDIT-EXIT on that path, so the PERFORM in 2000-ADJUDICATE never returns. Pended claims never reach the IF CLM-REJECTED test or the pricing paragraph, and whatever 2800-PEND does next decides their fate.

That is a business rule (pended claims skip pricing) implemented purely by control flow. A function-call model of the CFG would show 2000-ADJUDICATE always continuing after the edit, and the extractor would attach the pricing rules to pended claims.

Leaving a range and never coming back

Jumping out of a performed range is legal, common and dangerous. The COBOL standard leaves undefined what happens if control later reaches the end of that range's last paragraph by another route while the original PERFORM is still logically active, and compilers implement perform return mechanisms differently. Some code depends on the behavior of the compiler it was written for.

We do not silently pick one compiler's behavior. The engine tracks, for every path, which perform return points are armed. When a path leaves a range and can later reach the end of that range's exit paragraph by fall-through or another GO TO, the location is flagged with the path that reaches it, and the scan configuration can select the documented behavior of the target compiler for the analysis. In practice these flags are rare and almost always correspond to code that someone, at some point, debugged by experiment.

GO TO DEPENDING ON

GO TO P1 P2 P3 DEPENDING ON WS-IDX is a computed jump: it transfers to the nth label when WS-IDX is n, and falls through to the next statement when the index is out of range. The CFG models it as a multi-way branch with one edge per label plus the fall-through edge, each labeled with its index condition. Dataflow then narrows the possible values of WS-IDX where it can. If the index is always set from an 88-level checked table, the out-of-range edge may be provably dead, and the rule extractor can drop it. If it cannot be proven dead, it stays, because that fall-through branch is a common home for undocumented default behavior.

ALTER, the hardest case

ALTER P1 TO PROCEED TO P9 rewrites the target of the GO TO statement in paragraph P1 at runtime. It was removed from the language standard in 2002 and remains in plenty of production code compiled with older settings. Statically, the target of an altered GO TO is one of the original target or any label appearing in an ALTER that names that paragraph.

The engine collects every ALTER statement, builds the candidate target set for each alterable GO TO, and adds a state variable to the analysis that records which ALTER last executed along each path. Where the analysis resolves the target uniquely on a path, the edge is concrete. Where it cannot, all candidate edges are kept and the location is flagged. The modern mapping is almost always an explicit state machine, which the ALTER pattern entry shows in detail.

cfg-findings.json (excerpt)JSON
1[
2 { "kind": "range-exit", "program": "CLMADJ04", "from": "2110-EDIT-PROVIDER", "to": "2800-PEND",
3 "range": ["2100-EDIT", "2199-EDIT-EXIT"], "lines": [15, 15], "effect": "PERFORM at line 2 does not return" },
4 { "kind": "alter-target-set", "program": "CLMADJ04", "goto": "3400-ROUTE", "targets": ["3410-STD", "3420-COB", "3430-MANUAL"],
5 "resolved": false, "status": "needs-review" }
6]

Why the precision matters

Every business rule PYD ENTERPRISES extracts is defined by a path condition over this graph. If the graph is wrong, the conditions are wrong, and a rule that fires only for clean claims gets documented as firing for all of them. The PERFORM THRU fall-through pattern and the COBOL parser page describe the rest of the front end.

Keep reading

All posts

Legacy anti-patterns catalog

25 legacy anti-patterns, mapped to modern designs.

From COBOL ALTER and GO TO DEPENDING ON to EJB entity beans and PL/SQL cursors: why each is dangerous during migration, how it is detected, and the before and after code.

Explore the catalog

See your own code as a rule inventory.

Run PYD ENTERPRISES on up to 100,000 lines of COBOL, Java or .NET in your private VPC and review the extracted rules beside their source lines.