Skip to main content

ptx_parser/parser/
function.rs

1use ptx_syntax_proc_macros::func;
2
3use crate::{
4    alt, c,
5    lexer::PtxToken,
6    mapc, ok,
7    parser::{
8        ParseErrorKind, PtxParseError, PtxParser, PtxTokenStream, Span,
9        util::{
10            alt, between, colon_p, comma_p, directive_exact_p, directive_p, identifier_p,
11            integer_p, langle_p, lbrace_p, lparen_p, many, map, minus_p, optional,
12            parse_signed_integer, parse_u32_literal, parse_unsigned_integer, plus_p, rangle_p,
13            rbrace_p, register_p, rparen_p, semicolon_p, sep_by, sep_by1, seq, seq5, skip_first,
14            skip_second, skip_semicolon, string_literal_p, string_p, try_map, u32_p,
15        },
16    },
17    seq_n,
18    r#type::{
19        AliasFunctionDirective, AttributeDirective, BranchTargetsDirective, CallPrototypeDirective,
20        CallTargetsDirective, DataType, DwarfDirective, DwarfDirectiveKind, EntryFunctionDirective,
21        EntryFunctionHeaderDirective, FuncFunctionDirective, FuncFunctionHeaderDirective,
22        FunctionBody, FunctionDim, FunctionStatement, FunctionSymbol, Instruction, Label,
23        LocationDirective, LocationFunctionInfo, LocationInlinedAt, ParameterDirective,
24        PragmaDirective, PragmaDirectiveKind, RegisterDirective, RegisterTarget,
25        RegisterVectorWidth, SectionDirective, SectionEntry, StatementDirective,
26        StatementSectionDirectiveLine, VariableDirective, VariableSymbol,
27    },
28};
29
30impl PtxParser for StatementDirective {
31    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
32        let branch_targets = try_map(
33            skip_semicolon(skip_first(
34                directive_exact_p("branchtargets"),
35                sep_by1(Label::parse(), comma_p()),
36            )),
37            |labels, span| {
38                let directive = BranchTargetsDirective {
39                    labels,
40                    span: span.clone(),
41                };
42                ok!(StatementDirective::BranchTargets { directive })
43            },
44        );
45
46        let call_targets = try_map(
47            skip_semicolon(skip_first(
48                directive_exact_p("calltargets"),
49                sep_by1(FunctionSymbol::parse(), comma_p()),
50            )),
51            |targets, span| {
52                let directive = CallTargetsDirective {
53                    targets,
54                    span: span.clone(),
55                };
56                ok!(StatementDirective::CallTargets { directive })
57            },
58        );
59
60        let call_prototype = try_map(
61            skip_semicolon(skip_first(
62                directive_exact_p("callprototype"),
63                seq5(
64                    return_spec_parser(),
65                    parameter_list_parser(),
66                    noreturn_parser(),
67                    abi_preserve_parser(),
68                    abi_preserve_control_parser(),
69                ),
70            )),
71            |(return_param, params, noreturn, abi_preserve, abi_preserve_control), span| {
72                let directive = CallPrototypeDirective {
73                    return_param,
74                    params,
75                    noreturn,
76                    abi_preserve,
77                    abi_preserve_control,
78                    span: span.clone(),
79                };
80                ok!(StatementDirective::CallPrototype { directive })
81            },
82        );
83
84        let location = mapc!(location_directive(), StatementDirective::Loc { directive });
85
86        let reg_stmt = mapc!(register_statement(), StatementDirective::Reg { directive });
87
88        let local_stmt = mapc!(
89            skip_first(directive_exact_p("local"), VariableDirective::parse()),
90            StatementDirective::Local { directive }
91        );
92
93        let param_stmt = mapc!(
94            skip_first(directive_exact_p("param"), VariableDirective::parse()),
95            StatementDirective::Param { directive }
96        );
97
98        let shared_stmt = mapc!(
99            skip_first(directive_exact_p("shared"), VariableDirective::parse()),
100            StatementDirective::Shared { directive }
101        );
102
103        alt!(
104            location,
105            reg_stmt,
106            local_stmt,
107            param_stmt,
108            shared_stmt,
109            branch_targets,
110            call_targets,
111            call_prototype,
112            dwarf_directive(),
113            pragma_directive(),
114            section_directive()
115        )
116    }
117}
118
119impl PtxParser for SectionDirective {
120    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
121        map(
122            skip_first(
123                directive_exact_p("section"),
124                seq(section_name_parser(), section_body_parser()),
125            ),
126            |(name, entries), span| {
127                c!(SectionDirective {
128                    name = name,
129                    entries,
130                })
131            },
132        )
133    }
134}
135
136impl PtxParser for DwarfDirective {
137    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
138        skip_first(directive_exact_p("dwarf"), dwarf_kind_parser())
139    }
140}
141
142impl PtxParser for FunctionStatement {
143    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
144        let label_stmt = map(seq(Label::parse(), colon_p()), |(label, _), span| {
145            c!(FunctionStatement::Label { label })
146        });
147        let block_stmt = map(statement_block_parser(), |statements, span| {
148            c!(FunctionStatement::Block { statements })
149        });
150        let directive_stmt = mapc!(
151            StatementDirective::parse(),
152            FunctionStatement::Directive { directive }
153        );
154        let instruction_stmt = mapc!(
155            Instruction::parse(),
156            FunctionStatement::Instruction { instruction }
157        );
158
159        // Design note: `alt` retains the error that reached furthest, so a
160        // malformed nested block does not require separate dispatch logic.
161        alt!(label_stmt, block_stmt, directive_stmt, instruction_stmt)
162    }
163}
164
165fn return_spec_parser()
166-> impl Fn(&mut PtxTokenStream) -> Result<(Option<ParameterDirective>, Span), PtxParseError> {
167    alt(
168        map(ParameterDirective::parse(), |param, _| Some(param)),
169        map(underscore_placeholder(), |_, _| None),
170    )
171}
172
173fn underscore_placeholder() -> impl Fn(&mut PtxTokenStream) -> Result<((), Span), PtxParseError> {
174    try_map(identifier_p(), |name, span| {
175        if name == "_" {
176            Ok(())
177        } else {
178            Err(PtxParseError {
179                kind: ParseErrorKind::UnexpectedToken {
180                    expected: vec!["identifier `_`".into()],
181                    found: name,
182                },
183                span,
184            })
185        }
186    })
187}
188
189fn parameter_list_parser()
190-> impl Fn(&mut PtxTokenStream) -> Result<(Vec<ParameterDirective>, Span), PtxParseError> {
191    map(
192        between(
193            lparen_p(),
194            rparen_p(),
195            sep_by(ParameterDirective::parse(), comma_p()),
196        ),
197        |params, _| params,
198    )
199}
200
201fn noreturn_parser() -> impl Fn(&mut PtxTokenStream) -> Result<(bool, Span), PtxParseError> {
202    map(optional(directive_exact_p("noreturn")), |flag, _| {
203        flag.is_some()
204    })
205}
206
207fn abi_preserve_parser()
208-> impl Fn(&mut PtxTokenStream) -> Result<(Option<u32>, Span), PtxParseError> {
209    map(
210        optional(skip_first(directive_exact_p("abi_preserve"), u32_p())),
211        |value, _span| value,
212    )
213}
214
215fn abi_preserve_control_parser()
216-> impl Fn(&mut PtxTokenStream) -> Result<(Option<u32>, Span), PtxParseError> {
217    map(
218        optional(skip_first(
219            directive_exact_p("abi_preserve_control"),
220            u32_p(),
221        )),
222        |value, _span| value,
223    )
224}
225fn dwarf_directive()
226-> impl Fn(&mut PtxTokenStream) -> Result<(StatementDirective, Span), PtxParseError> {
227    mapc!(
228        skip_semicolon(DwarfDirective::parse()),
229        StatementDirective::Dwarf { directive }
230    )
231}
232
233fn dwarf_kind_parser()
234-> impl Fn(&mut PtxTokenStream) -> Result<(DwarfDirective, Span), PtxParseError> {
235    let byte_values = try_map(
236        seq(
237            directive_exact_p("byte"),
238            sep_by1(unsigned_integer_literal(), comma_p()),
239        ),
240        |(_, values), span| {
241            let mut parsed = Vec::new();
242            for (text, value_span) in values {
243                let value = parse_unsigned_integer(&text, value_span, 0, u8::MAX as u128)?;
244                parsed.push(value as u8);
245            }
246            ok!(DwarfDirective {
247                kind = DwarfDirectiveKind::ByteValues(parsed)
248            })
249        },
250    );
251
252    let four_byte_values = try_map(
253        seq(
254            four_byte_keyword(),
255            sep_by1(unsigned_integer_literal(), comma_p()),
256        ),
257        |(_, values), span| {
258            let mut parsed = Vec::new();
259            for (text, value_span) in values {
260                let value = parse_unsigned_integer(&text, value_span, 0, u32::MAX as u128)?;
261                parsed.push(value as u32);
262            }
263            ok!(DwarfDirective {
264                kind = DwarfDirectiveKind::FourByteValues(parsed)
265            })
266        },
267    );
268
269    let four_byte_label = try_map(
270        seq(four_byte_keyword(), Label::parse()),
271        |(_, label), span| {
272            ok!(DwarfDirective {
273                kind = DwarfDirectiveKind::FourByteLabel(label)
274            })
275        },
276    );
277
278    let quad_values = try_map(
279        seq(
280            directive_exact_p("quad"),
281            sep_by1(unsigned_integer_literal(), comma_p()),
282        ),
283        |(_, values), span| {
284            let mut parsed = Vec::new();
285            for (text, value_span) in values {
286                let value = parse_unsigned_integer(&text, value_span, 0, u64::MAX as u128)?;
287                parsed.push(value as u64);
288            }
289            ok!(DwarfDirective {
290                kind = DwarfDirectiveKind::QuadValues(parsed)
291            })
292        },
293    );
294
295    let quad_label = try_map(
296        seq(directive_exact_p("quad"), Label::parse()),
297        |(_, label), span| {
298            ok!(DwarfDirective {
299                kind = DwarfDirectiveKind::QuadLabel(label)
300            })
301        },
302    );
303
304    alt!(
305        byte_values,
306        four_byte_label,
307        quad_label,
308        four_byte_values,
309        quad_values
310    )
311}
312
313fn pragma_directive()
314-> impl Fn(&mut PtxTokenStream) -> Result<(StatementDirective, Span), PtxParseError> {
315    try_map(
316        skip_semicolon(seq(directive_exact_p("pragma"), string_literal_p())),
317        |(_, text), span| {
318            let kind = match text.trim() {
319                "nounroll" => PragmaDirectiveKind::Nounroll,
320                "enable_smem_spilling" => PragmaDirectiveKind::EnableSmemSpilling,
321                other if other.starts_with("used_bytes_mask") => {
322                    let mask = other["used_bytes_mask".len()..].trim().to_string();
323                    PragmaDirectiveKind::UsedBytesMask { mask }
324                }
325                other if other.starts_with("frequency") => {
326                    let value_str = other["frequency".len()..].trim();
327                    let value = parse_u32_literal(value_str, span)?;
328                    PragmaDirectiveKind::Frequency { value }
329                }
330                other => PragmaDirectiveKind::Raw(other.to_string()),
331            };
332            let directive = c!(PragmaDirective { kind });
333            ok!(StatementDirective::Pragma { directive })
334        },
335    )
336}
337
338fn section_directive()
339-> impl Fn(&mut PtxTokenStream) -> Result<(StatementDirective, Span), PtxParseError> {
340    mapc!(
341        SectionDirective::parse(),
342        StatementDirective::Section { directive }
343    )
344}
345
346fn register_statement()
347-> impl Fn(&mut PtxTokenStream) -> Result<(RegisterDirective, Span), PtxParseError> {
348    map(
349        skip_semicolon(seq_n!(
350            skip_first(directive_exact_p("reg"), optional(register_vector_width())),
351            DataType::parse(),
352            register_targets_parser()
353        )),
354        |(vector, ty, registers), span| RegisterDirective {
355            vector,
356            ty,
357            registers,
358            span,
359        },
360    )
361}
362
363fn register_vector_width()
364-> impl Fn(&mut PtxTokenStream) -> Result<(RegisterVectorWidth, Span), PtxParseError> {
365    alt(
366        map(string_p(".v2"), |_, _| RegisterVectorWidth::V2),
367        map(string_p(".v4"), |_, _| RegisterVectorWidth::V4),
368    )
369}
370
371fn register_targets_parser()
372-> impl Fn(&mut PtxTokenStream) -> Result<(Vec<RegisterTarget>, Span), PtxParseError> {
373    map(
374        sep_by1(
375            seq(register_symbol(), optional(register_count())),
376            comma_p(),
377        ),
378        |entries, _span| {
379            let registers = entries
380                .into_iter()
381                .map(|(symbol, range)| {
382                    let symbol_span = symbol.span;
383                    RegisterTarget {
384                        name: symbol,
385                        range,
386                        span: symbol_span,
387                    }
388                })
389                .collect();
390            registers
391        },
392    )
393}
394
395fn register_symbol() -> impl Fn(&mut PtxTokenStream) -> Result<(VariableSymbol, Span), PtxParseError>
396{
397    alt(
398        map(register_p(), |name, span| VariableSymbol {
399            val: name,
400            span,
401        }),
402        map(identifier_p(), |val, span| VariableSymbol { val, span }),
403    )
404}
405
406fn register_count() -> impl Fn(&mut PtxTokenStream) -> Result<(u32, Span), PtxParseError> {
407    between(langle_p(), rangle_p(), u32_p())
408}
409
410fn location_directive()
411-> impl Fn(&mut PtxTokenStream) -> Result<(LocationDirective, Span), PtxParseError> {
412    let inlined_at = map(
413        seq_n!(
414            skip_first(string_p("inlined_at"), u32_p()),
415            u32_p(),
416            u32_p()
417        ),
418        |(file_index, line, column), span| LocationInlinedAt {
419            file_index,
420            line,
421            column,
422            span,
423        },
424    );
425    let function = map(
426        skip_first(
427            comma_p(),
428            seq_n!(
429                skip_first(string_p("function_name"), Label::parse()),
430                optional(skip_first(
431                    plus_p(),
432                    try_map(integer_p(), |digits, span| {
433                        let value = parse_unsigned_integer(&digits, span, 0, i64::MAX as u128)?;
434                        Ok(value as i64)
435                    })
436                )),
437                skip_first(comma_p(), inlined_at)
438            ),
439        ),
440        |(label, label_offset, inlined_at), span| LocationFunctionInfo {
441            label,
442            label_offset,
443            inlined_at,
444            span,
445        },
446    );
447    map(
448        seq_n!(
449            skip_first(directive_exact_p("loc"), u32_p()),
450            u32_p(),
451            u32_p(),
452            optional(function)
453        ),
454        |(file_index, line, column, function), span| LocationDirective {
455            file_index,
456            line,
457            column,
458            function,
459            span,
460        },
461    )
462}
463
464fn section_name_parser() -> impl Fn(&mut PtxTokenStream) -> Result<(String, Span), PtxParseError> {
465    alt(
466        map(directive_p(), func!(|name| format!(".{name}"))),
467        identifier_p(),
468    )
469}
470
471fn section_body_parser()
472-> impl Fn(&mut PtxTokenStream) -> Result<(Vec<SectionEntry>, Span), PtxParseError> {
473    between(lbrace_p(), rbrace_p(), many(section_entry_parser()))
474}
475
476fn skip_optional_semicolon<T, P>(
477    parser: P,
478) -> impl Fn(&mut PtxTokenStream) -> Result<(T, Span), PtxParseError>
479where
480    P: Fn(&mut PtxTokenStream) -> Result<(T, Span), PtxParseError>,
481{
482    move |stream| {
483        let (value, span) = parser(stream)?;
484        let _ = optional(semicolon_p())(stream)?;
485        Ok((value, span))
486    }
487}
488
489fn section_entry_parser()
490-> impl Fn(&mut PtxTokenStream) -> Result<(SectionEntry, Span), PtxParseError> {
491    alt(
492        label_entry(),
493        map(
494            section_directive_line(),
495            func!(|line| SectionEntry::Directive(line)),
496        ),
497    )
498}
499
500fn label_entry() -> impl Fn(&mut PtxTokenStream) -> Result<(SectionEntry, Span), PtxParseError> {
501    map(seq(Label::parse(), colon_p()), |(label, _), span| {
502        SectionEntry::Label { label, span }
503    })
504}
505
506fn section_directive_line()
507-> impl Fn(&mut PtxTokenStream) -> Result<(StatementSectionDirectiveLine, Span), PtxParseError> {
508    let b8 = try_map(
509        skip_optional_semicolon(skip_first(
510            directive_exact_p("b8"),
511            sep_by1(signed_integer_literal(), comma_p()),
512        )),
513        func!(|values| {
514            let mut out = Vec::new();
515            for (text, value_span) in values {
516                let value = parse_signed_integer(&text, value_span, -128, 255)?;
517                out.push(value as i16);
518            }
519            ok!(StatementSectionDirectiveLine::B8 { values = out })
520        }),
521    );
522
523    let b16 = try_map(
524        skip_optional_semicolon(skip_first(
525            directive_exact_p("b16"),
526            sep_by1(signed_integer_literal(), comma_p()),
527        )),
528        |values, span| {
529            let mut out = Vec::new();
530            for (text, value_span) in values {
531                let value = parse_signed_integer(&text, value_span, -32_768, 65_535)?;
532                out.push(value as i32);
533            }
534            ok!(StatementSectionDirectiveLine::B16 { values = out })
535        },
536    );
537
538    let b32 = try_map(
539        skip_optional_semicolon(skip_first(directive_exact_p("b32"), b32_section_suffix())),
540        |line, span| Ok(line.with_span(span)),
541    );
542
543    let b64 = try_map(
544        skip_optional_semicolon(skip_first(directive_exact_p("b64"), b64_section_suffix())),
545        |line, span| Ok(line.with_span(span)),
546    );
547
548    alt!(b8, b16, b32, b64)
549}
550
551fn b32_section_suffix()
552-> impl Fn(&mut PtxTokenStream) -> Result<(StatementSectionDirectiveLine, Span), PtxParseError> {
553    let immediate = try_map(
554        sep_by1(signed_integer_literal(), comma_p()),
555        |values, span| {
556            let mut out = Vec::new();
557            for (text, value_span) in values {
558                let value =
559                    parse_signed_integer(&text, value_span, i64::MIN as i128, i64::MAX as i128)?;
560                out.push(value as i64);
561            }
562            ok!(StatementSectionDirectiveLine::B32Immediate { values = out })
563        },
564    );
565
566    let label_diff = try_map(
567        seq_n!(Label::parse(), minus_p(), Label::parse()),
568        |(left, _, right), span| {
569            ok!(StatementSectionDirectiveLine::B32LabelDiff {
570                entries = (left, right)
571            })
572        },
573    );
574
575    let label_plus = try_map(
576        seq_n!(
577            Label::parse(),
578            alt(map(plus_p(), |_, _| 1i32), map(minus_p(), |_, _| -1i32)),
579            integer_p(),
580        ),
581        |(label, sign, digits), span| {
582            let limit = if sign < 0 {
583                (i32::MAX as u128) + 1
584            } else {
585                i32::MAX as u128
586            };
587            let magnitude = parse_unsigned_integer(&digits, span, 0, limit)? as i128;
588            let value = if sign < 0 { -magnitude } else { magnitude };
589            ok!(StatementSectionDirectiveLine::B32LabelPlusImm {
590                entries = (label, value as i32)
591            })
592        },
593    );
594
595    let label_only = map(
596        Label::parse(),
597        |label, span| c!(StatementSectionDirectiveLine::B32Label { labels = label }),
598    );
599
600    alt!(immediate, label_diff, label_plus, label_only)
601}
602
603fn b64_section_suffix()
604-> impl Fn(&mut PtxTokenStream) -> Result<(StatementSectionDirectiveLine, Span), PtxParseError> {
605    let immediate = try_map(
606        sep_by1(signed_integer_literal(), comma_p()),
607        |values, span| {
608            let mut out = Vec::new();
609            for (text, value_span) in values {
610                let value = parse_signed_integer(&text, value_span, i128::MIN, i128::MAX)?;
611                out.push(value);
612            }
613            ok!(StatementSectionDirectiveLine::B64Immediate { values = out })
614        },
615    );
616
617    let label_diff = try_map(
618        seq_n!(Label::parse(), minus_p(), Label::parse()),
619        |(left, _, right), span| {
620            ok!(StatementSectionDirectiveLine::B64LabelDiff {
621                entries = (left, right)
622            })
623        },
624    );
625
626    let label_plus = try_map(
627        seq_n!(
628            Label::parse(),
629            alt(map(plus_p(), |_, _| 1i32), map(minus_p(), |_, _| -1i32)),
630            integer_p(),
631        ),
632        |(label, sign, digits), span| {
633            let limit = if sign < 0 {
634                (i64::MAX as u128) + 1
635            } else {
636                i64::MAX as u128
637            };
638            let magnitude = parse_unsigned_integer(&digits, span, 0, limit)? as i128;
639            let value = if sign < 0 { -magnitude } else { magnitude };
640            ok!(StatementSectionDirectiveLine::B64LabelPlusImm {
641                entries = (label, value as i64)
642            })
643        },
644    );
645
646    let label_only = map(
647        Label::parse(),
648        |label, span| c!(StatementSectionDirectiveLine::B64Label { labels = label }),
649    );
650
651    alt!(immediate, label_diff, label_plus, label_only)
652}
653
654fn signed_integer_literal()
655-> impl Fn(&mut PtxTokenStream) -> Result<((String, Span), Span), PtxParseError> {
656    map(
657        seq(
658            optional(alt(map(minus_p(), |_, _| '-'), map(plus_p(), |_, _| '+'))),
659            integer_p(),
660        ),
661        |(sign, digits), span| {
662            let mut value = String::new();
663            if let Some(ch) = sign {
664                if ch == '-' {
665                    value.push('-');
666                }
667            }
668            value.push_str(&digits);
669            (value, span)
670        },
671    )
672}
673
674fn unsigned_integer_literal()
675-> impl Fn(&mut PtxTokenStream) -> Result<((String, Span), Span), PtxParseError> {
676    map(integer_p(), |digits, span| (digits, span))
677}
678
679fn four_byte_keyword() -> impl Fn(&mut PtxTokenStream) -> Result<((), Span), PtxParseError> {
680    move |stream| {
681        stream.try_with_span(|stream| {
682            stream.expect(&PtxToken::Dot)?;
683            let (value, value_span) = integer_p()(stream)?;
684            if value != "4" {
685                return Err(crate::unexpected_value!(value_span, &["4"], value));
686            }
687            let (name, name_span) = identifier_p()(stream)?;
688            if name != "byte" {
689                return Err(crate::unexpected_value!(name_span, &["byte"], name));
690            }
691            Ok(())
692        })
693    }
694}
695
696impl PtxParser for AliasFunctionDirective {
697    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
698        use crate::parser::util::{comma_p, directive_exact_p, semicolon_p, skip_first};
699
700        try_map(
701            seq_n!(
702                skip_first(directive_exact_p("alias"), FunctionSymbol::parse()),
703                skip_first(comma_p(), FunctionSymbol::parse()),
704                semicolon_p()
705            ),
706            |(alias, target, _), span| ok!(AliasFunctionDirective { alias, target }),
707        )
708    }
709}
710
711impl PtxParser for FunctionBody {
712    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
713        try_map(statement_block_parser(), |statements, span| {
714            ok!(FunctionBody { statements })
715        })
716    }
717}
718
719/// Parse one brace-delimited executable scope without treating a malformed
720/// statement as the end of the block.
721fn statement_block_parser()
722-> impl Fn(&mut PtxTokenStream) -> Result<(Vec<FunctionStatement>, Span), PtxParseError> {
723    |stream| {
724        stream.try_with_span(|stream| {
725            stream.expect(&PtxToken::LBrace)?;
726            let mut statements = Vec::new();
727            loop {
728                match stream.peek() {
729                    Ok((PtxToken::RBrace, _)) => {
730                        stream.consume()?;
731                        return Ok(statements);
732                    }
733                    Ok(_) => {
734                        let (statement, _) = FunctionStatement::parse()(stream)?;
735                        statements.push(statement);
736                    }
737                    Err(error) => return Err(error),
738                }
739            }
740        })
741    }
742}
743
744/// Parser for pre-body declarations (.reg, .local, .shared, .param) that appear
745/// between the function header and the opening brace.
746fn pre_body_declaration()
747-> impl Fn(&mut PtxTokenStream) -> Result<(StatementDirective, Span), PtxParseError> {
748    let reg_stmt = mapc!(register_statement(), StatementDirective::Reg { directive });
749
750    let local_stmt = mapc!(
751        skip_first(directive_exact_p("local"), VariableDirective::parse()),
752        StatementDirective::Local { directive }
753    );
754
755    let param_stmt = mapc!(
756        skip_first(directive_exact_p("param"), VariableDirective::parse()),
757        StatementDirective::Param { directive }
758    );
759
760    let shared_stmt = mapc!(
761        skip_first(directive_exact_p("shared"), VariableDirective::parse()),
762        StatementDirective::Shared { directive }
763    );
764
765    alt!(reg_stmt, local_stmt, param_stmt, shared_stmt)
766}
767
768impl PtxParser for FuncFunctionDirective {
769    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
770        let return_spec = alt(
771            map(
772                between(
773                    lparen_p(),
774                    rparen_p(),
775                    optional(ParameterDirective::parse()),
776                ),
777                |param, _| param,
778            ),
779            map(optional(ParameterDirective::parse()), |param, _| param),
780        );
781
782        let body_or_prototype = alt(
783            map(FunctionBody::parse(), |body, _| Some(body)),
784            map(semicolon_p(), |_, _| None),
785        );
786
787        mapc!(
788            seq_n!(
789                skip_first(directive_exact_p("func"), many(AttributeDirective::parse())),
790                return_spec,
791                FunctionSymbol::parse(),
792                between(
793                    lparen_p(),
794                    rparen_p(),
795                    sep_by(ParameterDirective::parse(), comma_p()),
796                ),
797                many(FuncFunctionHeaderDirective::parse()),
798                many(pre_body_declaration()),
799                body_or_prototype,
800            ),
801            FuncFunctionDirective {
802                attributes,
803                return_param,
804                name,
805                params,
806                directives,
807                pre_body_declarations,
808                body
809            }
810        )
811    }
812}
813
814impl PtxParser for EntryFunctionDirective {
815    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
816        // Entry functions can be either:
817        // 1. Forward declarations (prototypes): .entry name();
818        // 2. Definitions: .entry name() { ... }
819        let body_or_prototype = alt(
820            map(FunctionBody::parse(), |body, _| Some(body)),
821            map(semicolon_p(), |_, _| None),
822        );
823
824        mapc!(
825            seq_n!(
826                skip_first(directive_exact_p("entry"), FunctionSymbol::parse()),
827                between(
828                    lparen_p(),
829                    rparen_p(),
830                    sep_by(ParameterDirective::parse(), comma_p()),
831                ),
832                many(EntryFunctionHeaderDirective::parse()),
833                body_or_prototype,
834            ),
835            EntryFunctionDirective {
836                name,
837                params,
838                directives,
839                body,
840            }
841        )
842    }
843}
844
845impl PtxParser for FuncFunctionHeaderDirective {
846    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
847        alt!(
848            mapc!(
849                directive_exact_p("noreturn"),
850                FuncFunctionHeaderDirective::NoReturn {}
851            ),
852            mapc!(
853                skip_first(
854                    directive_exact_p("pragma"),
855                    seq(sep_by1(string_literal_p(), comma_p()), semicolon_p())
856                ),
857                FuncFunctionHeaderDirective::Pragma { args, _ }
858            ),
859            mapc!(
860                skip_first(directive_exact_p("abi_preserve"), u32_p()),
861                FuncFunctionHeaderDirective::AbiPreserve { value }
862            ),
863            mapc!(
864                skip_first(directive_exact_p("abi_preserve_control"), u32_p()),
865                FuncFunctionHeaderDirective::AbiPreserveControl { value }
866            )
867        )
868    }
869}
870
871impl PtxParser for EntryFunctionHeaderDirective {
872    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
873        alt!(
874            mapc!(
875                skip_first(directive_exact_p("maxnreg"), u32_p()),
876                EntryFunctionHeaderDirective::MaxNReg { value }
877            ),
878            try_map(
879                skip_first(directive_exact_p("maxntid"), sep_by1(u32_p(), comma_p())),
880                |dim_strs, span| {
881                    let dim = parse_function_dim(&dim_strs, span)?;
882                    ok!(EntryFunctionHeaderDirective::MaxNTid { dim })
883                }
884            ),
885            try_map(
886                skip_first(directive_exact_p("reqntid"), sep_by1(u32_p(), comma_p())),
887                |dim_strs, span| {
888                    let dim = parse_function_dim(&dim_strs, span)?;
889                    ok!(EntryFunctionHeaderDirective::ReqNTid { dim })
890                }
891            ),
892            mapc!(
893                skip_first(directive_exact_p("minnctapersm"), u32_p()),
894                EntryFunctionHeaderDirective::MinNCtaPerSm { value }
895            ),
896            mapc!(
897                skip_first(directive_exact_p("maxnctapersm"), u32_p()),
898                EntryFunctionHeaderDirective::MaxNCtaPerSm { value }
899            ),
900            mapc!(
901                skip_first(
902                    directive_exact_p("pragma"),
903                    skip_second(sep_by1(string_literal_p(), comma_p()), semicolon_p())
904                ),
905                EntryFunctionHeaderDirective::Pragma { args }
906            )
907        )
908    }
909}
910
911fn parse_function_dim(dims: &[u32], span: Span) -> Result<FunctionDim, PtxParseError> {
912    match dims.len() {
913        1 => {
914            let x = dims[0];
915            Ok(FunctionDim::X { x, span })
916        }
917        2 => {
918            let x = dims[0];
919            let y = dims[1];
920            Ok(FunctionDim::XY { x, y, span })
921        }
922        3 => {
923            let x = dims[0];
924            let y = dims[1];
925            let z = dims[2];
926            Ok(FunctionDim::XYZ { x, y, z, span })
927        }
928        _ => Err(PtxParseError {
929            kind: ParseErrorKind::InvalidLiteral(format!(
930                "expected 1-3 dimensions, got {}",
931                dims.len()
932            )),
933            span,
934        }),
935    }
936}