1use crate::ast::data::Constant;
2use crate::ast::linkage::Linkage;
3use crate::ast::types::{AbiType, BaseType};
4use crate::ast::{BlockName, FloatLiteral, GlobalName, Ident, Span, Spanned, TemporaryName};
5use crate::lexer::Keyword;
6use crate::print::{IndentedPrinter, impl_display_via_print};
7use crate::utils::{IterExt, delegate_enum_getters, impl_enum_display};
8use std::fmt;
9use std::fmt::{Display, Formatter, Write};
10use std::str::FromStr;
11
12mod parse;
13#[cfg(test)]
14mod test;
15
16#[derive(Clone, Debug, Eq, PartialEq, Hash)]
17pub struct FunctionDef {
18 pub span: Span,
19 pub linkage: Linkage,
20 pub return_type: Option<AbiType>,
21 pub name: GlobalName,
22 pub params: Vec<ParamDef>,
23 pub body: FunctionBody,
24}
25impl FunctionDef {
26 pub(crate) fn span(&self) -> Span {
28 self.span
29 }
30 pub fn validate(&self) -> Result<(), Vec<InvalidFunctionReason>> {
31 let mut res = Vec::new();
32 for (index, param) in self.params.iter().enumerate() {
33 match param {
34 ParamDef::Regular(_) => {}
35 ParamDef::Environment(_) => {
36 if index > 0 {
37 res.push(InvalidFunctionReason::EnvironmentParamMustComeFirst {
38 span: param.span(),
39 });
40 }
41 }
42 ParamDef::Variadic(_) => {
43 if index < self.params.len() - 1 {
44 res.push(InvalidFunctionReason::VariadicParamMustComeLast {
45 span: param.span(),
46 });
47 }
48 }
49 }
50 }
51 if res.is_empty() { Ok(()) } else { Err(res) }
52 }
53 fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
54 if !self.linkage.is_empty() {
55 write!(out, "{} ", self.linkage)?;
56 }
57 out.write_str("function ")?;
58 if let Some(ref return_type) = self.return_type {
59 write!(out, "{return_type} ")?;
60 }
61 write!(out, "{}({}) ", self.name, self.params.iter().format(", "))?;
62 self.body.print(out)?;
63 Ok(())
64 }
65}
66impl_display_via_print!(FunctionDef);
67
68#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
70#[non_exhaustive]
71pub enum InvalidFunctionReason {
72 #[error("Variadic parameter must come last")]
73 VariadicParamMustComeLast { span: Span },
74 #[error("Environment parameter must come first")]
75 EnvironmentParamMustComeFirst { span: Span },
76}
77
78#[derive(Clone, Debug, Eq, PartialEq, Hash)]
79pub enum ParamDef {
80 Regular(RegularParamDef),
81 Environment(EnvironmentParamDef),
82 Variadic(VariadicParamDef),
83}
84impl ParamDef {
85 pub fn span(&self) -> Span {
86 match self {
87 ParamDef::Regular(param) => param.span,
88 ParamDef::Environment(param) => param.span,
89 ParamDef::Variadic(param) => param.span,
90 }
91 }
92 pub fn name(&self) -> Result<&'_ TemporaryName, UnnamedParamError> {
93 Ok(match self {
94 ParamDef::Regular(param) => ¶m.name,
95 ParamDef::Environment(param) => ¶m.name,
96 ParamDef::Variadic(param) => {
97 return Err(UnnamedParamError::Variadic { span: param.span });
98 }
99 })
100 }
101}
102impl Display for ParamDef {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 match self {
105 ParamDef::Regular(param) => write!(f, "{param}"),
106 ParamDef::Environment(param) => write!(f, "{param}"),
107 ParamDef::Variadic(param) => write!(f, "{param}"),
108 }
109 }
110}
111#[derive(thiserror::Error, Debug, Eq, PartialEq)]
112#[non_exhaustive]
113pub enum UnnamedParamError {
114 #[error("Variadic parameter has no name")]
115 Variadic { span: Span },
116}
117impl UnnamedParamError {
118 pub fn span(&self) -> Span {
119 match self {
120 UnnamedParamError::Variadic { span } => *span,
121 }
122 }
123}
124
125#[derive(Clone, Debug, Eq, PartialEq, Hash)]
126pub struct RegularParamDef {
127 pub span: Span,
128 pub name: TemporaryName,
129 pub ty: AbiType,
130}
131impl Display for RegularParamDef {
132 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133 write!(f, "{} {}", self.ty, self.name)
134 }
135}
136#[derive(Clone, Debug, Eq, PartialEq, Hash)]
137pub struct EnvironmentParamDef {
138 pub span: Span,
139 pub name: TemporaryName,
140}
141impl Display for EnvironmentParamDef {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 write!(f, "env {}", self.name)
144 }
145}
146
147#[derive(Clone, Debug, Eq, PartialEq, Hash)]
148pub struct VariadicParamDef {
149 pub span: Span,
150}
151impl Display for VariadicParamDef {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 f.write_str("...")
154 }
155}
156
157#[derive(Clone, Debug, Eq, PartialEq, Hash)]
158pub struct FunctionBody {
159 pub span: Span,
160 pub blocks: Vec<FunctionBlock>,
161}
162impl FunctionBody {
163 fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
164 out.write_str("{\n")?;
165 for block in &self.blocks {
166 block.print(out)?;
167 }
168 out.maybe_writeln()?;
169 out.write_char('}')
170 }
171}
172impl_display_via_print!(FunctionBody);
173#[derive(Clone, Debug, Eq, PartialEq, Hash)]
174pub struct FunctionBlock {
175 pub span: Span,
176 pub label: BlockName,
177 pub phis: Vec<PhiInstruction>,
178 pub instructions: Vec<RegularInstruction>,
179 pub terminator: Option<JumpInstruction>,
180}
181impl FunctionBlock {
182 fn print(&self, out: &mut IndentedPrinter<'_>) -> fmt::Result {
183 writeln!(out, "{}", self.label)?;
184 out.indented(|out| {
185 for phi in &self.phis {
186 writeln!(out, "{phi}")?;
187 }
188 for insn in &self.instructions {
189 writeln!(out, "{insn}")?;
190 }
191 if let Some(ref term) = self.terminator {
192 writeln!(out, "{term}")?;
193 }
194 Ok(())
195 })
196 }
197}
198impl_display_via_print!(FunctionBlock);
199#[derive(Clone, Debug, Eq, PartialEq, Hash)]
200pub struct PhiInstruction {
201 pub span: Span,
202 pub dest_info: InsnDestInfo,
203 pub args: Vec<PhiArg>,
204}
205impl PhiInstruction {
206 #[inline]
207 pub fn dest(&self) -> &'_ TemporaryName {
208 &self.dest_info.dest
209 }
210 #[inline]
211 pub fn dest_type(&self) -> &'_ BaseType {
212 &self.dest_info.ty
213 }
214}
215impl Display for PhiInstruction {
216 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
217 write!(f, "{} phi ", self.dest_info)?;
218 write!(f, "{}", self.args.iter().format(", "))?;
219 Ok(())
220 }
221}
222#[derive(Clone, Debug, Eq, PartialEq, Hash)]
223pub struct PhiArg {
224 pub span: Span,
225 pub block: BlockName,
226 pub value: Value,
227}
228impl Display for PhiArg {
229 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
230 write!(f, "{} {}", self.block, self.value)
231 }
232}
233
234#[derive(Clone, Debug, Eq, PartialEq, Hash)]
236pub struct InsnDestInfo {
237 pub span: Span,
238 pub dest: TemporaryName,
239 pub ty: BaseType,
240}
241impl Display for InsnDestInfo {
242 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
243 write!(f, "{} ={}", self.dest, self.ty)
244 }
245}
246#[derive(Clone, Debug, Eq, PartialEq, Hash)]
248#[non_exhaustive]
249pub enum RegularInstruction {
250 Simple(SimpleInstruction),
251 Call(CallInstruction),
252}
253delegate_enum_getters! {
254 enum RegularInstruction {
255 Simple,
256 Call
257 } get {
258 pub fn dest_info(&self) -> Option<&'_ InsnDestInfo>;
259 pub fn dest(&self) -> Option<&'_ TemporaryName>;
260 pub fn dest_type(&self) -> Option<&'_ BaseType>;
261 pub fn name(&self) -> Ident;
262 pub fn span(&self) -> Span;
263 }
264}
265impl From<SimpleInstruction> for RegularInstruction {
266 fn from(value: SimpleInstruction) -> Self {
267 RegularInstruction::Simple(value)
268 }
269}
270impl From<CallInstruction> for RegularInstruction {
271 fn from(value: CallInstruction) -> Self {
272 RegularInstruction::Call(value)
273 }
274}
275impl_enum_display!(
276 enum RegularInstruction {
277 Simple,
278 Call,
279 }
280);
281#[derive(Clone, Debug, Eq, PartialEq, Hash)]
282pub struct SimpleInstruction {
283 pub span: Span,
284 pub dest_info: Option<InsnDestInfo>,
285 pub args: Vec<Value>,
286 pub name: Ident,
287}
288impl SimpleInstruction {
289 fn name(&self) -> Ident {
290 self.name.clone()
291 }
292}
293macro_rules! regular_insn_common {
294 ($target:ident) => {
295 impl $target {
296 fn dest_info(&self) -> Option<&'_ InsnDestInfo> {
298 self.dest_info.as_ref()
299 }
300 #[inline]
301 fn span(&self) -> Span {
302 self.span
303 }
304 pub fn dest(&self) -> Option<&'_ TemporaryName> {
305 self.dest_info.as_ref().map(|info| &info.dest)
306 }
307 pub fn dest_type(&self) -> Option<&'_ BaseType> {
308 self.dest_info.as_ref().map(|info| &info.ty)
309 }
310 }
311 };
312}
313regular_insn_common!(SimpleInstruction);
314impl Display for SimpleInstruction {
315 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
316 if let Some(ref info) = self.dest_info {
317 write!(f, "{info} ")?;
318 }
319 write!(f, "{}", self.name)?;
320 if !self.args.is_empty() {
321 f.write_char(' ')?;
322 }
323 write!(f, "{}", self.args.iter().format(", "))?;
324 Ok(())
325 }
326}
327#[derive(Clone, Debug, Eq, PartialEq, Hash)]
328pub struct CallInstruction {
329 pub span: Span,
330 pub call_kw_span: Span,
332 pub dest_info: Option<InsnDestInfo>,
333 pub target: Value,
334 pub args: Vec<CallArgument>,
335}
336impl Display for CallInstruction {
337 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
338 if let Some(ref info) = self.dest_info {
339 write!(f, "{info} ")?;
340 }
341 write!(f, "call {}({})", self.target, self.args.iter().format(", "))
342 }
343}
344impl CallInstruction {
345 pub fn name(&self) -> Ident {
346 Spanned {
347 span: self.call_kw_span,
348 value: Keyword::Call,
349 }
350 .into()
351 }
352}
353regular_insn_common!(CallInstruction);
354
355#[derive(Clone, Debug, Eq, PartialEq, Hash)]
357pub enum CallArgument {
358 Regular(RegularCallArgument),
359 Environment(Value),
360 VariadicMarker(Span),
361}
362impl Display for CallArgument {
363 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
364 match self {
365 CallArgument::Regular(inner) => Display::fmt(inner, f),
366 CallArgument::Environment(value) => write!(f, "env {value}"),
367 CallArgument::VariadicMarker(_) => f.write_str("..."),
368 }
369 }
370}
371#[derive(Clone, Debug, Eq, PartialEq, Hash)]
373pub struct RegularCallArgument {
374 pub span: Span,
375 pub ty: AbiType,
376 pub value: Value,
377}
378impl Display for RegularCallArgument {
379 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
380 write!(f, "{} {}", self.ty, self.value)
381 }
382}
383
384#[derive(Clone, Debug, Eq, PartialEq, Hash)]
385pub struct ThreadLocalRef {
386 pub span: Span,
387 pub name: GlobalName,
388}
389impl Display for ThreadLocalRef {
390 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
391 write!(f, "thread {}", self.name)
392 }
393}
394
395#[derive(Clone, Debug, Eq, PartialEq, Hash)]
396#[non_exhaustive]
397pub enum Value {
398 Constant(Constant),
399 ThreadLocalRef(ThreadLocalRef),
400 Temporary(TemporaryName),
401}
402impl_enum_display!(
403 enum Value {
404 Constant,
405 ThreadLocalRef,
406 Temporary,
407 }
408);
409macro_rules! impl_from_constant {
410 ($($target:ty),+) => {
411 $(impl From<$target> for Value {
412 fn from(value: $target) -> Self {
413 Value::Constant(value.into())
414 }
415 })*
416 };
417}
418impl_from_constant!(Constant, i128, i64, i32, u64, FloatLiteral);
419impl From<TemporaryName> for Value {
420 fn from(name: TemporaryName) -> Self {
421 Value::Temporary(name)
422 }
423}
424
425macro_rules! insn_kind_names {
426 ($target:ident {
427 const KIND_DESC = $kind_desc:literal;
428 $($variant:ident => $name:literal),+ $(,)?
429 }) => {
430 impl $target {
431 pub fn name(&self) -> &'static str {
432 match self {
433 $(Self::$variant => $name,)*
434 }
435 }
436 pub fn from_name(name: &str) -> Option<Self> {
437 match name {
438 $($name => Some(Self::$variant),)*
439 _ => None,
440 }
441 }
442 }
443 impl FromStr for $target {
444 type Err = UnknownInstructionNameError;
445 fn from_str(s: &str) -> Result<Self, Self::Err> {
446 Self::from_name(s).ok_or_else(|| UnknownInstructionNameError {
447 kind_desc: Some($kind_desc),
448 name: s.into()
449 })
450 }
451 }
452 };
453}
454#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
455pub enum JumpInstructionKind {
456 Jump,
457 JumpNonZero,
458 Return,
459 Halt,
460}
461insn_kind_names!(JumpInstructionKind {
462 const KIND_DESC = "jump";
463 Jump => "jmp",
464 JumpNonZero => "jnz",
465 Return => "ret",
466 Halt => "hlt",
467});
468#[derive(Clone, Debug, Eq, PartialEq, Hash)]
469pub enum JumpInstruction {
470 Jump {
471 span: Span,
472 target: BlockName,
473 },
474 JumpNonZero {
475 span: Span,
476 op: Value,
477 target: BlockName,
478 fallthrough: BlockName,
479 },
480 Return {
481 span: Span,
482 value: Option<Value>,
483 },
484 Halt {
485 span: Span,
486 },
487}
488impl JumpInstruction {
489 #[inline]
490 pub fn dest_info(&self) -> Option<&InsnDestInfo> {
491 None
492 }
493 pub fn span(&self) -> Span {
494 match *self {
495 JumpInstruction::Jump { span, .. }
496 | JumpInstruction::JumpNonZero { span, .. }
497 | JumpInstruction::Return { span, .. }
498 | JumpInstruction::Halt { span, .. } => span,
499 }
500 }
501 pub fn kind(self) -> JumpInstructionKind {
502 match self {
503 JumpInstruction::Jump { .. } => JumpInstructionKind::Jump,
504 JumpInstruction::JumpNonZero { .. } => JumpInstructionKind::JumpNonZero,
505 JumpInstruction::Return { .. } => JumpInstructionKind::Return,
506 JumpInstruction::Halt { .. } => JumpInstructionKind::Halt,
507 }
508 }
509}
510impl Display for JumpInstruction {
511 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
512 match self {
513 JumpInstruction::Jump { span: _, target } => {
514 write!(f, "jmp {target}")
515 }
516 JumpInstruction::JumpNonZero {
517 span: _,
518 op,
519 fallthrough,
520 target,
521 } => {
522 write!(f, "jnz {op}, {target}, {fallthrough}")
523 }
524 JumpInstruction::Return { span: _, value } => {
525 f.write_str("ret")?;
526 if let Some(value) = value {
527 write!(f, " {value}")?;
528 }
529 Ok(())
530 }
531 JumpInstruction::Halt { span: _ } => f.write_str("hlt"),
532 }
533 }
534}
535
536#[derive(thiserror::Error, Debug, Clone, Eq, PartialEq)]
537pub struct UnknownInstructionNameError {
538 kind_desc: Option<&'static str>,
539 name: String,
540}
541impl UnknownInstructionNameError {
542 pub fn name(&self) -> &'_ str {
543 &self.name
544 }
545}
546impl Display for UnknownInstructionNameError {
547 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
548 f.write_str("Unknown")?;
549 if let Some(kind) = self.kind_desc {
550 write!(f, " {kind}")?;
551 }
552 write!(f, " instruction name: {:?}", self.name)
553 }
554}