Skip to content

Commit b02beac

Browse files
committed
Lower asm expressions
1 parent cd391bb commit b02beac

File tree

6 files changed

+320
-40
lines changed

6 files changed

+320
-40
lines changed

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

Lines changed: 121 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ use crate::{
3434
FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
3535
FormatPlaceholder, FormatSign, FormatTrait,
3636
},
37-
Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
38-
Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability,
39-
OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement,
37+
Array, AsmOperand, AsmOptions, Binding, BindingAnnotation, BindingId, BindingProblems,
38+
CaptureBy, ClosureKind, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass, Label, LabelId,
39+
Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat, PatId, RecordFieldPat,
40+
RecordLitField, Statement,
4041
},
4142
item_scope::BuiltinShadowMode,
4243
lang_item::LangItem,
@@ -693,13 +694,7 @@ impl ExprCollector<'_> {
693694
}
694695
}
695696
ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr),
696-
ast::Expr::AsmExpr(e) => {
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-
)
702-
}
697+
ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr),
703698
ast::Expr::OffsetOfExpr(e) => {
704699
let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty()));
705700
let fields = e.fields().map(|it| it.as_name()).collect();
@@ -2055,7 +2050,123 @@ impl ExprCollector<'_> {
20552050
is_assignee_expr: false,
20562051
})
20572052
}
2053+
20582054
// endregion: format
2055+
fn lower_inline_asm(&mut self, asm: ast::AsmExpr, syntax_ptr: AstPtr<ast::Expr>) -> ExprId {
2056+
let mut clobber_abis = FxIndexSet::default();
2057+
let mut operands = vec![];
2058+
let mut options = AsmOptions::empty();
2059+
for operand in asm.asm_operands() {
2060+
let lower_reg = |reg: Option<ast::AsmRegSpec>| {
2061+
let reg = reg?;
2062+
if let Some(string) = reg.string_token() {
2063+
Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text())))
2064+
} else {
2065+
reg.name_ref().map(|name_ref| {
2066+
InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text()))
2067+
})
2068+
}
2069+
};
2070+
2071+
let op = match operand {
2072+
ast::AsmOperand::AsmClobberAbi(clobber_abi) => {
2073+
if let Some(abi_name) = clobber_abi.string_token() {
2074+
clobber_abis.insert(Symbol::intern(abi_name.text()));
2075+
}
2076+
continue;
2077+
}
2078+
ast::AsmOperand::AsmOptions(opt) => {
2079+
opt.asm_options().for_each(|opt| {
2080+
if opt.att_syntax_token().is_some() {
2081+
options |= AsmOptions::ATT_SYNTAX;
2082+
} else if opt.may_unwind_token().is_some() {
2083+
options |= AsmOptions::MAY_UNWIND;
2084+
} else if opt.nomem_token().is_some() {
2085+
options |= AsmOptions::NOMEM;
2086+
} else if opt.noreturn_token().is_some() {
2087+
options |= AsmOptions::NORETURN;
2088+
} else if opt.nostack_token().is_some() {
2089+
options |= AsmOptions::NOSTACK;
2090+
} else if opt.preserves_flags_token().is_some() {
2091+
options |= AsmOptions::PRESERVES_FLAGS;
2092+
} else if opt.pure_token().is_some() {
2093+
options |= AsmOptions::PURE;
2094+
} else if opt.raw_token().is_some() {
2095+
options |= AsmOptions::RAW;
2096+
} else if opt.readonly_token().is_some() {
2097+
options |= AsmOptions::READONLY;
2098+
}
2099+
});
2100+
continue;
2101+
}
2102+
ast::AsmOperand::AsmRegOperand(op) => {
2103+
let dir_spec = op.asm_dir_spec().unwrap();
2104+
let Some(reg) = lower_reg(op.asm_reg_spec()) else {
2105+
continue;
2106+
};
2107+
if dir_spec.in_token().is_some() {
2108+
let expr = self
2109+
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
2110+
AsmOperand::In { reg, expr }
2111+
} else if dir_spec.out_token().is_some() {
2112+
let expr = self
2113+
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
2114+
AsmOperand::Out { reg, expr: Some(expr), late: false }
2115+
} else if dir_spec.lateout_token().is_some() {
2116+
let expr = self
2117+
.collect_expr_opt(op.asm_operand_expr().and_then(|it| it.in_expr()));
2118+
AsmOperand::Out { reg, expr: Some(expr), late: true }
2119+
} else if dir_spec.inout_token().is_some() {
2120+
let op_expr = op.asm_operand_expr().unwrap();
2121+
let in_expr = self.collect_expr_opt(op_expr.in_expr());
2122+
let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
2123+
match out_expr {
2124+
Some(out_expr) => AsmOperand::SplitInOut {
2125+
reg,
2126+
in_expr,
2127+
out_expr: Some(out_expr),
2128+
late: false,
2129+
},
2130+
None => AsmOperand::InOut { reg, expr: in_expr, late: false },
2131+
}
2132+
} else if dir_spec.inlateout_token().is_some() {
2133+
let op_expr = op.asm_operand_expr().unwrap();
2134+
let in_expr = self.collect_expr_opt(op_expr.in_expr());
2135+
let out_expr = op_expr.out_expr().map(|it| self.collect_expr(it));
2136+
match out_expr {
2137+
Some(out_expr) => AsmOperand::SplitInOut {
2138+
reg,
2139+
in_expr,
2140+
out_expr: Some(out_expr),
2141+
late: false,
2142+
},
2143+
None => AsmOperand::InOut { reg, expr: in_expr, late: false },
2144+
}
2145+
} else {
2146+
continue;
2147+
}
2148+
}
2149+
ast::AsmOperand::AsmLabel(l) => {
2150+
AsmOperand::Label(self.collect_block_opt(l.block_expr()))
2151+
}
2152+
ast::AsmOperand::AsmConst(c) => AsmOperand::Const(self.collect_expr_opt(c.expr())),
2153+
ast::AsmOperand::AsmSym(s) => {
2154+
let Some(path) = s.path().and_then(|p| self.expander.parse_path(self.db, p))
2155+
else {
2156+
continue;
2157+
};
2158+
AsmOperand::Sym(path)
2159+
}
2160+
};
2161+
operands.push(op);
2162+
}
2163+
2164+
let template = asm.template().map(|it| self.collect_expr(it)).collect();
2165+
self.alloc_expr(
2166+
Expr::InlineAsm(InlineAsm { template, operands: operands.into_boxed_slice(), options }),
2167+
syntax_ptr,
2168+
)
2169+
}
20592170
}
20602171

