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