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