20612172
fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> {

crates/hir-def/src/hir.rs

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,120 @@ pub struct OffsetOf {
308308
#[derive(Debug, Clone, PartialEq, Eq)]
309309
pub struct InlineAsm {
310310
pub template: Box<[ExprId]>,
311-
pub operands: Box<[()]>,
311+
pub operands: Box<[AsmOperand]>,
312+
pub options: AsmOptions,
313+
}
314+
315+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
316+
pub struct AsmOptions(u16);
317+
bitflags::bitflags! {
318+
impl AsmOptions: u16 {
319+
const PURE = 1 << 0;
320+
const NOMEM = 1 << 1;
321+
const READONLY = 1 << 2;
322+
const PRESERVES_FLAGS = 1 << 3;
323+
const NORETURN = 1 << 4;
324+
const NOSTACK = 1 << 5;
325+
const ATT_SYNTAX = 1 << 6;
326+
const RAW = 1 << 7;
327+
const MAY_UNWIND = 1 << 8;
328+
}
329+
}
330+
331+
impl AsmOptions {
332+
pub const COUNT: usize = Self::all().bits().count_ones() as usize;
333+
334+
pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW);
335+
pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN);
336+
337+
pub fn human_readable_names(&self) -> Vec<&'static str> {
338+
let mut options = vec![];
339+
340+
if self.contains(AsmOptions::PURE) {
341+
options.push("pure");
342+
}
343+
if self.contains(AsmOptions::NOMEM) {
344+
options.push("nomem");
345+
}
346+
if self.contains(AsmOptions::READONLY) {
347+
options.push("readonly");
348+
}
349+
if self.contains(AsmOptions::PRESERVES_FLAGS) {
350+
options.push("preserves_flags");
351+
}
352+
if self.contains(AsmOptions::NORETURN) {
353+
options.push("noreturn");
354+
}
355+
if self.contains(AsmOptions::NOSTACK) {
356+
options.push("nostack");
357+
}
358+
if self.contains(AsmOptions::ATT_SYNTAX) {
359+
options.push("att_syntax");
360+
}
361+
if self.contains(AsmOptions::RAW) {
362+
options.push("raw");
363+
}
364+
if self.contains(AsmOptions::MAY_UNWIND) {
365+
options.push("may_unwind");
366+
}
367+
368+
options
369+
}
370+
}
371+
372+
impl std::fmt::Debug for AsmOptions {
373+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
374+
bitflags::parser::to_writer(self, f)
375+
}
376+
}
377+
378+
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
379+
pub enum AsmOperand {
380+
In {
381+
reg: InlineAsmRegOrRegClass,
382+
expr: ExprId,
383+
},
384+
Out {
385+
reg: InlineAsmRegOrRegClass,
386+
expr: Option<ExprId>,
387+
late: bool,
388+
},
389+
InOut {
390+
reg: InlineAsmRegOrRegClass,
391+
expr: ExprId,
392+
late: bool,
393+
},
394+
SplitInOut {
395+
reg: InlineAsmRegOrRegClass,
396+
in_expr: ExprId,
397+
out_expr: Option<ExprId>,
398+
late: bool,
399+
},
400+
Label(ExprId),
401+
Const(ExprId),
402+
Sym(Path),
403+
}
404+
405+
impl AsmOperand {
406+
pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> {
407+
match self {
408+
Self::In { reg, .. }
409+
| Self::Out { reg, .. }
410+
| Self::InOut { reg, .. }
411+
| Self::SplitInOut { reg, .. } => Some(reg),
412+
Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None,
413+
}
414+
}
415+
416+
pub fn is_clobber(&self) -> bool {
417+
matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None })
418+
}
419+
}
420+
421+
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
422+
pub enum InlineAsmRegOrRegClass {
423+
Reg(Symbol),
424+
RegClass(Symbol),
312425
}
313426

