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