Skip to main content

ptx_parser/parser/
common.rs

1use crate::{
2    alt, c, err, mapc, ok,
3    parser::{
4        ParseErrorKind, PtxParseError, PtxParser, PtxTokenStream, Span,
5        util::{
6            alt, at_p, between, comma_p, directive_exact_p, directive_p, exclamation_p,
7            identifier_p, lbrace_p, lbracket_p, literal_p, lparen_p, map, minus_p, optional,
8            parse_index_suffix, plus_p, rbrace_p, rbracket_p, register_p, rparen_p, sep_by1, seq,
9            skip_first, try_map, u64_p,
10        },
11    },
12    seq_n, span,
13    span::Spanned,
14    r#type::{
15        AddressBase, AddressOffset, AddressOperand, AttributeDirective, Axis, CodeLinkage,
16        DataLinkage, DataType, FunctionSymbol, GeneralOperand, Immediate, Instruction, Label,
17        Operand, ParamStateSpace, Predicate, PredicateRegister, RegisterOperand, Sign,
18        SpecialRegister, TexHandler2, TexHandler3, TexHandler3Optional, VariableSymbol,
19        VectorOperand,
20    },
21};
22
23const CODE_LINKAGE_EXPECTED: [&str; 3] = [".visible", ".extern", ".weak"];
24
25impl PtxParser for CodeLinkage {
26    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
27        try_map(directive_p(), |name, span| {
28            let node = match name.as_str() {
29                "visible" => c!(CodeLinkage::Visible),
30                "extern" => c!(CodeLinkage::Extern),
31                "weak" => c!(CodeLinkage::Weak),
32                _ => {
33                    return err!(ParseErrorKind::UnexpectedToken {
34                        expected: CODE_LINKAGE_EXPECTED
35                            .iter()
36                            .map(|s| s.to_string())
37                            .collect(),
38                        found: format!(".{name}"),
39                    });
40                }
41            };
42            Ok(node)
43        })
44    }
45}
46
47const DATA_LINKAGE_EXPECTED: [&str; 4] = [".visible", ".extern", ".weak", ".common"];
48
49impl PtxParser for DataLinkage {
50    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
51        try_map(directive_p(), |name, span| {
52            let node = match name.as_str() {
53                "visible" => c!(DataLinkage::Visible),
54                "extern" => c!(DataLinkage::Extern),
55                "weak" => c!(DataLinkage::Weak),
56                "common" => c!(DataLinkage::Common),
57                _ => {
58                    return err!(ParseErrorKind::UnexpectedToken {
59                        expected: DATA_LINKAGE_EXPECTED
60                            .iter()
61                            .map(|s| s.to_string())
62                            .collect(),
63                        found: format!(".{name}"),
64                    });
65                }
66            };
67            Ok(node)
68        })
69    }
70}
71
72const DATA_TYPE_EXPECTED: [&str; 21] = [
73    ".u8",
74    ".u16",
75    ".u32",
76    ".u64",
77    ".s8",
78    ".s16",
79    ".s32",
80    ".s64",
81    ".f16",
82    ".f16x2",
83    ".f32",
84    ".f64",
85    ".b8",
86    ".b16",
87    ".b32",
88    ".b64",
89    ".b128",
90    ".pred",
91    ".texref",
92    ".samplerref",
93    ".surfref",
94];
95
96impl PtxParser for DataType {
97    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
98        try_map(directive_p(), |name, span| {
99            let node = match name.as_str() {
100                "u8" => c!(DataType::U8),
101                "u16" => c!(DataType::U16),
102                "u32" => c!(DataType::U32),
103                "u64" => c!(DataType::U64),
104                "s8" => c!(DataType::S8),
105                "s16" => c!(DataType::S16),
106                "s32" => c!(DataType::S32),
107                "s64" => c!(DataType::S64),
108                "f16" => c!(DataType::F16),
109                "f16x2" => c!(DataType::F16x2),
110                "f32" => c!(DataType::F32),
111                "f64" => c!(DataType::F64),
112                "b8" => c!(DataType::B8),
113                "b16" => c!(DataType::B16),
114                "b32" => c!(DataType::B32),
115                "b64" => c!(DataType::B64),
116                "b128" => c!(DataType::B128),
117                "pred" => c!(DataType::Pred),
118                "texref" => c!(DataType::TexRef),
119                "samplerref" => c!(DataType::SamplerRef),
120                "surfref" => c!(DataType::SurfRef),
121                _ => {
122                    return err!(ParseErrorKind::UnexpectedToken {
123                        expected: DATA_TYPE_EXPECTED.iter().map(|s| s.to_string()).collect(),
124                        found: format!(".{name}"),
125                    });
126                }
127            };
128            Ok(node)
129        })
130    }
131}
132
133impl PtxParser for AttributeDirective {
134    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
135        // Design note: PTX spells variable attributes as the nested
136        // `.attribute(.managed)` or `.attribute(.unified(...))` construct;
137        // the AST stores only the semantic inner attribute.
138        try_map(
139            seq(
140                directive_exact_p("attribute"),
141                between(
142                    lparen_p(),
143                    rparen_p(),
144                    alt(
145                        map(directive_exact_p("managed"), |_, span| {
146                            c!(AttributeDirective::Managed)
147                        }),
148                        mapc!(
149                            skip_first(
150                                directive_exact_p("unified"),
151                                between(
152                                    lparen_p(),
153                                    rparen_p(),
154                                    seq_n!(u64_p(), skip_first(comma_p(), u64_p()))
155                                ),
156                            ),
157                            AttributeDirective::Unified { uuid1, uuid2 }
158                        ),
159                    ),
160                ),
161            ),
162            |(_, attribute), span| Ok(attribute.with_span(span)),
163        )
164    }
165}
166
167impl PtxParser for Sign {
168    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
169        alt(
170            map(plus_p(), |_, span| c!(Sign::Positive)),
171            map(minus_p(), |_, span| c!(Sign::Negative)),
172        )
173    }
174}
175
176impl PtxParser for RegisterOperand {
177    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
178        try_map(register_p(), |mut raw, span| {
179            let component = if let Some(idx) = raw.rfind('.') {
180                if idx + 1 >= raw.len() {
181                    return err!(ParseErrorKind::InvalidLiteral(
182                        "register component missing after '.'".into(),
183                    ));
184                }
185                let suffix = raw[idx + 1..].to_string();
186                raw.truncate(idx);
187                Some(suffix)
188            } else {
189                None
190            };
191            let name = raw;
192            ok!(RegisterOperand { name, component })
193        })
194    }
195}
196
197impl PtxParser for VariableSymbol {
198    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
199        map(identifier_p(), |val, span| c!(VariableSymbol { val }))
200    }
201}
202
203impl PtxParser for FunctionSymbol {
204    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
205        map(identifier_p(), |val, span| c!(FunctionSymbol { val }))
206    }
207}
208
209impl PtxParser for Label {
210    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
211        alt(
212            map(identifier_p(), |val, span| c!(Label { val })),
213            map(directive_p(), |name, span| {
214                c!(Label {
215                    val = format!(".{name}")
216                })
217            }),
218        )
219    }
220}
221
222impl PtxParser for PredicateRegister {
223    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
224        try_map(register_p(), |name, span| {
225            if name.starts_with("%p") {
226                ok!(PredicateRegister { name })
227            } else {
228                err!(ParseErrorKind::InvalidLiteral(
229                    "expected predicate register (%pX)".into(),
230                ))
231            }
232        })
233    }
234}
235
236impl PtxParser for Predicate {
237    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
238        try_map(
239            seq_n!(at_p(), optional(exclamation_p()), Operand::parse()),
240            |(_, negation, operand), span| {
241                let negated = negation.is_some();
242                ok!(Predicate { negated, operand })
243            },
244        )
245    }
246}
247
248impl PtxParser for Instruction {
249    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
250        use crate::r#type::instruction::Inst;
251
252        try_map(
253            seq(optional(Predicate::parse()), Inst::parse()),
254            |(predicate, inst), span| ok!(Instruction { predicate, inst }),
255        )
256    }
257}
258
259impl PtxParser for ParamStateSpace {
260    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
261        alt!(
262            map(directive_exact_p("const"), |_, span| {
263                c!(ParamStateSpace::Const)
264            }),
265            map(directive_exact_p("global"), |_, span| {
266                c!(ParamStateSpace::Global)
267            }),
268            map(directive_exact_p("local"), |_, span| {
269                c!(ParamStateSpace::Local)
270            }),
271            map(directive_exact_p("shared"), |_, span| {
272                c!(ParamStateSpace::Shared)
273            }),
274        )
275    }
276}
277
278impl PtxParser for Operand {
279    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
280        let register = mapc!(RegisterOperand::parse(), Operand::Register { operand });
281        let immediate = map(
282            seq(optional(Sign::parse()), Immediate::parse()),
283            |(sign, mut operand), span| {
284                if matches!(sign, Some(Sign::Negative { .. })) && !operand.value.starts_with('-') {
285                    operand.value = format!("-{}", operand.value);
286                }
287                c!(Operand::Immediate { operand })
288            },
289        );
290        let symbol_offset = map(
291            seq_n!(identifier_p(), plus_p(), Immediate::parse()),
292            |(symbol, _, offset), span| c!(Operand::SymbolOffset { symbol, offset }),
293        );
294        let vector_symbol_component = try_map(
295            seq(identifier_p(), directive_p()),
296            |(symbol, component), span| match component.as_str() {
297                "x" | "y" | "z" | "w" | "r" | "g" | "b" | "a" => {
298                    ok!(Operand::VectorSymbolComponent { symbol, component })
299                }
300                _ => err!(ParseErrorKind::InvalidLiteral(format!(
301                    "invalid named vector register component .{component}"
302                ))),
303            },
304        );
305        let symbol = mapc!(identifier_p(), Operand::Symbol { name });
306        alt!(
307            register,
308            immediate,
309            symbol_offset,
310            vector_symbol_component,
311            symbol
312        )
313    }
314}
315
316impl PtxParser for VectorOperand {
317    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
318        try_map(
319            between(lbrace_p(), rbrace_p(), sep_by1(Operand::parse(), comma_p())),
320            |operands, span| match operands.len() {
321                1 => {
322                    let mut iter = operands.into_iter();
323                    let operand = iter.next().unwrap();
324                    ok!(VectorOperand::Vector1 { operand })
325                }
326                2 => {
327                    let mut iter = operands.into_iter();
328                    let operands = [iter.next().unwrap(), iter.next().unwrap()];
329                    ok!(VectorOperand::Vector2 { operands })
330                }
331                3 => {
332                    let mut iter = operands.into_iter();
333                    let operands = [
334                        iter.next().unwrap(),
335                        iter.next().unwrap(),
336                        iter.next().unwrap(),
337                    ];
338                    ok!(VectorOperand::Vector3 { operands })
339                }
340                4 => {
341                    let mut iter = operands.into_iter();
342                    let operands = [
343                        iter.next().unwrap(),
344                        iter.next().unwrap(),
345                        iter.next().unwrap(),
346                        iter.next().unwrap(),
347                    ];
348                    ok!(VectorOperand::Vector4 { operands })
349                }
350                8 => {
351                    let mut iter = operands.into_iter();
352                    let operands = [
353                        iter.next().unwrap(),
354                        iter.next().unwrap(),
355                        iter.next().unwrap(),
356                        iter.next().unwrap(),
357                        iter.next().unwrap(),
358                        iter.next().unwrap(),
359                        iter.next().unwrap(),
360                        iter.next().unwrap(),
361                    ];
362                    ok!(VectorOperand::Vector8 { operands })
363                }
364                _ => {
365                    let span = operands.first().map(Spanned::span).unwrap_or(span!(0..0));
366                    Err(PtxParseError {
367                        kind: ParseErrorKind::UnexpectedToken {
368                            expected: vec!["vector with 1,2,3,4, or 8 operands".into()],
369                            found: format!("vector with {} operands", operands.len()),
370                        },
371                        span,
372                    })
373                }
374            },
375        )
376    }
377}
378
379impl PtxParser for GeneralOperand {
380    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
381        alt(
382            mapc!(VectorOperand::parse(), GeneralOperand::Vec { operand }),
383            mapc!(Operand::parse(), GeneralOperand::Single { operand }),
384        )
385    }
386}
387
388fn handler_len_error(ty: &str, expected: &str, found: usize) -> PtxParseError {
389    PtxParseError {
390        kind: ParseErrorKind::InvalidLiteral(format!("{ty} expects {expected}, found {found}")),
391        span: Span::new(0, 0),
392    }
393}
394
395fn tex_operands()
396-> impl Fn(&mut PtxTokenStream) -> Result<(Vec<GeneralOperand>, Span), PtxParseError> {
397    between(
398        lbracket_p(),
399        rbracket_p(),
400        sep_by1(GeneralOperand::parse(), comma_p()),
401    )
402}
403
404impl PtxParser for TexHandler2 {
405    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
406        try_map(tex_operands(), |operands, span| {
407            if operands.len() != 2 {
408                return Err(handler_len_error(
409                    "TexHandler2",
410                    "exactly 2 operands",
411                    operands.len(),
412                ));
413            }
414            let mut iter = operands.into_iter();
415            let first = iter.next().unwrap();
416            let second = iter.next().unwrap();
417            let operands = [first, second];
418            ok!(TexHandler2 { operands })
419        })
420    }
421}
422
423impl PtxParser for TexHandler3 {
424    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
425        try_map(tex_operands(), |operands, span| {
426            if operands.len() != 3 {
427                return Err(handler_len_error(
428                    "TexHandler3",
429                    "exactly 3 operands",
430                    operands.len(),
431                ));
432            }
433            let mut iter = operands.into_iter();
434            let handle = iter.next().unwrap();
435            let sampler = iter.next().unwrap();
436            let coords = iter.next().unwrap();
437            ok!(TexHandler3 {
438                handle,
439                sampler,
440                coords
441            })
442        })
443    }
444}
445
446impl PtxParser for TexHandler3Optional {
447    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
448        try_map(tex_operands(), |operands, span| match operands.len() {
449            2 => {
450                let mut iter = operands.into_iter();
451                let handle = iter.next().unwrap();
452                let coords = iter.next().unwrap();
453                let sampler = None;
454                ok!(TexHandler3Optional {
455                    handle,
456                    sampler,
457                    coords
458                })
459            }
460            3 => {
461                let mut iter = operands.into_iter();
462                let handle = iter.next().unwrap();
463                let sampler = Some(iter.next().unwrap());
464                let coords = iter.next().unwrap();
465                ok!(TexHandler3Optional {
466                    handle,
467                    sampler,
468                    coords
469                })
470            }
471            other => Err(handler_len_error(
472                "TexHandler3Optional",
473                "2 or 3 operands",
474                other,
475            )),
476        })
477    }
478}
479
480impl PtxParser for SpecialRegister {
481    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
482        try_map(register_p(), |name, span| {
483            let raw = name.strip_prefix('%').ok_or_else(|| PtxParseError {
484                kind: ParseErrorKind::InvalidLiteral("expected % prefix".into()),
485                span,
486            })?;
487
488            let (base, axis_suffix) = split_axis_suffix(raw).map_err(|msg| PtxParseError {
489                kind: ParseErrorKind::InvalidLiteral(msg.into()),
490                span,
491            })?;
492            let base_lower = base.to_ascii_lowercase();
493
494            let mut node = match base_lower.as_str() {
495                "aggr_smem_size" => {
496                    ensure_no_axis(axis_suffix, span)?;
497                    c!(SpecialRegister::AggrSmemSize)
498                }
499                "dynamic_smem_size" => {
500                    ensure_no_axis(axis_suffix, span)?;
501                    c!(SpecialRegister::DynamicSmemSize)
502                }
503                "lanemask_gt" => {
504                    ensure_no_axis(axis_suffix, span)?;
505                    c!(SpecialRegister::LanemaskGt)
506                }
507                "reserved_smem_offset_begin" => {
508                    ensure_no_axis(axis_suffix, span)?;
509                    c!(SpecialRegister::ReservedSmemOffsetBegin)
510                }
511                "clock" => {
512                    ensure_no_axis(axis_suffix, span)?;
513                    c!(SpecialRegister::Clock)
514                }
515                "lanemask_le" => {
516                    ensure_no_axis(axis_suffix, span)?;
517                    c!(SpecialRegister::LanemaskLe)
518                }
519                "reserved_smem_offset_cap" => {
520                    ensure_no_axis(axis_suffix, span)?;
521                    c!(SpecialRegister::ReservedSmemOffsetCap)
522                }
523                "clock64" => {
524                    ensure_no_axis(axis_suffix, span)?;
525                    c!(SpecialRegister::Clock64)
526                }
527                "globaltimer" => {
528                    ensure_no_axis(axis_suffix, span)?;
529                    c!(SpecialRegister::Globaltimer)
530                }
531                "lanemask_lt" => {
532                    ensure_no_axis(axis_suffix, span)?;
533                    c!(SpecialRegister::LanemaskLt)
534                }
535                "reserved_smem_offset_end" => {
536                    ensure_no_axis(axis_suffix, span)?;
537                    c!(SpecialRegister::ReservedSmemOffsetEnd)
538                }
539                "globaltimer_hi" => {
540                    ensure_no_axis(axis_suffix, span)?;
541                    c!(SpecialRegister::GlobaltimerHi)
542                }
543                "nclusterid" => {
544                    ensure_no_axis(axis_suffix, span)?;
545                    c!(SpecialRegister::Nclusterid)
546                }
547                "smid" => {
548                    ensure_no_axis(axis_suffix, span)?;
549                    c!(SpecialRegister::Smid)
550                }
551                "globaltimer_lo" => {
552                    ensure_no_axis(axis_suffix, span)?;
553                    c!(SpecialRegister::GlobaltimerLo)
554                }
555                "gridid" => {
556                    ensure_no_axis(axis_suffix, span)?;
557                    c!(SpecialRegister::Gridid)
558                }
559                "nsmid" => {
560                    ensure_no_axis(axis_suffix, span)?;
561                    c!(SpecialRegister::Nsmid)
562                }
563                "total_smem_size" => {
564                    ensure_no_axis(axis_suffix, span)?;
565                    c!(SpecialRegister::TotalSmemSize)
566                }
567                "is_explicit_cluster" => {
568                    ensure_no_axis(axis_suffix, span)?;
569                    c!(SpecialRegister::IsExplicitCluster)
570                }
571                "warpid" => {
572                    ensure_no_axis(axis_suffix, span)?;
573                    c!(SpecialRegister::Warpid)
574                }
575                "clusterid" => {
576                    ensure_no_axis(axis_suffix, span)?;
577                    c!(SpecialRegister::Clusterid)
578                }
579                "laneid" => {
580                    ensure_no_axis(axis_suffix, span)?;
581                    c!(SpecialRegister::Laneid)
582                }
583                "nwarpid" => {
584                    ensure_no_axis(axis_suffix, span)?;
585                    c!(SpecialRegister::Nwarpid)
586                }
587                "warpsz" => {
588                    ensure_no_axis(axis_suffix, span)?;
589                    c!(SpecialRegister::WARPSZ)
590                }
591                "lanemask_eq" => {
592                    ensure_no_axis(axis_suffix, span)?;
593                    c!(SpecialRegister::LanemaskEq)
594                }
595                "current_graph_exec" => {
596                    ensure_no_axis(axis_suffix, span)?;
597                    c!(SpecialRegister::CurrentGraphExec)
598                }
599                "lanemask_ge" => {
600                    ensure_no_axis(axis_suffix, span)?;
601                    c!(SpecialRegister::LanemaskGe)
602                }
603                "cluster_ctaid" => {
604                    let axis = axis_with_span(axis_suffix, &span)?;
605                    c!(SpecialRegister::ClusterCtaid { axis })
606                }
607                "cluster_ctarank" => {
608                    let axis = axis_with_span(axis_suffix, &span)?;
609                    c!(SpecialRegister::ClusterCtarank { axis })
610                }
611                "nctaid" => {
612                    let axis = axis_with_span(axis_suffix, &span)?;
613                    c!(SpecialRegister::Nctaid { axis })
614                }
615                "tid" => {
616                    let axis = axis_with_span(axis_suffix, &span)?;
617                    c!(SpecialRegister::Tid { axis })
618                }
619                "cluster_nctaid" => {
620                    let axis = axis_with_span(axis_suffix, &span)?;
621                    c!(SpecialRegister::ClusterNctaid { axis })
622                }
623                "cluster_nctarank" => {
624                    let axis = axis_with_span(axis_suffix, &span)?;
625                    c!(SpecialRegister::ClusterNctarank { axis })
626                }
627                "ntid" => {
628                    let axis = axis_with_span(axis_suffix, &span)?;
629                    c!(SpecialRegister::Ntid { axis })
630                }
631                "ctaid" => {
632                    let axis = axis_with_span(axis_suffix, &span)?;
633                    c!(SpecialRegister::Ctaid { axis })
634                }
635                base if base.starts_with("envreg") => {
636                    ensure_no_axis(axis_suffix, span)?;
637                    let digits = base.strip_prefix("envreg").unwrap();
638                    let index = parse_index_suffix(digits, 31, "envreg", span)?;
639                    c!(SpecialRegister::Envreg { index })
640                }
641                base if base.starts_with("pm") && base.ends_with("_64") => {
642                    ensure_no_axis(axis_suffix, span)?;
643                    let digits = base
644                        .strip_prefix("pm")
645                        .and_then(|rest| rest.strip_suffix("_64"))
646                        .unwrap();
647                    let index = parse_index_suffix(digits, 7, "pm64", span)?;
648                    c!(SpecialRegister::Pm64 { index })
649                }
650                base if base.starts_with("pm") => {
651                    ensure_no_axis(axis_suffix, span)?;
652                    let digits = base.strip_prefix("pm").unwrap();
653                    let index = parse_index_suffix(digits, 7, "pm", span)?;
654                    c!(SpecialRegister::Pm { index })
655                }
656                base if base.starts_with("reserved_smem_offset_") => {
657                    ensure_no_axis(axis_suffix, span)?;
658                    let digits = base.strip_prefix("reserved_smem_offset_").unwrap();
659                    let index = parse_index_suffix(digits, 1, "reserved_smem_offset", span)?;
660                    c!(SpecialRegister::ReservedSmemOffset { index })
661                }
662                _ => {
663                    return err!(ParseErrorKind::InvalidLiteral(format!(
664                        "unknown special register: {name}"
665                    )));
666                }
667            };
668
669            node.set_span(span);
670            Ok(node)
671        })
672    }
673}
674
675impl PtxParser for Immediate {
676    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
677        mapc!(literal_p(), Immediate { value })
678    }
679}
680
681impl PtxParser for AddressBase {
682    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
683        alt(
684            mapc!(RegisterOperand::parse(), AddressBase::Register { operand }),
685            mapc!(VariableSymbol::parse(), AddressBase::Variable { symbol }),
686        )
687    }
688}
689
690impl PtxParser for AddressOffset {
691    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
692        alt(
693            mapc!(
694                seq(plus_p(), RegisterOperand::parse()),
695                AddressOffset::Register { _, operand }
696            ),
697            map(
698                seq_n!(
699                    map(plus_p(), |_, span| span),
700                    optional(map(minus_p(), |_, span| span)),
701                    Immediate::parse(),
702                ),
703                |(plus_span, minus_span, value), span| {
704                    // Design note: PTX spells a negative address displacement
705                    // as `base+-N`. The first `+` belongs to AddressOffset;
706                    // only the optional `-` is the immediate value's sign.
707                    let sign = minus_span.map_or(Sign::Positive { span: plus_span }, |span| {
708                        Sign::Negative { span }
709                    });
710                    c!(AddressOffset::Immediate { sign, value })
711                },
712            ),
713        )
714    }
715}
716
717impl PtxParser for AddressOperand {
718    fn parse() -> impl Fn(&mut PtxTokenStream) -> Result<(Self, Span), PtxParseError> {
719        alt!(
720            mapc!(
721                seq(
722                    VariableSymbol::parse(),
723                    between(lbracket_p(), rbracket_p(), Immediate::parse()),
724                ),
725                AddressOperand::Array { base, index }
726            ),
727            mapc!(
728                between(
729                    lbracket_p(),
730                    rbracket_p(),
731                    seq(AddressBase::parse(), optional(AddressOffset::parse())),
732                ),
733                AddressOperand::Offset { base, offset }
734            ),
735            map(
736                between(
737                    lbracket_p(),
738                    rbracket_p(),
739                    seq(optional(Sign::parse()), Immediate::parse()),
740                ),
741                |(sign, mut addr), span| {
742                    if matches!(sign, Some(Sign::Negative { .. })) && !addr.value.starts_with('-') {
743                        addr.value = format!("-{}", addr.value);
744                    }
745                    c!(AddressOperand::ImmediateAddress { addr })
746                },
747            ),
748        )
749    }
750}
751
752fn split_axis_suffix(raw: &str) -> Result<(&str, Option<char>), &'static str> {
753    if let Some(idx) = raw.rfind('.') {
754        let (base, suffix) = raw.split_at(idx);
755        if suffix.len() != 2 {
756            return Err("invalid axis suffix");
757        }
758        let axis_char = suffix.chars().nth(1).unwrap();
759        Ok((base, Some(axis_char)))
760    } else {
761        Ok((raw, None))
762    }
763}
764
765fn axis_from_suffix(axis: Option<char>, span: Span) -> Result<Axis, PtxParseError> {
766    match axis.map(|c| c.to_ascii_lowercase()) {
767        None => Ok(Axis::None { span }),
768        Some('x') => Ok(Axis::X { span }),
769        Some('y') => Ok(Axis::Y { span }),
770        Some('z') => Ok(Axis::Z { span }),
771        Some(other) => Err(PtxParseError {
772            kind: ParseErrorKind::InvalidLiteral(format!(
773                "invalid axis '.{}' for special register",
774                other
775            )),
776            span,
777        }),
778    }
779}
780
781fn axis_with_span(axis: Option<char>, span: &Span) -> Result<Axis, PtxParseError> {
782    axis_from_suffix(axis, *span)
783}
784
785fn ensure_no_axis(axis: Option<char>, span: Span) -> Result<(), PtxParseError> {
786    if let Some(ch) = axis {
787        Err(PtxParseError {
788            kind: ParseErrorKind::InvalidLiteral(format!(
789                "register does not accept axis '.{}'",
790                ch
791            )),
792            span,
793        })
794    } else {
795        Ok(())
796    }
797}