Skip to main content

veryl_analyzer/ir/
function.rs

1use crate::conv::Context;
2use crate::conv::utils::check_compatibility;
3use crate::ir::assign_table::{AssignContext, AssignTable};
4use crate::ir::{
5    AssignDestination, Comptime, Expression, FfTable, IrResult, Shape, Signature, Statement,
6    ValueVariant, VarId, VarIndex, VarPath, VarPathSelect, VarSelect,
7};
8use crate::symbol::{Direction, Symbol, SymbolId, SymbolKind};
9use crate::value::{Value, ValueBigUint};
10use crate::{AnalyzerError, HashMap, ir_error};
11use indent::indent_all_by;
12use std::fmt;
13use veryl_parser::resource_table::StrId;
14use veryl_parser::token_range::TokenRange;
15
16#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
17pub struct FuncPath {
18    pub path: VarPath,
19    pub sig: Signature,
20}
21
22impl FuncPath {
23    pub fn new(id: SymbolId) -> Self {
24        Self {
25            path: VarPath::default(),
26            sig: Signature::new(id),
27        }
28    }
29
30    pub fn add_prelude(&mut self, x: &[StrId]) {
31        self.path.add_prelude(x)
32    }
33
34    pub fn base(&self) -> FuncPath {
35        let mut ret = self.clone();
36        ret.sig.parameters.clear();
37        ret.sig.generic_parameters.clear();
38        ret
39    }
40}
41
42impl fmt::Display for FuncPath {
43    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44        let mut ret = String::new();
45
46        if !self.path.0.is_empty() {
47            ret.push_str(&format!("{}.", self.path));
48        }
49        ret.push_str(&format!("{}", self.sig));
50
51        ret.fmt(f)
52    }
53}
54
55#[derive(Clone)]
56pub struct FuncArg {
57    pub name: StrId,
58    pub comptime: Comptime,
59    pub members: Vec<(VarPath, Comptime, Direction)>,
60}
61
62#[derive(Clone)]
63pub struct Function {
64    pub name: StrId,
65    pub id: VarId,
66    pub path: FuncPath,
67    pub r#type: Comptime,
68    pub array: Shape,
69    pub arity: usize,
70    pub args: Vec<FuncArg>,
71    pub is_const: bool,
72    pub functions: Vec<FunctionBody>,
73    pub token: TokenRange,
74}
75
76impl Function {
77    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
78        for x in &self.functions {
79            x.eval_assign(context, assign_table);
80        }
81    }
82
83    pub fn set_index(&mut self, index: &VarIndex) {
84        for x in &mut self.functions {
85            x.set_index(index);
86        }
87    }
88
89    pub fn get_function(&self, index: &[usize]) -> Option<FunctionBody> {
90        let index = self.array.calc_index(index)?;
91        self.functions.get(index).cloned()
92    }
93
94    pub fn to_proto(&self) -> FuncProto {
95        FuncProto {
96            name: self.name,
97            id: self.id,
98            r#type: self.r#type.clone(),
99            arity: self.arity,
100            args: self.args.clone(),
101            token: self.token,
102        }
103    }
104}
105
106#[derive(Clone)]
107pub struct FuncProto {
108    pub name: StrId,
109    pub id: VarId,
110    pub r#type: Comptime,
111    pub arity: usize,
112    pub args: Vec<FuncArg>,
113    pub token: TokenRange,
114}
115
116#[derive(Clone)]
117pub struct FunctionBody {
118    pub ret: Option<VarId>,
119    pub arg_map: HashMap<VarPath, VarId>,
120    pub statements: Vec<Statement>,
121}
122
123impl FunctionBody {
124    pub fn eval_assign(&self, context: &mut Context, assign_table: &mut AssignTable) {
125        for x in &self.statements {
126            x.eval_assign(context, assign_table, AssignContext::Function, &[]);
127        }
128    }
129
130    pub fn set_index(&mut self, index: &VarIndex) {
131        for x in &mut self.statements {
132            x.set_index(index);
133        }
134    }
135}
136
137impl fmt::Display for Function {
138    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139        let mut ret = String::new();
140
141        for (i, f) in self.functions.iter().enumerate() {
142            if self.functions.len() == 1 {
143                ret.push_str(&format!("func {}({})", self.id, self.path));
144            } else {
145                ret.push_str(&format!("func {}[{}]({})", self.id, i, self.path));
146            }
147
148            if let Some(x) = f.ret {
149                ret.push_str(&format!(" -> {x}"));
150            }
151            ret.push_str(" {\n");
152
153            for s in &f.statements {
154                let text = format!("{}\n", s);
155                ret.push_str(&indent_all_by(2, text));
156            }
157
158            ret.push_str("}\n");
159        }
160
161        ret.trim_end().fmt(f)
162    }
163}
164
165#[derive(Clone, Debug)]
166pub struct FunctionCall {
167    pub id: VarId,
168    pub index: Option<Vec<usize>>,
169    pub comptime: Comptime,
170    pub inputs: HashMap<VarPath, Expression>,
171    pub outputs: HashMap<VarPath, Vec<AssignDestination>>,
172}
173
174impl FunctionCall {
175    pub fn eval_type(&mut self, context: &mut Context) {
176        self.comptime.is_const = self.eval_comptime_flag(context);
177    }
178
179    pub fn eval_value(&self, context: &mut Context) -> Option<Value> {
180        let func = context.functions.get(&self.id)?;
181        let func = if let Some(x) = &self.index {
182            func.get_function(x)
183        } else {
184            func.get_function(&[])
185        }?;
186
187        // set inputs
188        for (path, expr) in &self.inputs {
189            let id = func.arg_map.get(path)?;
190            let value = expr.eval_value(context)?;
191            let var = context.variables.get_mut(id)?;
192            var.set_value(&[], value, None);
193        }
194
195        let disable_const_opt = context.disalbe_const_opt;
196        context.disalbe_const_opt = true;
197        for x in &func.statements {
198            x.eval_value(context);
199        }
200        context.disalbe_const_opt = disable_const_opt;
201
202        // TODO get outputs
203
204        if let Some(x) = &func.ret {
205            let variable = context.variables.get(x)?;
206            variable.get_value(&[]).cloned()
207        } else {
208            None
209        }
210    }
211
212    pub fn eval_comptime(&mut self, context: &mut Context) -> Comptime {
213        let value = self.eval_value(context);
214        let value = if let Some(x) = value {
215            ValueVariant::Numeric(x)
216        } else {
217            ValueVariant::Unknown
218        };
219
220        let mut ret = self.comptime.clone();
221        ret.value = value;
222
223        ret.is_const = self.eval_comptime_flag(context);
224        ret
225    }
226
227    pub fn eval_assign(
228        &self,
229        context: &mut Context,
230        assign_table: &mut AssignTable,
231        assign_context: AssignContext,
232    ) {
233        for output in self.outputs.values() {
234            for dst in output {
235                if let Some(index) = dst.index.eval_value(context) {
236                    let variable = context.get_variable_info(dst.id).unwrap();
237                    if let Some((beg, end)) =
238                        dst.select.eval_value(context, &variable.r#type, false)
239                    {
240                        let mask = ValueBigUint::gen_mask_range(beg, end);
241                        let (success, tokens) = assign_table.insert_assign(
242                            &variable,
243                            index,
244                            mask,
245                            false,
246                            self.comptime.token,
247                        );
248                        if !success & assign_context.is_ff() {
249                            context.insert_error(AnalyzerError::multiple_assignment(
250                                &variable.path.to_string(),
251                                &self.comptime.token,
252                                &tokens,
253                            ));
254                        }
255                    }
256                }
257            }
258        }
259    }
260
261    pub fn gather_ff(
262        &self,
263        context: &mut Context,
264        table: &mut FfTable,
265        decl: usize,
266        assign_target: Option<(VarId, Option<usize>)>,
267        from_ff: bool,
268    ) {
269        for input in self.inputs.values() {
270            input.gather_ff(context, table, decl, assign_target, from_ff);
271        }
272        for dsts in self.outputs.values() {
273            for dst in dsts {
274                dst.gather_ff(context, table, decl);
275            }
276        }
277    }
278
279    pub fn gather_ff_comb_assign(&self, context: &mut Context, table: &mut FfTable, decl: usize) {
280        for dsts in self.outputs.values() {
281            for dst in dsts {
282                dst.gather_ff_comb_assign(context, table, decl);
283            }
284        }
285    }
286
287    pub fn set_index(&mut self, index: &VarIndex) {
288        for x in self.inputs.values_mut() {
289            x.set_index(index);
290        }
291        for x in self.outputs.values_mut() {
292            for x in x {
293                x.set_index(index);
294            }
295        }
296    }
297
298    fn eval_comptime_flag(&mut self, context: &mut Context) -> bool {
299        let mut is_const = context
300            .functions
301            .get(&self.id)
302            .map(|func| func.is_const)
303            .unwrap_or(true);
304        for expr in self.inputs.values_mut() {
305            is_const &= expr.eval_comptime(context, None).is_const;
306        }
307
308        // function with side-effect through output ports is not const
309        if !self.outputs.is_empty() {
310            is_const = false;
311        }
312
313        is_const
314    }
315}
316
317impl fmt::Display for FunctionCall {
318    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
319        let mut args = String::new();
320
321        let mut inputs: Vec<_> = self.inputs.iter().collect();
322        let mut outputs: Vec<_> = self.outputs.iter().collect();
323        inputs.sort_by_key(|x| x.0);
324        outputs.sort_by_key(|x| x.0);
325
326        for (id, val) in &inputs {
327            args.push_str(&format!("{id}: {val}, "));
328        }
329        for (id, val) in &outputs {
330            if val.len() == 1 {
331                args.push_str(&format!("{id}: {}, ", val[0]));
332            } else {
333                args.push_str(&format!("{id}: {{{}", val[0]));
334                for x in &val[1..] {
335                    args.push_str(&format!(", {x}"));
336                }
337                args.push_str("}}, ");
338            }
339        }
340        let args = if args.is_empty() {
341            &args
342        } else {
343            &args[0..args.len() - 2]
344        };
345
346        let mut index = String::new();
347        if let Some(x) = &self.index {
348            for x in x {
349                index.push_str(&format!("[{}]", x));
350            }
351        }
352
353        format!("{}{}({})", self.id, index, args).fmt(f)
354    }
355}
356
357pub type PositionalArgs = Vec<(Expression, Vec<VarPathSelect>, TokenRange)>;
358pub type NamedArgs = Vec<(StrId, (Expression, Vec<VarPathSelect>, TokenRange))>;
359pub type FunctionArgs = (
360    HashMap<VarPath, Expression>,
361    HashMap<VarPath, Vec<AssignDestination>>,
362);
363
364#[derive(Clone)]
365pub enum Arguments {
366    Positional(PositionalArgs),
367    Named(NamedArgs),
368    Mixed(PositionalArgs, NamedArgs),
369    Null,
370}
371
372impl Arguments {
373    pub fn is_empty(&self) -> bool {
374        self.len() == 0
375    }
376
377    pub fn len(&self) -> usize {
378        match self {
379            Arguments::Positional(x) => x.len(),
380            Arguments::Named(x) => x.len(),
381            Arguments::Mixed(x, y) => x.len() + y.len(),
382            Arguments::Null => 0,
383        }
384    }
385
386    pub fn to_system_function_args(
387        self,
388        _context: &mut Context,
389        symbol: &Symbol,
390    ) -> Vec<(Expression, Vec<VarPathSelect>, TokenRange)> {
391        let ret = match self {
392            Arguments::Positional(x) => x,
393            Arguments::Named(_) => {
394                // TODO error
395                return vec![];
396            }
397            Arguments::Mixed(_, _) => vec![],
398            Arguments::Null => vec![],
399        };
400
401        if let SymbolKind::SystemFunction(x) = &symbol.kind {
402            let arity = x.ports.len();
403
404            if arity != ret.len() {
405                // TODO
406                //let name = symbol.token.text.to_string();
407                //context.insert_error(AnalyzerError::mismatch_function_arity(
408                //    &name,
409                //    arity,
410                //    ret.len(),
411                //    &symbol.token.into(),
412                //))
413            }
414        }
415
416        ret
417    }
418
419    pub fn to_function_args(
420        self,
421        context: &mut Context,
422        func: &FuncProto,
423        token: TokenRange,
424    ) -> IrResult<FunctionArgs> {
425        let mut inputs = HashMap::default();
426        let mut outputs = HashMap::default();
427
428        if func.arity != self.len() {
429            context.insert_error(AnalyzerError::mismatch_function_arity(
430                &func.name.to_string(),
431                func.arity,
432                self.len(),
433                &token,
434            ));
435            return Err(ir_error!(func.token));
436        }
437
438        let mut arg_map_by_name = HashMap::default();
439        let mut arg_map_by_index = HashMap::default();
440        for (i, arg) in func.args.iter().enumerate() {
441            arg_map_by_name.insert(arg.name, arg.clone());
442            arg_map_by_index.insert(i, arg.clone());
443        }
444
445        let mut connections = vec![];
446        match self {
447            Arguments::Positional(x) => {
448                for (i, (expr, dst, _)) in x.into_iter().enumerate() {
449                    if let Some(arg) = arg_map_by_index.get(&i) {
450                        connections.push((arg, expr, dst));
451                    }
452                }
453            }
454            Arguments::Named(x) => {
455                for (name, (expr, dst, _)) in x {
456                    if let Some(arg) = arg_map_by_name.get(&name) {
457                        connections.push((arg, expr, dst));
458                    }
459                }
460            }
461            Arguments::Mixed(_, _) => (),
462            Arguments::Null => (),
463        };
464
465        for (arg, mut expr, dst) in connections {
466            if arg.members.len() == 1 {
467                let (path, _, direction) = &arg.members[0];
468                match direction {
469                    Direction::Input => {
470                        inputs.insert(path.clone(), expr);
471                    }
472                    Direction::Output => {
473                        let dst = dst
474                            .into_iter()
475                            .filter_map(|x| x.to_assign_destination(context, false))
476                            .collect();
477                        outputs.insert(path.clone(), dst);
478                    }
479                    _ => (),
480                }
481            } else {
482                let expr_comptime = expr.eval_comptime(context, None);
483                let expr_token = expr_comptime.token;
484                let expr_members = expr_comptime
485                    .r#type
486                    .expand_interface(context, &dst[0].0, expr_token)?;
487
488                check_compatibility(context, &arg.comptime.r#type, expr_comptime, &expr_token);
489
490                for (x, y) in arg.members.iter().zip(expr_members.iter()) {
491                    let arg_path = x.0.clone();
492                    let direction = x.2;
493                    let expr_path = y.0.clone();
494
495                    match direction {
496                        Direction::Input => {
497                            let expr = VarPathSelect(expr_path, VarSelect::default(), expr_token);
498                            let expr = expr.to_expression(context);
499                            if let Some(expr) = expr {
500                                inputs.insert(arg_path, expr);
501                            }
502                        }
503                        Direction::Output => {
504                            let dst = VarPathSelect(expr_path, VarSelect::default(), expr_token);
505                            let dst = dst.to_assign_destination(context, false);
506                            if let Some(dst) = dst {
507                                outputs.insert(arg_path, vec![dst]);
508                            }
509                        }
510                        _ => (),
511                    }
512                }
513            }
514        }
515
516        Ok((inputs, outputs))
517    }
518}