Skip to main content

ironwork_rt/lir/
flow.rs

1//! Control flow (lir.md §8): ops, terminators, ranges, and the VM's frames and return points
2//! (assumption C99 PERFORM_RETURN_POINTS).
3
4use super::{
5    AbendId, ArithId, BlockId, CallId, CicsId, CondId, DisplayId, ExprId, FileOpId, InitId, InspectId, IntExpr,
6    InvokeId, MarkupId, MovePlan, Odo, Operand, ParaId, PlaceId, RangeId, ReleaseId, ReportOp, ReturnId, ScreenInput, ScreenPlan, SearchAllId, SenderCheck, SortId, SqlId,
7    StepPlan, StringId, SymId, TempId, UnstringId, UpDown,
8};
9use crate::abend::Ending;
10use crate::vocab::AcceptFrom;
11use crate::{codec_enum, codec_struct};
12use std::collections::BTreeMap;
13
14#[derive(Clone, Debug, PartialEq, Eq)]
15pub enum Op {
16    /// `to` located, then `from`, which takes NUMCHECK's `check` before it is read.
17    Move { from: Operand, to: PlaceId, plan: MovePlan, check: SenderCheck },
18    /// SET TO, and PERFORM VARYING's FROM: as `Move`, but a data item sender is read as a number,
19    /// its digits checked, where MOVE carries a zoned or packed sender's invalid digits (C260).
20    Set { from: Operand, to: PlaceId, plan: MovePlan },
21    Initialize { target: PlaceId, plan: InitId },
22    Arith(ArithId),
23    /// SET ADDRESS OF: `address` evaluated once, NULL or an address in run-unit memory, then
24    /// each LINKAGE record given it in turn.
25    SetAddress { records: Vec<u16>, address: Operand },
26    /// SET UP BY or DOWN BY: `by` evaluated once, then each receiver read and moved in turn.
27    SetUpDown { by: IntExpr, down: bool, targets: Vec<(PlaceId, UpDown)> },
28    /// SET TO ENTRY: `entry` read as a program name and the program loaded, dynamically unless it
29    /// is a literal under NODYNAM, then each function-pointer or procedure-pointer given the value
30    /// naming it in the run unit's list of entries.
31    SetEntry { entry: Operand, targets: Vec<PlaceId> },
32    /// PERFORM VARYING's increment: `var` located, then each place of `prepass`, then `var + by`
33    /// computed and stored.
34    Step { var: PlaceId, by: ExprId, plan: StepPlan, prepass: Vec<PlaceId> },
35    /// SEARCH's index steps, SORT-RETURN.
36    SetInt { target: PlaceId, value: IntExpr },
37    Inspect(InspectId),
38    String(StringId),
39    Unstring(UnstringId),
40    SearchAll(SearchAllId),
41    /// Raises the PERFORM and CALL depth, abending past 100.
42    Nest,
43    Unnest(u8),
44    SetTemp(TempId, IntExpr),
45    DecTemp(TempId),
46    /// SEARCH's table count, evaluated once at the statement's start as `occurrences` evaluates it,
47    /// abending as it abends, and held in the top frame's counter `temp`, which `Count::Temp` reads.
48    SetCount(TempId, Odo),
49    Display(DisplayId),
50    /// DISPLAY UPON SYSERR under `--compliance extended`: the line written to the run's standard
51    /// error.
52    DisplayError(DisplayId),
53    Accept { target: PlaceId, from: AcceptFrom, plan: MovePlan },
54    /// DISPLAY ... UPON ARGUMENT-NUMBER under `--compliance extended`: the next ACCEPT ... FROM
55    /// ARGUMENT-VALUE takes the PARM argument word this numbers.
56    ArgumentNumber(IntExpr),
57    /// DISPLAY UPON ENVIRONMENT-NAME, or with `value` UPON ENVIRONMENT-VALUE, under `--compliance
58    /// extended`: plan `display`'s text names the environment variable, or becomes its value.
59    Environment { display: DisplayId, value: bool },
60    /// DISPLAY on the screen under `--compliance extended`: plan `display`'s text written where
61    /// `screen` puts it.
62    ScreenDisplay { display: DisplayId, screen: ScreenPlan },
63    /// ACCEPT from fields of the screen under `--compliance extended`, one for a positioned ACCEPT
64    /// and one for each TO or USING field of a SCREEN SECTION's screen; with ON EXCEPTION
65    /// phrases, `handled`, Arm(1) when a key other than ENTER ended it.
66    ScreenAccept { inputs: Vec<ScreenInput>, handled: bool },
67    File(FileOpId),
68    Call(CallId),
69    Cancel(Operand),
70    Sort(SortId),
71    Release(ReleaseId),
72    Return(ReturnId),
73    Report(ReportOp),
74    Invoke(InvokeId),
75    Cics(CicsId),
76    Sql(SqlId),
77    /// ALTER: from now on control reaching paragraph `para` goes to `to`. The alter table lives as
78    /// long as the program's WORKING-STORAGE.
79    Alter { para: ParaId, to: ParaId },
80    /// Control reaching a paragraph of segment `priority`: when that differs from the segment
81    /// register and is 50 or more, the alter entries of the segment's paragraphs are cleared. The
82    /// register then holds `priority`.
83    EnterSegment(u8),
84    /// Under the DEBUG option: the line of the statement starting, which DEBUG-LINE gives.
85    DebugLine(u32),
86    /// Under the DEBUG option, after an ALTER of a paragraph a debugging section serves: that
87    /// section, with DEBUG-NAME `name` and DEBUG-CONTENTS `contents`, the TO PROCEED TO name.
88    DebugAlter { range: RangeId, name: SymId, contents: SymId },
89    /// JSON GENERATE, JSON PARSE, XML GENERATE or XML PARSE (lir.md §9.13).
90    Markup(MarkupId),
91}
92
93/// What an op tells the VM.
94#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub enum Step {
96    Next,
97    /// The handler a service selected, for the block's `Select`.
98    Arm(u8),
99    /// A transfer a service chose at run time (HANDLE CONDITION), or a procedure it
100    /// ran left by.
101    GoTo(ParaId),
102    End(Ending),
103    /// A procedure the op ran passed the return point of active frame `frame`, which completes.
104    Return(u64),
105    /// A procedure the op ran passed the return point of a PERFORM control had left.
106    Resume(Resume),
107}
108
109#[derive(Clone, Debug, PartialEq, Eq)]
110pub enum Terminator {
111    Jump(BlockId),
112    Branch { cond: CondId, then: BlockId, otherwise: BlockId },
113    /// On the `Arm` the block's last op returned.
114    Select(Vec<BlockId>),
115    /// Control passes the end of paragraph `next` − 1 for `next`, which may be one past the last:
116    /// the return point armed there, if any, is taken.
117    ParagraphEnd { next: ParaId },
118    GoTo(ParaId),
119    /// GO TO … DEPENDING ON: `targets[k − 1]` for k in range, taken as a `GoTo`; else `otherwise`.
120    Switch { value: IntExpr, targets: Vec<ParaId>, otherwise: BlockId },
121    /// An out-of-line PERFORM: push a frame, arm its return point and enter the range; `ret` runs
122    /// when it completes.
123    PerformEnter { range: RangeId, ret: BlockId, resume: Option<Resume> },
124    /// Nothing in the run unit's first program, GOBACK in any other.
125    ExitProgram { next: BlockId },
126    End(Ending),
127    Abend(AbendId),
128    /// The entry of a paragraph an ALTER names: a `GoTo` of the target the alter table holds for
129    /// `para`, or `Jump(otherwise)` while it holds none.
130    AlteredGoTo { para: ParaId, otherwise: BlockId },
131    /// Under the DEBUG option, the entry of a paragraph a debugging section serves: the section
132    /// runs with DEBUG-NAME `name`, then `next`; a section that leaves takes the top frame with it.
133    Debug { range: RangeId, name: SymId, next: BlockId },
134}
135
136/// The statement after an out-of-line PERFORM that runs once and is a statement of paragraph
137/// `para`: `block`, where control resumes when it passes that PERFORM's return point after
138/// leaving its range. `block` also keys what the PERFORM displaced.
139#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub struct Resume {
141    pub para: ParaId,
142    pub block: BlockId,
143}
144
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub struct Range {
147    pub first: ParaId,
148    pub last: ParaId,
149    pub kind: RangeKind,
150}
151
152impl Range {
153    /// The paragraphs a GO TO stays in the range for, of a program of `paragraphs`: `first` to
154    /// `last`, or to the program's last paragraph when `last` comes before `first`; every one for
155    /// a SORT or MERGE procedure.
156    pub fn region(&self, paragraphs: u32) -> (ParaId, ParaId) {
157        let end = paragraphs.saturating_sub(1);
158        match self.kind {
159            RangeKind::SortProcedure => (0, end),
160            _ if self.last < self.first => (self.first, end),
161            _ => (self.first, self.last),
162        }
163    }
164}
165
166#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
167pub enum RangeKind {
168    Perform,
169    SortProcedure,
170    UseBeforeReporting,
171    /// A USE AFTER EXCEPTION/ERROR procedure.
172    UseProcedure,
173    /// A USE FOR DEBUGGING section.
174    Debugging,
175    /// An XML PARSE processing procedure.
176    Processing,
177}
178
179/// What the declaratives need at run time besides each file's own procedure (`FileDesc.error`).
180#[derive(Clone, Debug, Default, PartialEq, Eq)]
181pub struct Declaratives {
182    /// The EXCEPTION/ERROR procedures for files open, or being opened, INPUT, OUTPUT, I-O and
183    /// EXTEND, in that order.
184    pub modes: [Option<RangeId>; 4],
185    /// DEBUG-ITEM's offset and length in the slab, when a debugging section can run.
186    pub debug_item: Option<(u32, u32)>,
187}
188
189/// A return point armed at the end of a range's last paragraph by frame `frame`, with the PERFORM's
190/// resume.
191#[derive(Clone, Copy, Debug, PartialEq, Eq)]
192pub struct ReturnPoint {
193    pub frame: u64,
194    pub resume: Option<Resume>,
195}
196
197/// An active range. `displaced` is the return point it replaced at its range's end, put back when
198/// it completes; `segment` the segment register when it was pushed, back when it completes. Its
199/// paragraphs run at `depth`, and `temps` are the TIMES counters of the statements that run under
200/// it, by `TempId`.
201#[derive(Clone, Debug, PartialEq, Eq)]
202pub struct Frame {
203    pub id: u64,
204    pub kind: FrameKind,
205    pub displaced: Option<ReturnPoint>,
206    pub segment: u8,
207    pub depth: u32,
208    pub temps: Vec<i64>,
209}
210
211#[derive(Clone, Copy, Debug, PartialEq, Eq)]
212pub enum FrameKind {
213    /// The program's own run: every paragraph, and no return point.
214    Main,
215    Perform { range: RangeId, ret: BlockId, resume: Option<Resume> },
216    /// A procedure a service or a `Debug` runs in a dispatch loop of its own, which ends when
217    /// the frame completes or is left.
218    Procedure { range: RangeId },
219}
220
221/// The return points of one activation: the one armed at each paragraph's end, what each
222/// resumable PERFORM control left displaced (by its `Resume.block`), and the frames, Main first.
223#[derive(Clone, Debug, Default, PartialEq, Eq)]
224pub struct Returns {
225    pub armed: Vec<Option<ReturnPoint>>,
226    pub saved: BTreeMap<BlockId, Option<ReturnPoint>>,
227    pub frames: Vec<Frame>,
228    pub next_frame: u64,
229}
230
231codec_enum!(Op {
232    Move { from, to, plan, check } = 0,
233    Initialize { target, plan } = 1,
234    Arith(id) = 2,
235    SetAddress { records, address } = 3,
236    SetUpDown { by, down, targets } = 4,
237    Step { var, by, plan, prepass } = 5,
238    SetInt { target, value } = 6,
239    Inspect(id) = 7,
240    String(id) = 8,
241    Unstring(id) = 9,
242    SearchAll(id) = 10,
243    Nest = 11,
244    Unnest(n) = 12,
245    SetTemp(temp, value) = 13,
246    DecTemp(temp) = 14,
247    Display(id) = 15,
248    Accept { target, from, plan } = 16,
249    File(id) = 17,
250    Call(id) = 18,
251    Cancel(name) = 19,
252    Sort(id) = 20,
253    Release(id) = 21,
254    Return(id) = 22,
255    Report(op) = 23,
256    Invoke(id) = 24,
257    Cics(id) = 25,
258    Sql(id) = 26,
259    Alter { para, to } = 27,
260    EnterSegment(priority) = 28,
261    DebugLine(line) = 30,
262    DebugAlter { range, name, contents } = 31,
263    Markup(id) = 32,
264    Set { from, to, plan } = 33,
265    SetCount(temp, odo) = 34,
266    SetEntry { entry, targets } = 35,
267    ArgumentNumber(value) = 36,
268    ScreenDisplay { display, screen } = 37,
269    ScreenAccept { inputs, handled } = 38,
270    Environment { display, value } = 39,
271    DisplayError(id) = 40,
272});
273codec_enum!(Step { Next = 0, Arm(arm) = 1, GoTo(para) = 2, End(ending) = 3, Return(frame) = 4, Resume(resume) = 5 });
274codec_enum!(Terminator {
275    Jump(block) = 0,
276    Branch { cond, then, otherwise } = 1,
277    Select(arms) = 2,
278    ParagraphEnd { next } = 3,
279    GoTo(para) = 4,
280    Switch { value, targets, otherwise } = 5,
281    ExitProgram { next } = 7,
282    End(ending) = 8,
283    Abend(abend) = 9,
284    AlteredGoTo { para, otherwise } = 10,
285    Debug { range, name, next } = 11,
286    PerformEnter { range, ret, resume } = 12,
287});
288codec_struct!(Resume { para, block });
289codec_struct!(Range { first, last, kind });
290codec_enum!(RangeKind { Perform = 0, SortProcedure = 1, UseBeforeReporting = 2, UseProcedure = 3, Debugging = 4, Processing = 5 });
291codec_struct!(Declaratives { modes, debug_item });
292codec_struct!(ReturnPoint { frame, resume });
293codec_struct!(Frame { id, kind, displaced, segment, depth, temps });
294codec_enum!(FrameKind { Main = 0, Perform { range, ret, resume } = 1, Procedure { range } = 2 });
295codec_struct!(Returns { armed, saved, frames, next_frame });