314427
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

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

Lines changed: 61 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
99
use either::Either;
1010
use hir_def::{
1111
hir::{
12-
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
12+
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
13+
Literal, Statement, UnaryOp,
1314
},
1415
lang_item::{LangItem, LangItemTarget},
1516
path::{GenericArg, GenericArgs, Path},
@@ -41,9 +42,9 @@ use crate::{
4142
primitive::{self, UintTy},
4243
static_lifetime, to_chalk_trait_id,
4344
traits::FnTrait,
44-
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig,
45-
FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
46-
TyExt, TyKind,
45+
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer,
46+
FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty,
47+
TyBuilder, TyExt, TyKind,
4748
};
4849

4950
use super::{
@@ -919,9 +920,62 @@ impl InferenceContext<'_> {
919920
expected
920921
}
921922
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
922-
Expr::InlineAsm(it) => {
923-
it.template.iter().for_each(|&expr| _ = self.infer_expr_no_expect(expr));
924-
self.result.standard_types.unit.clone()
923+
Expr::InlineAsm(asm) => {
924+
asm.template.iter().for_each(|&expr| _ = self.infer_expr_no_expect(expr));
925+
let mut check_expr_asm_operand = |expr, is_input: bool| {
926+
let ty = self.infer_expr_no_expect(expr);
927+
928+
// If this is an input value, we require its type to be fully resolved
929+
// at this point. This allows us to provide helpful coercions which help
930+
// pass the type candidate list in a later pass.
931+
//
932+
// We don't require output types to be resolved at this point, which
933+
// allows them to be inferred based on how they are used later in the
934+
// function.
935+
if is_input {
936+
let ty = self.resolve_ty_shallow(&ty);
937+
match ty.kind(Interner) {
938+
TyKind::FnDef(def, parameters) => {
939+
let fnptr_ty = TyKind::Function(
940+
CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
941+
)
942+
.intern(Interner);
943+
_ = self.coerce(Some(expr), &ty, &fnptr_ty);
944+
}
945+
TyKind::Ref(mutbl, _, base_ty) => {
946+
let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
947+
_ = self.coerce(Some(expr), &ty, &ptr_ty);
948+
}
949+
_ => {}
950+
}
951+
}
952+
};
953+
954+
let diverge = asm.options.contains(AsmOptions::NORETURN);
955+
asm.operands.iter().for_each(|operand| match *operand {
956+
AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true),
957+
AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => {
958+
check_expr_asm_operand(expr, false)
959+
}
960+
AsmOperand::Out { expr: None, .. } => (),
961+
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
962+
check_expr_asm_operand(in_expr, true);
963+
if let Some(out_expr) = out_expr {
964+
check_expr_asm_operand(out_expr, false);
965+
}
966+
}
967+
// FIXME
968+
AsmOperand::Label(_) => (),
969+
// FIXME
970+
AsmOperand::Const(_) => (),
971+
// FIXME
972+
AsmOperand::Sym(_) => (),
973+
});
974+
if diverge {
975+
self.result.standard_types.never.clone()
976+
} else {
977+
self.result.standard_types.unit.clone()
978+
}
925979
}
926980
};
927981
// use a new type variable if we got unknown here

0 commit comments

Comments
 (0)