Skip to main content

rucc_codegen/
term.rs

1//! The IR as something a lowering rule can match against.
2//!
3//! Design: `spec/10-backend.md` section 10.2.
4//!
5//! A rule is written about a term and the compiler has no terms. It has a function full of
6//! instructions, and what a pattern is about is one of them together with whatever its operands
7//! were computed from. So this is the [`Subject`] the matcher asks its three questions of, and
8//! the answers come out of the IR: nothing is built and nothing is thrown away.
9//!
10//! # How an operand is shown
11//!
12//! The same IR value can be several different terms. `(add.i32 (value.i32 x) (iconst.i32 k))`
13//! and `(add.i32 (value.i32 x) (value.i32 y))` are two patterns over one instruction, and which
14//! one it is depends on whether the second operand is a constant and on whether the rule that
15//! wants a constant will take this one. `(add.i64 (value.i64 x) (mul.i64 (value.i64 y)
16//! (iconst.i64 4)))` is a third, and it is about two instructions rather than one.
17//!
18//! The matcher does not backtrack across alternatives for one node: [`Subject::head`] gives one
19//! answer and the walk believes it. So the choice is made before the walk rather than during it.
20//! A [`Plan`] says how each operand of the instruction is shown, the selector tries the plans in
21//! order, and the first that matches is the one that fires. There are at most three ways to show
22//! an operand and at most two operands in any pattern this rule set has, so the whole of the
23//! search is a handful of walks over a trie, each of which fails in its first node or two.
24//!
25//! # How deep it goes
26//!
27//! One level. An operand may be shown as the instruction that computed it, and that
28//! instruction's own operands are shown as a register or as a constant and never expanded
29//! again, which is as deep as any pattern in `x86-64.rules` reaches. A rule set that wants three
30//! levels needs this to grow a level, and it would be found by the rule failing to fire rather
31//! than by anything going wrong.
32
33use rucc_ir::{Def, Extra, Func, Inst, IntPred, Opcode, Type, Value};
34
35use crate::select::Subject;
36
37/// How many operands of one instruction a plan can speak about.
38///
39/// Two is what every pattern in the rule set needs, and a third costs nothing to carry. An
40/// instruction with more operands than this is one no rule matches, which is the same answer it
41/// would get from a plan that could describe it.
42pub const MAX_ARGS: usize = 3;
43
44/// How one operand is shown to the matcher.
45#[derive(Clone, Copy, Debug, PartialEq, Eq)]
46pub enum Shown {
47    /// As a value sitting in a register, which is what `(value.iN x)` matches.
48    Reg,
49    /// As a constant the selector has in hand, which is what `(iconst.iN k)` matches.
50    Const,
51    /// As the instruction that computed it, so a rule can be about two instructions at once.
52    Expand,
53}
54
55/// How every operand of one instruction is shown.
56pub type Plan = [Shown; MAX_ARGS];
57
58/// Everything shown as a register, which is the plan that matches when no other does.
59pub const PLAIN: Plan = [Shown::Reg; MAX_ARGS];
60
61/// One node of the term the matcher is walking.
62///
63/// A position rather than a term, because the term does not exist. Two of these are values in
64/// their own right, and they are the two a pattern can bind: the register a `value` wraps and
65/// the number an `iconst` wraps.
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67pub enum Term {
68    /// The instruction being selected.
69    Root,
70    /// Operand `i` of the root, shown the way the plan says to show it.
71    Arg(u8),
72    /// Operand `j` of the instruction that computed operand `i` of the root.
73    Deep(u8, u8),
74    /// A value in a register, which is what a pattern binds when it writes `(value.iN x)`.
75    Reg(Value),
76    /// A constant, which is what a pattern binds or tests inside an `(iconst.iN k)`.
77    Num(i128),
78}
79
80/// One instruction of a function, as the terms a rule could match.
81#[derive(Debug)]
82pub struct Terms<'a> {
83    func: &'a Func,
84    root: Inst,
85    plan: Plan,
86}
87
88impl<'a> Terms<'a> {
89    /// The instruction, shown the way the plan says.
90    #[must_use]
91    pub fn new(func: &'a Func, root: Inst, plan: Plan) -> Self {
92        Self { func, root, plan }
93    }
94
95    /// The instruction this is about.
96    #[must_use]
97    pub fn root(&self) -> Inst {
98        self.root
99    }
100
101    /// What the root, or an instruction one of its operands was expanded into, is called in a
102    /// rule file.
103    #[must_use]
104    pub fn name(&self, inst: Inst) -> Option<&'static str> {
105        head_of(self.func, inst)
106    }
107
108    /// The value operands of an instruction.
109    fn args(&self, inst: Inst) -> &[Value] {
110        &self.func[self.func[inst].args]
111    }
112
113    /// Operand `index` of the root, or nothing if it has no such operand.
114    fn arg_value(&self, index: u8) -> Option<Value> {
115        self.args(self.root).get(usize::from(index)).copied()
116    }
117
118    /// The instruction a value is the result of, or nothing for a block parameter.
119    fn def_of(&self, value: Value) -> Option<Inst> {
120        match self.func[value].def {
121            Def::Result { inst, .. } => Some(inst),
122            Def::Param { .. } => None,
123        }
124    }
125
126    /// What a value is, if it is a constant.
127    #[must_use]
128    pub fn constant(&self, value: Value) -> Option<i128> {
129        let inst = self.def_of(value)?;
130        let data = &self.func[inst];
131        if data.opcode != Opcode::IConst {
132            return None;
133        }
134        let Extra::Imm(imm) = data.extra else { return None };
135        let ty = self.func[value].ty;
136        ty.is_int().then(|| self.func[imm].signed(ty))
137    }
138
139    /// The head of a value shown as a register or as a constant, which is a term of one
140    /// argument either way: the thing the pattern binds.
141    fn leaf_head(&self, value: Value, shown: Shown) -> Option<(&'static str, usize)> {
142        let ty = self.func[value].ty;
143        let name = match shown {
144            Shown::Reg => value_head(ty)?,
145            Shown::Const => iconst_head(ty)?,
146            // An expansion is not a leaf, and nothing asks this about one.
147            Shown::Expand => return None,
148        };
149        Some((name, 1))
150    }
151
152    /// What a value shown as a register or as a constant binds, which is the value itself or
153    /// the number it is.
154    fn leaf_arg(&self, value: Value, shown: Shown) -> Term {
155        match shown {
156            Shown::Const => self.constant(value).map_or(Term::Reg(value), Term::Num),
157            Shown::Reg | Shown::Expand => Term::Reg(value),
158        }
159    }
160
161    /// How an operand of an expanded operand is shown, which is as a constant when it is one
162    /// and as a register otherwise.
163    ///
164    /// There is no choice to make here. The reason to show a constant as a register is that no
165    /// rule would take it as an immediate, and the answer to that inside an expansion is to
166    /// stop expanding, which is a plan the selector tries anyway.
167    fn deep_shown(&self, value: Value) -> Shown {
168        if self.constant(value).is_some() { Shown::Const } else { Shown::Reg }
169    }
170
171    /// The instruction an expanded operand of the root was computed by, with its operands.
172    fn expansion(&self, index: u8) -> Option<(Inst, &[Value])> {
173        let value = self.arg_value(index)?;
174        let inst = self.def_of(value)?;
175        Some((inst, self.args(inst)))
176    }
177}
178
179impl Subject for Terms<'_> {
180    type Node = Term;
181
182    fn head(&self, node: Term) -> Option<(&str, usize)> {
183        match node {
184            Term::Root => {
185                let name = head_of(self.func, self.root)?;
186                let data = &self.func[self.root];
187                // A constant has no operands and its term has one, which is the constant, so it
188                // is the one instruction whose arity is not the length of its operand list.
189                let arity =
190                    if data.opcode == Opcode::IConst { 1 } else { self.args(self.root).len() };
191                Some((name, arity))
192            }
193            Term::Arg(index) => {
194                let value = self.arg_value(index)?;
195                match self.plan[usize::from(index)] {
196                    Shown::Expand => {
197                        let (inst, args) = self.expansion(index)?;
198                        Some((head_of(self.func, inst)?, args.len()))
199                    }
200                    shown => self.leaf_head(value, shown),
201                }
202            }
203            Term::Deep(outer, inner) => {
204                let (_, args) = self.expansion(outer)?;
205                let value = *args.get(usize::from(inner))?;
206                self.leaf_head(value, self.deep_shown(value))
207            }
208            Term::Reg(_) | Term::Num(_) => None,
209        }
210    }
211
212    fn arg(&self, node: Term, index: usize) -> Term {
213        let index = u8::try_from(index).unwrap_or(u8::MAX);
214        match node {
215            Term::Root => {
216                let data = &self.func[self.root];
217                if data.opcode == Opcode::IConst {
218                    let value = data.first_result.expect("a constant has a result");
219                    return self.leaf_arg(value, Shown::Const);
220                }
221                Term::Arg(index)
222            }
223            Term::Arg(outer) => match self.plan[usize::from(outer)] {
224                Shown::Expand => Term::Deep(outer, index),
225                shown => {
226                    self.arg_value(outer).map_or(Term::Num(0), |value| self.leaf_arg(value, shown))
227                }
228            },
229            Term::Deep(outer, inner) => {
230                let value = self
231                    .expansion(outer)
232                    .and_then(|(_, args)| args.get(usize::from(inner)).copied());
233                value.map_or(Term::Num(0), |value| self.leaf_arg(value, self.deep_shown(value)))
234            }
235            // Neither has a head, so nothing asks either of them for an argument.
236            Term::Reg(_) | Term::Num(_) => node,
237        }
238    }
239
240    fn int(&self, node: Term) -> Option<i128> {
241        match node {
242            Term::Num(value) => Some(value),
243            _ => None,
244        }
245    }
246}
247
248/// What an instruction is called in a rule file, or nothing if the rules have no name for it.
249///
250/// The name carries the width, because a rule file that did not say how wide a term is would be
251/// a file whose reader has to look at the line above to find out. Which widths there are names
252/// for is the rule language's business and not this crate's: an instruction at a width nothing
253/// is written about has no name here, and the answer to it is that no rule matches.
254fn head_of(func: &Func, inst: Inst) -> Option<&'static str> {
255    let data = &func[inst];
256
257    // A store is the one instruction with a name here that computes nothing, so the width in
258    // its name is the width of what it is storing and has to come from an operand. That operand
259    // is the first one, which is the order `rucc_ir::Builder::store` puts them in and the order
260    // a pattern for one is written in.
261    //
262    // Nothing looks at the flags or the ordering, and both of those are worth saying out loud.
263    // A `volatile` access has to happen exactly once and must not move, and neither of those is
264    // something selection does: one IR load is one instruction whatever its flags say, and
265    // folding the address arithmetic into the addressing mode does not change how many times
266    // memory is touched. An ordering would be a different matter, because a store that releases
267    // is not a plain `mov` on any machine where it means anything, but an ordered access is
268    // `atomic_load` or `atomic_store` and those are different opcodes with no name here. The IR
269    // verifier is what makes that true rather than merely usual: it rejects an ordering on a
270    // plain access, so by the time anything is selected there is none to miss.
271    if data.opcode == Opcode::Store {
272        let value = *func[data.args].first()?;
273        return store_head(func[value].ty);
274    }
275
276    // A return is the other one, and the width comes from the operand for the same reason. A
277    // return of nothing has no name, and neither has a return of more than one value: a rule
278    // for either would have to say where each of them goes, and where a value goes is a fact
279    // about the convention rather than about a term, so the rule language has nothing to say
280    // about it. A return of nothing needs no rule at all, since the epilogue is the whole of it.
281    if data.opcode == Opcode::Return {
282        let [value] = &func[data.args] else { return None };
283        return ret_head(func[*value].ty);
284    }
285
286    // A conditional branch is the third instruction here that computes nothing. Where it goes is
287    // not part of its name and not part of any pattern: a machine IR block holds its own
288    // successors, so a rule for a branch never has to say a block, and what is left for it to say
289    // is what the branch is about, which is the condition.
290    if data.opcode == Opcode::BrIf {
291        let [cond] = &func[data.args] else { return None };
292        return (func[*cond].ty == Type::int(1)).then_some("brif.i1");
293    }
294
295    let result = data.first_result?;
296    let ty = func[result].ty;
297    match data.opcode {
298        Opcode::IConst => iconst_head(ty),
299        Opcode::Load => load_head(ty),
300        Opcode::ICmp => {
301            let Extra::IntPred(pred) = data.extra else { return None };
302            Some(icmp_head(pred))
303        }
304        Opcode::SExt | Opcode::ZExt | Opcode::Trunc => {
305            let from = func[*func[data.args].first()?].ty;
306            convert_head(data.opcode, from, ty)
307        }
308        opcode => binary_head(opcode, ty),
309    }
310}
311
312/// Which of the four widths a type is, or nothing for a width no rule is written at.
313fn slot(ty: Type) -> Option<usize> {
314    match ty.is_int().then(|| ty.bits())? {
315        8 => Some(0),
316        16 => Some(1),
317        32 => Some(2),
318        64 => Some(3),
319        _ => None,
320    }
321}
322
323/// What a value in a register is called at that width.
324///
325/// One bit is a width here and is not one in [`slot`], because it is a width a value comes in and
326/// not a width anything is computed at. A comparison produces one, a branch reads one, and the
327/// machine holds it in a whole byte register with the other seven bits zero, which is what a
328/// `setcc` leaves behind and what the model already abstracts over for every comparison.
329fn value_head(ty: Type) -> Option<&'static str> {
330    if ty.is_int() && ty.bits() == 1 {
331        return Some("value.i1");
332    }
333    Some(["value.i8", "value.i16", "value.i32", "value.i64"][slot(ty)?])
334}
335
336/// What a constant is called at that width.
337fn iconst_head(ty: Type) -> Option<&'static str> {
338    Some(["iconst.i8", "iconst.i16", "iconst.i32", "iconst.i64"][slot(ty)?])
339}
340
341/// What a load is called, which is the width of the value it produced.
342fn load_head(ty: Type) -> Option<&'static str> {
343    Some(["load.i8", "load.i16", "load.i32", "load.i64"][slot(ty)?])
344}
345
346/// What a store is called, which is the width of the value it writes, since it produces nothing
347/// to take a width from.
348fn store_head(ty: Type) -> Option<&'static str> {
349    Some(["store.i8", "store.i16", "store.i32", "store.i64"][slot(ty)?])
350}
351
352/// What a return is called, which is the width of the value it gives back, for the same reason.
353fn ret_head(ty: Type) -> Option<&'static str> {
354    Some(["ret.i8", "ret.i16", "ret.i32", "ret.i64"][slot(ty)?])
355}
356
357/// What a comparison is called, which does not carry the width of what it compared: the result
358/// is one bit whatever the operands were, and the operands say how wide they are themselves.
359fn icmp_head(pred: IntPred) -> &'static str {
360    match pred {
361        IntPred::Eq => "icmp_eq.i1",
362        IntPred::Ne => "icmp_ne.i1",
363        IntPred::Slt => "icmp_slt.i1",
364        IntPred::Sle => "icmp_sle.i1",
365        IntPred::Sgt => "icmp_sgt.i1",
366        IntPred::Sge => "icmp_sge.i1",
367        IntPred::Ult => "icmp_ult.i1",
368        IntPred::Ule => "icmp_ule.i1",
369        IntPred::Ugt => "icmp_ugt.i1",
370        IntPred::Uge => "icmp_uge.i1",
371    }
372}
373
374/// What a conversion is called, which is the two widths it is between.
375fn convert_head(opcode: Opcode, from: Type, to: Type) -> Option<&'static str> {
376    let table: &[[Option<&'static str>; 4]; 4] = match opcode {
377        Opcode::SExt => &SEXT,
378        Opcode::ZExt => &ZEXT,
379        Opcode::Trunc => &TRUNC,
380        _ => return None,
381    };
382    table[slot(from)?][slot(to)?]
383}
384
385/// What each of the binary operations is called at each width.
386fn binary_head(opcode: Opcode, ty: Type) -> Option<&'static str> {
387    let names: &[&'static str; 4] = match opcode {
388        Opcode::Add => &["add.i8", "add.i16", "add.i32", "add.i64"],
389        Opcode::Sub => &["sub.i8", "sub.i16", "sub.i32", "sub.i64"],
390        Opcode::Mul => &["mul.i8", "mul.i16", "mul.i32", "mul.i64"],
391        Opcode::SDiv => &["sdiv.i8", "sdiv.i16", "sdiv.i32", "sdiv.i64"],
392        Opcode::UDiv => &["udiv.i8", "udiv.i16", "udiv.i32", "udiv.i64"],
393        Opcode::SRem => &["srem.i8", "srem.i16", "srem.i32", "srem.i64"],
394        Opcode::URem => &["urem.i8", "urem.i16", "urem.i32", "urem.i64"],
395        Opcode::And => &["and.i8", "and.i16", "and.i32", "and.i64"],
396        Opcode::Or => &["or.i8", "or.i16", "or.i32", "or.i64"],
397        Opcode::Xor => &["xor.i8", "xor.i16", "xor.i32", "xor.i64"],
398        Opcode::Shl => &["shl.i8", "shl.i16", "shl.i32", "shl.i64"],
399        Opcode::LShr => &["lshr.i8", "lshr.i16", "lshr.i32", "lshr.i64"],
400        Opcode::AShr => &["ashr.i8", "ashr.i16", "ashr.i32", "ashr.i64"],
401        _ => return None,
402    };
403    Some(names[slot(ty)?])
404}
405
406/// The widening conversions, from the width down the side to the width across the top. The
407/// diagonal and everything below it is empty, because a sign extension to a width it already
408/// has is not an instruction and the IR does not have one.
409static SEXT: [[Option<&str>; 4]; 4] = [
410    [None, Some("sext.i8.i16"), Some("sext.i8.i32"), Some("sext.i8.i64")],
411    [None, None, Some("sext.i16.i32"), Some("sext.i16.i64")],
412    [None, None, None, Some("sext.i32.i64")],
413    [None, None, None, None],
414];
415
416static ZEXT: [[Option<&str>; 4]; 4] = [
417    [None, Some("zext.i8.i16"), Some("zext.i8.i32"), Some("zext.i8.i64")],
418    [None, None, Some("zext.i16.i32"), Some("zext.i16.i64")],
419    [None, None, None, Some("zext.i32.i64")],
420    [None, None, None, None],
421];
422
423/// The narrowing ones, which fill the other corner for the same reason.
424static TRUNC: [[Option<&str>; 4]; 4] = [
425    [None, None, None, None],
426    [Some("trunc.i16.i8"), None, None, None],
427    [Some("trunc.i32.i8"), Some("trunc.i32.i16"), None, None],
428    [Some("trunc.i64.i8"), Some("trunc.i64.i16"), Some("trunc.i64.i32"), None],
429];
430
431#[cfg(test)]
432mod tests {
433    use rucc_base::Interner;
434    use rucc_ir::{Builder, Flags, Signature};
435
436    use super::*;
437    use crate::select::Subject;
438
439    /// A function with one block, and the builder to put instructions in it.
440    fn func() -> (Func, rucc_ir::Block) {
441        let mut names = Interner::new();
442        let mut func = Func::new(names.intern("f"), Signature::new());
443        let block = func.create_block();
444        (func, block)
445    }
446
447    /// The instruction that computed a value, which every value in these tests has.
448    fn inst_of(func: &Func, value: Value) -> Inst {
449        match func[value].def {
450            Def::Result { inst, .. } => inst,
451            Def::Param { .. } => unreachable!(),
452        }
453    }
454
455    #[test]
456    fn an_instruction_is_the_term_the_rule_file_names_it_by() {
457        let (mut func, block) = func();
458        let i32 = Type::int(32);
459        let mut build = Builder::new(&mut func, block);
460        let k = build.iconst(i32, 7);
461        let x = build.iconst(i32, 3);
462        let sum = build.binary(Opcode::Add, x, k, Flags::default());
463        let add = inst_of(&func, sum);
464
465        let terms = Terms::new(&func, add, PLAIN);
466        assert_eq!(terms.head(Term::Root), Some(("add.i32", 2)));
467        assert_eq!(terms.head(Term::Arg(0)), Some(("value.i32", 1)));
468        assert_eq!(terms.arg(Term::Arg(0), 0), Term::Reg(x));
469        assert_eq!(terms.head(Term::Reg(x)), None);
470        assert_eq!(terms.int(Term::Reg(x)), None);
471    }
472
473    #[test]
474    fn an_operand_shown_as_a_constant_gives_the_number_up() {
475        let (mut func, block) = func();
476        let i32 = Type::int(32);
477        let mut build = Builder::new(&mut func, block);
478        let x = build.iconst(i32, 3);
479        let k = build.iconst(i32, -7);
480        let sum = build.binary(Opcode::Add, x, k, Flags::default());
481        let add = inst_of(&func, sum);
482
483        let terms = Terms::new(&func, add, [Shown::Reg, Shown::Const, Shown::Reg]);
484        assert_eq!(terms.head(Term::Arg(1)), Some(("iconst.i32", 1)));
485        assert_eq!(terms.arg(Term::Arg(1), 0), Term::Num(-7));
486        assert_eq!(terms.int(Term::Num(-7)), Some(-7));
487        // The same operand shown as a register is a register, and a guard asking what number it
488        // is gets no answer, which is what makes a rule about a number decline it.
489        let plain = Terms::new(&func, add, PLAIN);
490        assert_eq!(plain.head(Term::Arg(1)), Some(("value.i32", 1)));
491        assert_eq!(plain.int(plain.arg(Term::Arg(1), 0)), None);
492    }
493
494    #[test]
495    fn a_constant_is_a_term_of_one_argument_and_has_no_operands() {
496        let (mut func, block) = func();
497        let mut build = Builder::new(&mut func, block);
498        let k = build.iconst(Type::int(64), 12);
499        let inst = inst_of(&func, k);
500
501        let terms = Terms::new(&func, inst, PLAIN);
502        assert_eq!(terms.head(Term::Root), Some(("iconst.i64", 1)));
503        assert_eq!(terms.arg(Term::Root, 0), Term::Num(12));
504    }
505
506    #[test]
507    fn an_expanded_operand_is_the_instruction_that_computed_it() {
508        let (mut func, block) = func();
509        let i64 = Type::int(64);
510        // A parameter, because the point of the test is an operand that is not a constant.
511        let y = func.append_param(block, i64);
512        let mut build = Builder::new(&mut func, block);
513        let x = build.iconst(i64, 1);
514        let four = build.iconst(i64, 4);
515        let scaled = build.binary(Opcode::Mul, y, four, Flags::default());
516        let sum = build.binary(Opcode::Add, x, scaled, Flags::default());
517        let add = inst_of(&func, sum);
518
519        let terms = Terms::new(&func, add, [Shown::Reg, Shown::Expand, Shown::Reg]);
520        assert_eq!(terms.head(Term::Root), Some(("add.i64", 2)));
521        assert_eq!(terms.head(Term::Arg(1)), Some(("mul.i64", 2)));
522        assert_eq!(terms.head(Term::Deep(1, 0)), Some(("value.i64", 1)));
523        assert_eq!(terms.arg(Term::Deep(1, 0), 0), Term::Reg(y));
524        // The constant inside an expansion is shown as one without being asked to be.
525        assert_eq!(terms.head(Term::Deep(1, 1)), Some(("iconst.i64", 1)));
526        assert_eq!(terms.arg(Term::Deep(1, 1), 0), Term::Num(4));
527    }
528
529    #[test]
530    fn a_comparison_says_which_one_it_is_and_a_conversion_says_both_widths() {
531        let (mut func, block) = func();
532        let mut build = Builder::new(&mut func, block);
533        let x = build.iconst(Type::int(32), 1);
534        let y = build.iconst(Type::int(32), 2);
535        let less = build.icmp(IntPred::Slt, x, y);
536        let wide = build.unary(Opcode::SExt, x, Type::int(64));
537        let narrow = build.unary(Opcode::Trunc, x, Type::int(8));
538        let cmp = inst_of(&func, less);
539        assert_eq!(Terms::new(&func, cmp, PLAIN).head(Term::Root), Some(("icmp_slt.i1", 2)));
540        let sext = inst_of(&func, wide);
541        assert_eq!(Terms::new(&func, sext, PLAIN).head(Term::Root), Some(("sext.i32.i64", 1)));
542        let trunc = inst_of(&func, narrow);
543        assert_eq!(Terms::new(&func, trunc, PLAIN).head(Term::Root), Some(("trunc.i32.i8", 1)));
544    }
545
546    #[test]
547    fn a_width_no_rule_is_written_at_has_no_name() {
548        let (mut func, block) = func();
549        let mut build = Builder::new(&mut func, block);
550        let x = build.iconst(Type::int(128), 1);
551        let inst = inst_of(&func, x);
552        assert_eq!(Terms::new(&func, inst, PLAIN).head(Term::Root), None);
553    }
554}