Skip to content

Commit cd391bb

Browse files
committed
Parse builtin#asm expressions
1 parent 8d1d5cd commit cd391bb

File tree

21 files changed

+865
-31
lines changed

21 files changed

+865
-31
lines changed

.typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ extend-ignore-re = [
1515
'"flate2"',
1616
"raison d'être",
1717
"inout",
18+
"INOUT",
1819
"optin"
1920
]
2021

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ style = { level = "warn", priority = -1 }
185185
suspicious = { level = "warn", priority = -1 }
186186

187187
## allow following lints
188+
too_long_first_doc_paragraph = "allow"
188189
# subjective
189190
single_match = "allow"
190191
# () makes a fine error in most cases

crates/hir-def/src/body/lower.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -694,8 +694,11 @@ impl ExprCollector<'_> {
694694
}
695695
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
696696
ast::Expr::AsmExpr(e) => {
697-
let e = self.collect_expr_opt(e.expr());
698-
self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr)
697+
let template = e.template().map(|it| self.collect_expr(it)).collect();
698+
self.alloc_expr(
699+
Expr::InlineAsm(InlineAsm { template, operands: Box::default() }),
700+
syntax_ptr,
701+
)
699702
}
700703
ast::Expr::OffsetOfExpr(e) => {
701704
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));

