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 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 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 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 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 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 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#[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 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 }
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 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}