Skip to main content

veryl_analyzer/ir/
function.rs

1use crate::conv::Context;
2use crate::conv::checker::portability::allow_multiple_assign;
3use crate::conv::utils::{
4    check_compatibility, check_implicit_clock_conversion, eval_array_literal,
5};
6use crate::ir::assign_table::{AssignContext, AssignTable};
7use crate::ir::ff_table::AssignTarget;
8use crate::ir::{
9    AssignDestination, Comptime, Expression, FfTable, IrResult, Shape, Signature, Statement,
10    ValueVariant, VarId, VarIndex, VarPath, VarPathSelect,
11};
12use crate::symbol::{Direction, Symbol, SymbolId, SymbolKind};
13use crate::value::{Value, ValueBigUint};
14use crate::{AnalyzerError, HashMap, ir_error};
15use indent::indent_all_by;
16use std::fmt;
17use veryl_parser::resource_table::StrId;
18use veryl_parser::token_range::TokenRange;
19
20#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
21pub struct FuncPath {
22    pub path: VarPath,
23    pub sig: Signature,
24}
25
26impl FuncPath {
27    pub fn new(id: SymbolId) -> Self {
28        Self {
29            path: VarPath::default(),
30            sig: Signature::new(id),
31        }
32    }
33
34    pub fn add_prelude(&mut self, x: &[StrId]) {
35        self.path.add_prelude(x)
36    }
37
38    pub fn base(&self) -> FuncPath {
39        let mut ret = self.clone();
40        ret.sig.parameters.clear();
41        ret.sig.generic_parameters.clear();
42        ret
43    }
44}
45
46impl fmt::Display for FuncPath {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        let mut ret = String::new();
49
50        if !self.path.0.is_empty() {
51            ret.push_str(&format!("{}.", self.path));
52        }
53        ret.push_str(&format!("{}", self.sig));
54
55        ret.fmt(f)
56    }
57}
58
59#[derive(Clone)]
60pub struct FuncArg {
61    pub name: StrId,
62    pub comptime: Comptime,
63    pub members: Vec<(VarPath, Comptime, Direction)>,
64}
65
66#[derive(Clone)]
67pub struct Function {
68    pub name: StrId,
69    pub id: VarId,
70    pub path: FuncPath,
71    pub r#type: Comptime,
72    pub array: Shape,
73    pub arity: usize,
74    pub args: Vec<FuncArg>,
75    pub is_const: bool,
76    pub functions: Vec<FunctionBody>,
77    pub token: TokenRange,
78}
79
80impl Function {
81    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
82        for x in &self.functions {
83            x.eval_assign(context, assign_table);
84        }
85    }
86
87    pub fn set_index(&mut self, index: &VarIndex) {
88        for x in &mut self.functions {
89            x.set_index(index);
90        }
91    }
92
93    pub fn get_function(&self, index: &[usize]) -> Option<FunctionBody> {
94        let index = self.array.calc_index(index)?;
95        self.functions.get(index).cloned()
96    }
97
98    pub fn to_proto(&self) -> FuncProto {
99        FuncProto {
100            name: self.name,
101            id: self.id,
102            r#type: self.r#type.clone(),
103            arity: self.arity,
104            args: self.args.clone(),
105            token: self.token,
106        }
107    }
108}
109
110#[derive(Clone)]
111pub struct FuncProto {
112    pub name: StrId,
113    pub id: VarId,
114    pub r#type: Comptime,
115    pub arity: usize,
116    pub args: Vec<FuncArg>,
117    pub token: TokenRange,
118}
119
120#[derive(Clone)]
121pub struct FunctionBody {
122    pub ret: Option<VarId>,
123    pub arg_map: HashMap<VarPath, VarId>,
124    pub statements: Vec<Statement>,
125}
126
127impl FunctionBody {
128    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
129        for x in &self.statements {
130            x.eval_assign(context, assign_table, AssignContext::Function, &[]);
131        }
132    }
133
134    pub fn set_index(&mut self, index: &VarIndex) {
135        for x in &mut self.statements {
136            x.set_index(index);
137        }
138    }
139}
140
141impl fmt::Display for Function {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        let mut ret = String::new();
144
145        for (i, f) in self.functions.iter().enumerate() {
146            if self.functions.len() == 1 {
147                ret.push_str(&format!("func {}({})", self.id, self.path));
148            } else {
149                ret.push_str(&format!("func {}[{}]({})", self.id, i, self.path));
150            }
151
152            if let Some(x) = f.ret {
153                ret.push_str(&format!(" -> {x}"));
154            }
155            ret.push_str(" {\n");
156
157            for s in &f.statements {
158                let text = format!("{}\n", s);
159                ret.push_str(&indent_all_by(2, text));
160            }
161
162            ret.push_str("}\n");
163        }
164
165        ret.trim_end().fmt(f)
166    }
167}
168
169#[derive(Clone, Debug)]
170pub struct FunctionCall {
171    pub id: VarId,
172    pub index: Option<Vec<usize>>,
173    pub comptime: Comptime,
174    pub inputs: CallArgs<Expression>,
175    /// Output bindings that may actually be written by this specialization.
176    pub outputs: CallArgs<Vec<AssignDestination>>,
177}
178
179impl FunctionCall {
180    pub fn eval_type(&mut self, context: &mut Context) {
181        self.comptime.is_const = self.eval_comptime_flag(context);
182    }
183
184    pub fn eval_value(&self, context: &mut Context) -> Option<Value> {
185        let func = context.functions.get(&self.id)?;
186        let func = if let Some(x) = &self.index {
187            func.get_function(x)
188        } else {
189            func.get_function(&[])
190        }?;
191
192        // set inputs
193        for (path, expr) in &self.inputs {
194            let id = func.arg_map.get(path)?;
195            let value = expr.eval_value(context)?;
196            let var = context.variable_mut(id)?;
197            var.set_value(&[], value, None);
198        }
199
200        let disable_const_opt = context.disalbe_const_opt;
201        context.disalbe_const_opt = true;
202        let prior_overflow = context.comptime_for_overflow.take();
203        // Saved and restored around the body: a call nested in an argument or
204        // in the body itself must not inherit this frame's return state.
205        let prior_ret_var = std::mem::replace(&mut context.function_ret_var, func.ret);
206        let prior_returned = std::mem::replace(&mut context.function_returned, false);
207        for x in &func.statements {
208            if x.eval_value(context) == crate::ir::ControlFlow::Break {
209                break;
210            }
211        }
212        context.function_ret_var = prior_ret_var;
213        context.function_returned = prior_returned;
214        let overflowed = context.comptime_for_overflow.is_some();
215        if let Some(x) = prior_overflow {
216            context.comptime_for_overflow.get_or_insert(x);
217        }
218        context.disalbe_const_opt = disable_const_opt;
219
220        // TODO get outputs
221
222        // A skipped over-limit for loop leaves the return value at its
223        // pre-loop state; treat the call as unevaluable instead.
224        if overflowed {
225            return None;
226        }
227
228        if let Some(x) = &func.ret {
229            let variable = context.variables.get(x)?;
230            variable.get_value(&[]).cloned()
231        } else {
232            None
233        }
234    }
235
236    pub fn eval_comptime(&mut self, context: &mut Context) -> Comptime {
237        let value = self.eval_value(context);
238        let value = if let Some(x) = value {
239            ValueVariant::Numeric(x)
240        } else {
241            ValueVariant::Unknown
242        };
243
244        let mut ret = self.comptime.clone();
245        ret.value = value;
246
247        ret.is_const = self.eval_comptime_flag(context);
248        ret
249    }
250
251    pub fn eval_assign(
252        &self,
253        context: &mut Context,
254        assign_table: &mut AssignTable,
255        assign_context: AssignContext,
256    ) {
257        // Record reads from input expressions so that downstream consumers
258        // of `AssignTable.refernced` (e.g. the combinational-loop detector)
259        // see input-variable references at the call site.
260        for expr in self.inputs.values() {
261            expr.eval_assign(context, assign_table, assign_context);
262        }
263        for output in self.outputs.values() {
264            for dst in output {
265                if let Some(index) = dst.index.eval_value(context) {
266                    let Some(variable) = context.get_variable_info(dst.id) else {
267                        continue;
268                    };
269                    if let Some((beg, end)) =
270                        dst.select.eval_value(context, &variable.r#type, false)
271                    {
272                        let mask = ValueBigUint::gen_mask_range(beg, end);
273                        let (success, tokens) = assign_table.insert_assign(
274                            &variable,
275                            index,
276                            mask,
277                            false,
278                            false,
279                            self.comptime.token,
280                        );
281                        if !success
282                            && assign_context.is_ff()
283                            && !allow_multiple_assign(context, dst.id)
284                        {
285                            context.insert_error(AnalyzerError::multiple_assignment(
286                                &variable.path.to_string(),
287                                &self.comptime.token,
288                                &tokens,
289                            ));
290                        }
291                    }
292                }
293            }
294        }
295    }
296
297    pub fn gather_ff(
298        &self,
299        context: &mut Context,
300        table: &mut FfTable,
301        decl: usize,
302        assign_target: Option<&AssignTarget>,
303        from_ff: bool,
304    ) {
305        for input in self.inputs.values() {
306            input.gather_ff(context, table, decl, assign_target, from_ff);
307        }
308        for dsts in self.outputs.values() {
309            for dst in dsts {
310                dst.gather_ff(context, table, decl);
311            }
312        }
313    }
314
315    pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
316        for dsts in self.outputs.values() {
317            for dst in dsts {
318                dst.gather_ff_comb_assign(context, table, decl);
319            }
320        }
321    }
322
323    pub fn set_index(&mut self, index: &VarIndex) {
324        for x in self.inputs.values_mut() {
325            x.set_index(index);
326        }
327        for x in self.outputs.values_mut() {
328            for x in x {
329                x.set_index(index);
330            }
331        }
332    }
333
334    fn eval_comptime_flag(&mut self, context: &mut Context) -> bool {
335        let mut is_const = context
336            .functions
337            .get(&self.id)
338            .map(|func| func.is_const)
339            .unwrap_or(true);
340        for expr in self.inputs.values_mut() {
341            is_const &= expr.eval_comptime(context, None).is_const;
342        }
343
344        // function with side-effect through output ports is not const
345        if !self.outputs.is_empty() {
346            is_const = false;
347        }
348
349        is_const
350    }
351}
352
353impl fmt::Display for FunctionCall {
354    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355        let mut args = String::new();
356
357        let mut inputs: Vec<_> = self.inputs.iter().map(|(k, v)| (k, v)).collect();
358        let mut outputs: Vec<_> = self.outputs.iter().map(|(k, v)| (k, v)).collect();
359        inputs.sort_by_key(|x| x.0);
360        outputs.sort_by_key(|x| x.0);
361
362        for (id, val) in &inputs {
363            args.push_str(&format!("{id}: {val}, "));
364        }
365        for (id, val) in &outputs {
366            if val.len() == 1 {
367                args.push_str(&format!("{id}: {}, ", val[0]));
368            } else {
369                args.push_str(&format!("{id}: {{{}", val[0]));
370                for x in &val[1..] {
371                    args.push_str(&format!(", {x}"));
372                }
373                args.push_str("}}, ");
374            }
375        }
376        let args = if args.is_empty() {
377            &args
378        } else {
379            &args[0..args.len() - 2]
380        };
381
382        let mut index = String::new();
383        if let Some(x) = &self.index {
384            for x in x {
385                index.push_str(&format!("[{}]", x));
386            }
387        }
388
389        format!("{}{}({})", self.id, index, args).fmt(f)
390    }
391}
392
393pub type PositionalArgs = Vec<(Expression, Vec<VarPathSelect>, TokenRange)>;
394pub type NamedArgs = Vec<(StrId, (Expression, Vec<VarPathSelect>, TokenRange))>;
395pub type FunctionArgs = (CallArgs<Expression>, CallArgs<Vec<AssignDestination>>);
396
397/// Argument bindings of a [`FunctionCall`], in source argument order.
398/// Not a map: `VarPath` keys are interned `StrId`s whose numeric values
399/// vary run-to-run (parallel interning), so map iteration would make the
400/// consumers' emission order — and the generated code — nondeterministic.
401/// Built only by `to_function_args`, which preserves the source order.
402#[derive(Clone, Debug, Default)]
403pub struct CallArgs<T>(Vec<(VarPath, T)>);
404
405impl<T> CallArgs<T> {
406    pub fn values(&self) -> impl Iterator<Item = &T> {
407        self.0.iter().map(|(_, v)| v)
408    }
409
410    pub fn values_mut(&mut self) -> impl Iterator<Item = &mut T> {
411        self.0.iter_mut().map(|(_, v)| v)
412    }
413}
414
415impl<T> std::ops::Deref for CallArgs<T> {
416    type Target = [(VarPath, T)];
417    fn deref(&self) -> &Self::Target {
418        &self.0
419    }
420}
421
422impl<T> std::ops::DerefMut for CallArgs<T> {
423    fn deref_mut(&mut self) -> &mut Self::Target {
424        &mut self.0
425    }
426}
427
428impl<'a, T> IntoIterator for &'a CallArgs<T> {
429    type Item = &'a (VarPath, T);
430    type IntoIter = std::slice::Iter<'a, (VarPath, T)>;
431    fn into_iter(self) -> Self::IntoIter {
432        self.0.iter()
433    }
434}
435
436#[derive(Clone)]
437pub enum Arguments {
438    Positional(PositionalArgs),
439    Named(NamedArgs),
440    Mixed(PositionalArgs, NamedArgs),
441    Null,
442}
443
444impl Arguments {
445    pub fn is_empty(&self) -> bool {
446        self.len() == 0
447    }
448
449    pub fn len(&self) -> usize {
450        match self {
451            Arguments::Positional(x) => x.len(),
452            Arguments::Named(x) => x.len(),
453            Arguments::Mixed(x, y) => x.len() + y.len(),
454            Arguments::Null => 0,
455        }
456    }
457
458    pub fn to_system_function_args(
459        self,
460        _context: &mut Context,
461        symbol: &Symbol,
462    ) -> Vec<(Expression, Vec<VarPathSelect>, TokenRange)> {
463        let ret = match self {
464            Arguments::Positional(x) => x,
465            Arguments::Named(_) => {
466                // TODO error
467                return vec![];
468            }
469            Arguments::Mixed(_, _) => vec![],
470            Arguments::Null => vec![],
471        };
472
473        if let SymbolKind::SystemFunction(x) = &symbol.kind {
474            let arity = x.ports.len();
475
476            if arity != ret.len() {
477                // TODO
478                //let name = symbol.token.text.to_string();
479                //context.insert_error(AnalyzerError::mismatch_function_arity(
480                //    &name,
481                //    arity,
482                //    ret.len(),
483                //    &symbol.token.into(),
484                //))
485            }
486        }
487
488        ret
489    }
490
491    pub fn to_function_args(
492        self,
493        context: &mut Context,
494        func: &FuncProto,
495        token: TokenRange,
496    ) -> IrResult<FunctionArgs> {
497        let mut inputs: Vec<(VarPath, Expression)> = Vec::new();
498        let mut outputs: Vec<(VarPath, Vec<AssignDestination>)> = Vec::new();
499
500        if func.arity != self.len() {
501            context.insert_error(AnalyzerError::mismatch_function_arity(
502                &func.name.to_string(),
503                func.arity,
504                self.len(),
505                &token,
506            ));
507            return Err(ir_error!(func.token));
508        }
509
510        let mut arg_map_by_name = HashMap::default();
511        let mut arg_map_by_index = HashMap::default();
512        for (i, arg) in func.args.iter().enumerate() {
513            arg_map_by_name.insert(arg.name, arg.clone());
514            arg_map_by_index.insert(i, arg.clone());
515        }
516
517        let mut connections = vec![];
518        match self {
519            Arguments::Positional(x) => {
520                for (i, (expr, dst, _)) in x.into_iter().enumerate() {
521                    if let Some(arg) = arg_map_by_index.get(&i) {
522                        connections.push((arg, expr, dst));
523                    }
524                }
525            }
526            Arguments::Named(x) => {
527                for (name, (expr, dst, _)) in x {
528                    if let Some(arg) = arg_map_by_name.get(&name) {
529                        connections.push((arg, expr, dst));
530                    }
531                }
532            }
533            Arguments::Mixed(_, _) => (),
534            Arguments::Null => (),
535        };
536
537        for (arg, mut expr, dst) in connections {
538            if arg.members.len() == 1
539                && !matches!(
540                    &arg.comptime.r#type.kind,
541                    crate::ir::TypeKind::Modport(_, _)
542                )
543            {
544                let (path, _, direction) = &arg.members[0];
545                match direction {
546                    Direction::Input => {
547                        let arg_type = &arg.comptime.r#type;
548                        // Argument expressions are initially converted without a
549                        // destination type. Apply the resolved formal shape here,
550                        // where the actual-to-formal binding is first available.
551                        if matches!(expr, Expression::ArrayLiteral(..)) {
552                            eval_array_literal(
553                                context,
554                                Some(&arg_type.array),
555                                Some(arg_type.width()),
556                                &mut expr,
557                            )?;
558                        }
559                        if arg_type.is_clock() || arg_type.is_reset() {
560                            let expr_comptime = expr.eval_comptime(context, None);
561                            let expr_token = expr_comptime.token;
562                            check_implicit_clock_conversion(
563                                context,
564                                arg_type,
565                                expr_comptime,
566                                &expr_token,
567                            );
568                        }
569                        inputs.push((path.clone(), expr));
570                    }
571                    Direction::Output => {
572                        let dst = dst
573                            .into_iter()
574                            .filter_map(|x| x.to_assign_destination(context, false))
575                            .collect();
576                        outputs.push((path.clone(), dst));
577                    }
578                    _ => (),
579                }
580            } else {
581                let expr_comptime = expr.eval_comptime(context, None);
582                let expr_token = expr_comptime.token;
583
584                check_compatibility(context, &arg.comptime.r#type, expr_comptime, &expr_token);
585
586                let actual = &dst[0];
587                for x in &arg.members {
588                    let arg_path = x.0.clone();
589                    let direction = x.2;
590                    let mut expr_path = actual.0.clone();
591                    expr_path.0.extend_from_slice(&arg_path.0[1..]);
592                    let actual_member = VarPathSelect(expr_path, actual.1.clone(), expr_token);
593
594                    match direction {
595                        Direction::Input => {
596                            let expr = actual_member.to_expression(context);
597                            if let Some(expr) = expr {
598                                inputs.push((arg_path, expr));
599                            }
600                        }
601                        Direction::Output => {
602                            let dst = actual_member.to_assign_destination(context, false);
603                            if let Some(dst) = dst {
604                                outputs.push((arg_path, vec![dst]));
605                            }
606                        }
607                        _ => (),
608                    }
609                }
610            }
611        }
612
613        Ok((CallArgs(inputs), CallArgs(outputs)))
614    }
615}