crates/hir-def/src/hir.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ pub struct OffsetOf {
307307

308308
#[derive(Debug, Clone, PartialEq, Eq)]
309309
pub struct InlineAsm {
310-
pub e: ExprId,
310+
pub template: Box<[ExprId]>,
311+
pub operands: Box<[()]>,
311312
}
312313

313314
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -372,7 +373,7 @@ impl Expr {
372373
match self {
373374
Expr::Missing => {}
374375
Expr::Path(_) | Expr::OffsetOf(_) => {}
375-
Expr::InlineAsm(it) => f(it.e),
376+
Expr::InlineAsm(it) => it.template.iter().copied().for_each(f),
376377
Expr::If { condition, then_branch, else_branch } => {
377378
f(*condition);
378379
f(*then_branch);

crates/hir-ty/src/infer/closure.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,9 @@ impl InferenceContext<'_> {
666666
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
667667
match &self.body[tgt_expr] {
668668
Expr::OffsetOf(_) => (),
669-
Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e),
669+
Expr::InlineAsm(e) => {
670+
e.template.iter().for_each(|it| self.walk_expr_without_adjust(*it))
671+
}
670672
Expr::If { condition, then_branch, else_branch } => {
671673
self.consume_expr(*condition);
672674
self.consume_expr(*then_branch);

crates/hir-ty/src/infer/expr.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,7 @@ impl InferenceContext<'_> {
920920
}
921921
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
922922
Expr::InlineAsm(it) => {
923-
self.infer_expr_no_expect(it.e);
923+
it.template.iter().for_each(|&expr| _ = self.infer_expr_no_expect(expr));
924924
self.result.standard_types.unit.clone()
925925
}
926926
};

crates/hir-ty/src/infer/mutability.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,10 @@ impl InferenceContext<'_> {
3939
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
4040
match &self.body[tgt_expr] {
4141
Expr::Missing => (),
42-
Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not),
42+
Expr::InlineAsm(e) => e
43+
.template
44+
.iter()
45+
.for_each(|&expr| self.infer_mut_expr_without_adjust(expr, Mutability::Not)),
4346
Expr::OffsetOf(_) => (),
4447
&Expr::If { condition, then_branch, else_branch } => {
4548
self.infer_mut_expr(condition, Mutability::Not);
@@ -129,7 +132,7 @@ impl InferenceContext<'_> {
129132
target,
130133
}) = base_adjustments
131134
{
132-
// For assignee exprs `IndexMut` obiligations are already applied
135+
// For assignee exprs `IndexMut` obligations are already applied
133136
if !is_assignee_expr {
134137
if let TyKind::Ref(_, _, ty) = target.kind(Interner) {
135138
base_ty = Some(ty.clone());

crates/parser/src/grammar/expressions/atom.rs

Lines changed: 164 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker {
245245

246246
// test builtin_expr
247247
// fn foo() {
248-
// builtin#asm(0);
248+
// builtin#asm("");
249249
// builtin#format_args("", 0, 1, a = 2 + 3, a + b);
250250
// builtin#offset_of(Foo, bar.baz.0);
251251
// }
@@ -297,18 +297,175 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
297297
p.expect(T![')']);
298298
Some(m.complete(p, FORMAT_ARGS_EXPR))
299299
} else if p.at_contextual_kw(T![asm]) {
300-
p.bump_remap(T![asm]);
301-
p.expect(T!['(']);
302-
// FIXME: We just put expression here so highlighting kind of keeps working
303-
expr(p);
304-
p.expect(T![')']);
305-
Some(m.complete(p, ASM_EXPR))
300+
parse_asm_expr(p, m)
306301
} else {
307302
m.abandon(p);
308303
None
309304
}
310305
}
311306

307+
// test asm_expr
308+
// fn foo() {
309+
// builtin#asm(
310+
// "mov {tmp}, {x}",
311+
// "shl {tmp}, 1",
312+
// "shl {x}, 2",
313+
// "add {x}, {tmp}",
314+
// x = inout(reg) x,
315+
// tmp = out(reg) _,
316+
// );
317+
// }
318+
fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
319+
p.bump_remap(T![asm]);
320+
p.expect(T!['(']);
321+
if expr(p).is_none() {
322+
p.err_and_bump("expected asm template");
323+
}
324+
let mut allow_templates = true;
325+
while !p.at(EOF) && !p.at(T![')']) {
326+
p.expect(T![,]);
327+
// accept trailing commas
328+
if p.at(T![')']) {
329+
break;
330+
}
331+
332+
// Parse clobber_abi
333+
if p.eat_contextual_kw(T![clobber_abi]) {
334+
parse_clobber_abi(p);
335+
allow_templates = false;
336+
continue;
337+
}
338+
339+
// Parse options
340+
if p.eat_contextual_kw(T![options]) {
341+
parse_options(p);
342+
allow_templates = false;
343+
continue;
344+
}
345+
346+
// Parse operand names
347+
if p.at(T![ident]) && p.nth_at(1, T![=]) {
348+
name(p);
349+
p.bump(T![=]);
350+
allow_templates = false;
351+
true
352+
} else {
353+
false
354+
};
355+
356+
let op = p.start();
357+
if p.eat(T![in]) {
358+
parse_reg(p);
359+
expr(p);
360+
op.complete(p, ASM_REG_OPERAND);
361+
} else if p.eat_contextual_kw(T![out]) {
362+
parse_reg(p);
363+
expr(p);
364+
op.complete(p, ASM_REG_OPERAND);
365+
} else if p.eat_contextual_kw(T![lateout]) {
366+
parse_reg(p);
367+
expr(p);
368+
op.complete(p, ASM_REG_OPERAND);
369+
} else if p.eat_contextual_kw(T![inout]) {
370+
parse_reg(p);
371+
expr(p);
372+
if p.eat(T![=>]) {
373+
expr(p);
374+
}
375+
op.complete(p, ASM_REG_OPERAND);
376+
} else if p.eat_contextual_kw(T![inlateout]) {
377+
parse_reg(p);
378+
expr(p);
379+
if p.eat(T![=>]) {
380+
expr(p);
381+
}
382+
op.complete(p, ASM_REG_OPERAND);
383+
} else if p.eat_contextual_kw(T![label]) {
384+
block_expr(p);
385+
op.complete(p, ASM_LABEL);
386+
} else if p.eat(T![const]) {
387+
expr(p);
388+
op.complete(p, ASM_CONST);
389+
} else if p.eat_contextual_kw(T![sym]) {
390+
expr(p);
391+
op.complete(p, ASM_SYM);
392+
} else if allow_templates {
393+
op.abandon(p);
394+
if expr(p).is_none() {
395+
p.err_and_bump("expected asm template");
396+
}
397+
continue;
398+
} else {
399+
op.abandon(p);
400+
p.err_and_bump("expected asm operand");
401+
if p.at(T!['}']) {
402+
break;
403+
}
404+
continue;
405+
};
406+
allow_templates = false;
407+
}
408+
p.expect(T![')']);
409+
Some(m.complete(p, ASM_EXPR))
410+
}
411+
412+
fn parse_options(p: &mut Parser<'_>) {
413+
p.expect(T!['(']);
414+
415+
while !p.eat(T![')']) && !p.at(EOF) {
416+
const OPTIONS: &[SyntaxKind] = &[
417+
T![pure],
418+
T![nomem],
419+
T![readonly],
420+
T![preserves_flags],
421+
T![noreturn],
422+
T![nostack],
423+
T![may_unwind],
424+
T![att_syntax],
425+
T![raw],
426+
];
427+
428+
if !OPTIONS.iter().any(|&syntax| p.eat(syntax)) {
429+
p.err_and_bump("expected asm option");
430+
continue;
431+
}
432+
433+
// Allow trailing commas
434+
if p.eat(T![')']) {
435+
break;
436+
}
437+
p.expect(T![,]);
438+
}
439+
}
440+
441+
fn parse_clobber_abi(p: &mut Parser<'_>) {
442+
p.expect(T!['(']);
443+
444+
while !p.eat(T![')']) && !p.at(EOF) {
445+
if !p.expect(T![string]) {
446+
break;
447+
}
448+
449+
// Allow trailing commas
450+
if p.eat(T![')']) {
451+
break;
452+
}
453+
p.expect(T![,]);
454+
}
455+
}
456+
457+
fn parse_reg(p: &mut Parser<'_>) {
458+
p.expect(T!['(']);
459+
if p.at(T![ident]) {
460+
name_ref(p)
461+
} else if p.at(T![string]) {
462+
p.bump_any()
463+
} else {
464+
p.err_and_bump("expected register name");
465+
}
466+
p.expect(T![')']);
467+
}
468+
312469
// test array_expr
313470
// fn foo() {
314471
// [];

crates/parser/src/parser.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,14 @@ impl<'t> Parser<'t> {
131131
true
132132
}
133133

134+
pub(crate) fn eat_contextual_kw(&mut self, kind: SyntaxKind) -> bool {
135+
if !self.at_contextual_kw(kind) {
136+
return false;
137+
}
138+
self.bump_remap(kind);
139+
true
140+
}
141+
134142
fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool {
135143
self.inp.kind(self.pos + n) == k1
136144
&& self.inp.kind(self.pos + n + 1) == k2

0 commit comments

Comments
 (0)