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, 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    Accept { target: PlaceId, from: AcceptFrom, plan: MovePlan },
51    File(FileOpId),
52    Call(CallId),
53    Cancel(Operand),
54    Sort(SortId),
55    Release(ReleaseId),
56    Return(ReturnId),
57    Report(ReportOp),
58    Invoke(InvokeId),
59    Cics(CicsId),
60    Sql(SqlId),
61    /// ALTER: from now on control reaching paragraph `para` goes to `to`. The alter table lives as
62    /// long as the program's WORKING-STORAGE.
63    Alter { para: ParaId, to: ParaId },
64    /// Control reaching a paragraph of segment `priority`: when that differs from the segment
65    /// register and is 50 or more, the alter entries of the segment's paragraphs are cleared. The
66    /// register then holds `priority`.
67    EnterSegment(u8),
68    /// Under the DEBUG option: the line of the statement starting, which DEBUG-LINE gives.
69    DebugLine(u32),
70    /// Under the DEBUG option, after an ALTER of a paragraph a debugging section serves: that
71    /// section, with DEBUG-NAME `name` and DEBUG-CONTENTS `contents`, the TO PROCEED TO name.
72    DebugAlter { range: RangeId, name: SymId, contents: SymId },
73    /// JSON GENERATE, JSON PARSE, XML GENERATE or XML PARSE (lir.md §9.13).
74    Markup(MarkupId),
75}
76
77/// What an op tells the VM.
78#[derive(Clone, Copy, Debug, PartialEq, Eq)]
79pub enum Step {
80    Next,
81    /// The handler a service selected, for the block's `Select`.
82    Arm(u8),
83    /// A transfer a service chose at run time (HANDLE CONDITION), or a procedure it
84    /// ran left by.
85    GoTo(ParaId),
86    End(Ending),
87    /// A procedure the op ran passed the return point of active frame `frame`, which completes.
88    Return(u64),
89    /// A procedure the op ran passed the return point of a PERFORM control had left.
90    Resume(Resume),
91}
92
93#[derive(Clone, Debug, PartialEq, Eq)]
94pub enum Terminator {
95    Jump(BlockId),
96    Branch { cond: CondId, then: BlockId, otherwise: BlockId },
97    /// On the `Arm` the block's last op returned.
98    Select(Vec<BlockId>),
99    /// Control passes the end of paragraph `next` − 1 for `next`, which may be one past the last:
100    /// the return point armed there, if any, is taken.
101    ParagraphEnd { next: ParaId },
102    GoTo(ParaId),
103    /// GO TO … DEPENDING ON: `targets[k − 1]` for k in range, taken as a `GoTo`; else `otherwise`.
104    Switch { value: IntExpr, targets: Vec<ParaId>, otherwise: BlockId },
105    /// An out-of-line PERFORM: push a frame, arm its return point and enter the range; `ret` runs
106    /// when it completes.
107    PerformEnter { range: RangeId, ret: BlockId, resume: Option<Resume> },
108    /// Nothing in the run unit's first program, GOBACK in any other.
109    ExitProgram { next: BlockId },
110    End(Ending),
111    Abend(AbendId),
112    /// The entry of a paragraph an ALTER names: a `GoTo` of the target the alter table holds for
113    /// `para`, or `Jump(otherwise)` while it holds none.
114    AlteredGoTo { para: ParaId, otherwise: BlockId },
115    /// Under the DEBUG option, the entry of a paragraph a debugging section serves: the section
116    /// runs with DEBUG-NAME `name`, then `next`; a section that leaves takes the top frame with it.
117    Debug { range: RangeId, name: SymId, next: BlockId },
118}
119
120/// The statement after an out-of-line PERFORM that runs once and is a statement of paragraph
121/// `para`: `block`, where control resumes when it passes that PERFORM's return point after
122/// leaving its range. `block` also keys what the PERFORM displaced.
123#[derive(Clone, Copy, Debug, PartialEq, Eq)]
124pub struct Resume {
125    pub para: ParaId,
126    pub block: BlockId,
127}
128
129#[derive(Clone, Copy, Debug, PartialEq, Eq)]
130pub struct Range {
131    pub first: ParaId,
132    pub last: ParaId,
133    pub kind: RangeKind,
134}
135
136impl Range {
137    /// The paragraphs a GO TO stays in the range for, of a program of `paragraphs`: `first` to
138    /// `last`, or to the program's last paragraph when `last` comes before `first`; every one for
139    /// a SORT or MERGE procedure.
140    pub fn region(&self, paragraphs: u32) -> (ParaId, ParaId) {
141        let end = paragraphs.saturating_sub(1);
142        match self.kind {
143            RangeKind::SortProcedure => (0, end),
144            _ if self.last < self.first => (self.first, end),
145            _ => (self.first, self.last),
146        }
147    }
148}
149
150#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
151pub enum RangeKind {
152    Perform,
153    SortProcedure,
154    UseBeforeReporting,
155    /// A USE AFTER EXCEPTION/ERROR procedure.
156    UseProcedure,
157    /// A USE FOR DEBUGGING section.
158    Debugging,
159    /// An XML PARSE processing procedure.
160    Processing,
161}
162
163/// What the declaratives need at run time besides each file's own procedure (`FileDesc.error`).
164#[derive(Clone, Debug, Default, PartialEq, Eq)]
165pub struct Declaratives {
166    /// The EXCEPTION/ERROR procedures for files open, or being opened, INPUT, OUTPUT, I-O and
167    /// EXTEND, in that order.
168    pub modes: [Option<RangeId>; 4],
169    /// DEBUG-ITEM's offset and length in the slab, when a debugging section can run.
170    pub debug_item: Option<(u32, u32)>,
171}
172
173/// A return point armed at the end of a range's last paragraph by frame `frame`, with the PERFORM's
174/// resume.
175#[derive(Clone, Copy, Debug, PartialEq, Eq)]
176pub struct ReturnPoint {
177    pub frame: u64,
178    pub resume: Option<Resume>,
179}
180
181/// An active range. `displaced` is the return point it replaced at its range's end, put back when
182/// it completes; `segment` the segment register when it was pushed, back when it completes. Its
183/// paragraphs run at `depth`, and `temps` are the TIMES counters of the statements that run under
184/// it, by `TempId`.
185#[derive(Clone, Debug, PartialEq, Eq)]
186pub struct Frame {
187    pub id: u64,
188    pub kind: FrameKind,
189    pub displaced: Option<ReturnPoint>,
190    pub segment: u8,
191    pub depth: u32,
192    pub temps: Vec<i64>,
193}
194
195#[derive(Clone, Copy, Debug, PartialEq, Eq)]
196pub enum FrameKind {
197    /// The program's own run: every paragraph, and no return point.
198    Main,
199    Perform { range: RangeId, ret: BlockId, resume: Option<Resume> },
200    /// A procedure a service or a `Debug` runs in a dispatch loop of its own, which ends when
201    /// the frame completes or is left.
202    Procedure { range: RangeId },
203}
204
205/// The return points of one activation: the one armed at each paragraph's end, what each
206/// resumable PERFORM control left displaced (by its `Resume.block`), and the frames, Main first.
207#[derive(Clone, Debug, Default, PartialEq, Eq)]
208pub struct Returns {
209    pub armed: Vec<Option<ReturnPoint>>,
210    pub saved: BTreeMap<BlockId, Option<ReturnPoint>>,
211    pub frames: Vec<Frame>,
212    pub next_frame: u64,
213}
214
215codec_enum!(Op {
216    Move { from, to, plan, check } = 0,
217    Initialize { target, plan } = 1,
218    Arith(id) = 2,
219    SetAddress { records, address } = 3,
220    SetUpDown { by, down, targets } = 4,
221    Step { var, by, plan, prepass } = 5,
222    SetInt { target, value } = 6,
223    Inspect(id) = 7,
224    String(id) = 8,
225    Unstring(id) = 9,
226    SearchAll(id) = 10,
227    Nest = 11,
228    Unnest(n) = 12,
229    SetTemp(temp, value) = 13,
230    DecTemp(temp) = 14,
231    Display(id) = 15,
232    Accept { target, from, plan } = 16,
233    File(id) = 17,
234    Call(id) = 18,
235    Cancel(name) = 19,
236    Sort(id) = 20,
237    Release(id) = 21,
238    Return(id) = 22,
239    Report(op) = 23,
240    Invoke(id) = 24,
241    Cics(id) = 25,
242    Sql(id) = 26,
243    Alter { para, to } = 27,
244    EnterSegment(priority) = 28,
245    DebugLine(line) = 30,
246    DebugAlter { range, name, contents } = 31,
247    Markup(id) = 32,
248    Set { from, to, plan } = 33,
249    SetCount(temp, odo) = 34,
250    SetEntry { entry, targets } = 35,
251});
252codec_enum!(Step { Next = 0, Arm(arm) = 1, GoTo(para) = 2, End(ending) = 3, Return(frame) = 4, Resume(resume) = 5 });
253codec_enum!(Terminator {
254    Jump(block) = 0,
255    Branch { cond, then, otherwise } = 1,
256    Select(arms) = 2,
257    ParagraphEnd { next } = 3,
258    GoTo(para) = 4,
259    Switch { value, targets, otherwise } = 5,
260    ExitProgram { next } = 7,
261    End(ending) = 8,
262    Abend(abend) = 9,
263    AlteredGoTo { para, otherwise } = 10,
264    Debug { range, name, next } = 11,
265    PerformEnter { range, ret, resume } = 12,
266});
267codec_struct!(Resume { para, block });
268codec_struct!(Range { first, last, kind });
269codec_enum!(RangeKind { Perform = 0, SortProcedure = 1, UseBeforeReporting = 2, UseProcedure = 3, Debugging = 4, Processing = 5 });
270codec_struct!(Declaratives { modes, debug_item });
271codec_struct!(ReturnPoint { frame, resume });
272codec_struct!(Frame { id, kind, displaced, segment, depth, temps });
273codec_enum!(FrameKind { Main = 0, Perform { range, ret, resume } = 1, Procedure { range } = 2 });
274codec_struct!(Returns { armed, saved, frames, next_frame });