mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 22:01:37 +00:00
Lower asm expressions
This commit is contained in:
parent
86658c66b4
commit
3b11ff8c4d
14 changed files with 612 additions and 97 deletions
|
@ -10,7 +10,10 @@ use chalk_ir::{
|
|||
use either::Either;
|
||||
use hir_def::{
|
||||
data::adt::VariantData,
|
||||
hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp},
|
||||
hir::{
|
||||
Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement,
|
||||
UnaryOp,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
resolver::{resolver_for_expr, ResolveValueResult, ValueNs},
|
||||
DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId,
|
||||
|
@ -666,9 +669,21 @@ impl InferenceContext<'_> {
|
|||
fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) {
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::OffsetOf(_) => (),
|
||||
Expr::InlineAsm(e) => {
|
||||
e.template.iter().for_each(|it| self.walk_expr_without_adjust(*it))
|
||||
}
|
||||
Expr::InlineAsm(e) => e.operands.iter().for_each(|op| match op {
|
||||
AsmOperand::In { expr, .. }
|
||||
| AsmOperand::Out { expr: Some(expr), .. }
|
||||
| AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr),
|
||||
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
self.walk_expr_without_adjust(*in_expr);
|
||||
if let Some(out_expr) = out_expr {
|
||||
self.walk_expr_without_adjust(*out_expr);
|
||||
}
|
||||
}
|
||||
AsmOperand::Out { expr: None, .. }
|
||||
| AsmOperand::Const(_)
|
||||
| AsmOperand::Label(_)
|
||||
| AsmOperand::Sym(_) => (),
|
||||
}),
|
||||
Expr::If { condition, then_branch, else_branch } => {
|
||||
self.consume_expr(*condition);
|
||||
self.consume_expr(*then_branch);
|
||||
|
|
|
@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin
|
|||
use either::Either;
|
||||
use hir_def::{
|
||||
hir::{
|
||||
ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp,
|
||||
ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId,
|
||||
Literal, Statement, UnaryOp,
|
||||
},
|
||||
lang_item::{LangItem, LangItemTarget},
|
||||
path::{GenericArg, GenericArgs, Path},
|
||||
|
@ -41,9 +42,9 @@ use crate::{
|
|||
primitive::{self, UintTy},
|
||||
static_lifetime, to_chalk_trait_id,
|
||||
traits::FnTrait,
|
||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig,
|
||||
FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder,
|
||||
TyExt, TyKind,
|
||||
Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer,
|
||||
FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty,
|
||||
TyBuilder, TyExt, TyKind,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -924,9 +925,61 @@ impl InferenceContext<'_> {
|
|||
expected
|
||||
}
|
||||
Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner),
|
||||
Expr::InlineAsm(it) => {
|
||||
it.template.iter().for_each(|&expr| _ = self.infer_expr_no_expect(expr));
|
||||
self.result.standard_types.unit.clone()
|
||||
Expr::InlineAsm(asm) => {
|
||||
let mut check_expr_asm_operand = |expr, is_input: bool| {
|
||||
let ty = self.infer_expr_no_expect(expr);
|
||||
|
||||
// If this is an input value, we require its type to be fully resolved
|
||||
// at this point. This allows us to provide helpful coercions which help
|
||||
// pass the type candidate list in a later pass.
|
||||
//
|
||||
// We don't require output types to be resolved at this point, which
|
||||
// allows them to be inferred based on how they are used later in the
|
||||
// function.
|
||||
if is_input {
|
||||
let ty = self.resolve_ty_shallow(&ty);
|
||||
match ty.kind(Interner) {
|
||||
TyKind::FnDef(def, parameters) => {
|
||||
let fnptr_ty = TyKind::Function(
|
||||
CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(),
|
||||
)
|
||||
.intern(Interner);
|
||||
_ = self.coerce(Some(expr), &ty, &fnptr_ty);
|
||||
}
|
||||
TyKind::Ref(mutbl, _, base_ty) => {
|
||||
let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner);
|
||||
_ = self.coerce(Some(expr), &ty, &ptr_ty);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let diverge = asm.options.contains(AsmOptions::NORETURN);
|
||||
asm.operands.iter().for_each(|operand| match *operand {
|
||||
AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true),
|
||||
AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => {
|
||||
check_expr_asm_operand(expr, false)
|
||||
}
|
||||
AsmOperand::Out { expr: None, .. } => (),
|
||||
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
check_expr_asm_operand(in_expr, true);
|
||||
if let Some(out_expr) = out_expr {
|
||||
check_expr_asm_operand(out_expr, false);
|
||||
}
|
||||
}
|
||||
// FIXME
|
||||
AsmOperand::Label(_) => (),
|
||||
// FIXME
|
||||
AsmOperand::Const(_) => (),
|
||||
// FIXME
|
||||
AsmOperand::Sym(_) => (),
|
||||
});
|
||||
if diverge {
|
||||
self.result.standard_types.never.clone()
|
||||
} else {
|
||||
self.result.standard_types.unit.clone()
|
||||
}
|
||||
}
|
||||
};
|
||||
// use a new type variable if we got unknown here
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
use chalk_ir::{cast::Cast, Mutability};
|
||||
use hir_def::{
|
||||
hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp},
|
||||
hir::{
|
||||
Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp,
|
||||
},
|
||||
lang_item::LangItem,
|
||||
};
|
||||
use hir_expand::name::Name;
|
||||
|
@ -39,10 +41,25 @@ impl InferenceContext<'_> {
|
|||
fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) {
|
||||
match &self.body[tgt_expr] {
|
||||
Expr::Missing => (),
|
||||
Expr::InlineAsm(e) => e
|
||||
.template
|
||||
.iter()
|
||||
.for_each(|&expr| self.infer_mut_expr_without_adjust(expr, Mutability::Not)),
|
||||
Expr::InlineAsm(e) => {
|
||||
e.operands.iter().for_each(|op| match op {
|
||||
AsmOperand::In { expr, .. }
|
||||
| AsmOperand::Out { expr: Some(expr), .. }
|
||||
| AsmOperand::InOut { expr, .. } => {
|
||||
self.infer_mut_expr_without_adjust(*expr, Mutability::Not)
|
||||
}
|
||||
AsmOperand::SplitInOut { in_expr, out_expr, .. } => {
|
||||
self.infer_mut_expr_without_adjust(*in_expr, Mutability::Not);
|
||||
if let Some(out_expr) = out_expr {
|
||||
self.infer_mut_expr_without_adjust(*out_expr, Mutability::Not);
|
||||
}
|
||||
}
|
||||
AsmOperand::Out { expr: None, .. }
|
||||
| AsmOperand::Label(_)
|
||||
| AsmOperand::Sym(_)
|
||||
| AsmOperand::Const(_) => (),
|
||||
});
|
||||
}
|
||||
Expr::OffsetOf(_) => (),
|
||||
&Expr::If { condition, then_branch, else_branch } => {
|
||||
self.infer_mut_expr(condition, Mutability::Not);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use expect_test::expect;
|
||||
use test_utils::{bench, bench_fixture, skip_slow_tests};
|
||||
|
||||
use crate::tests::check_infer_with_mismatches;
|
||||
use crate::tests::{check_infer_with_mismatches, check_no_mismatches};
|
||||
|
||||
use super::{check_infer, check_types};
|
||||
|
||||
|
@ -1406,3 +1406,100 @@ fn foo(t: Tensor) {
|
|||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asm_unit() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
//- minicore: asm
|
||||
fn unit() {
|
||||
asm!("")
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asm_no_return() {
|
||||
check_no_mismatches(
|
||||
r#"
|
||||
//- minicore: asm
|
||||
fn unit() -> ! {
|
||||
asm!("", options(noreturn))
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn asm_things() {
|
||||
check_infer(
|
||||
r#"
|
||||
//- minicore: asm, concat
|
||||
fn main() {
|
||||
unsafe {
|
||||
let foo = 1;
|
||||
let mut o = 0;
|
||||
asm!(
|
||||
"%input = OpLoad _ {0}",
|
||||
concat!("%result = ", bar, " _ %input"),
|
||||
"OpStore {1} %result",
|
||||
in(reg) &foo,
|
||||
in(reg) &mut o,
|
||||
);
|
||||
o
|
||||
|
||||
let thread_id: usize;
|
||||
asm!("
|
||||
mov {0}, gs:[0x30]
|
||||
mov {0}, [{0}+0x48]
|
||||
", out(reg) thread_id, options(pure, readonly, nostack));
|
||||
|
||||
static UNMAP_BASE: usize;
|
||||
const MEM_RELEASE: usize;
|
||||
static VirtualFree: usize;
|
||||
const OffPtr: usize;
|
||||
const OffFn: usize;
|
||||
asm!("
|
||||
push {free_type}
|
||||
push {free_size}
|
||||
push {base}
|
||||
|
||||
mov eax, fs:[30h]
|
||||
mov eax, [eax+8h]
|
||||
add eax, {off_fn}
|
||||
mov [eax-{off_fn}+{off_ptr}], eax
|
||||
|
||||
push eax
|
||||
|
||||
jmp {virtual_free}
|
||||
",
|
||||
off_ptr = const OffPtr,
|
||||
off_fn = const OffFn,
|
||||
|
||||
free_size = const 0,
|
||||
free_type = const MEM_RELEASE,
|
||||
|
||||
virtual_free = sym VirtualFree,
|
||||
|
||||
base = sym UNMAP_BASE,
|
||||
options(noreturn),
|
||||
);
|
||||
}
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
!0..122 'builti...muto,)': ()
|
||||
!0..190 'builti...tack))': ()
|
||||
!0..449 'builti...urn),)': !
|
||||
10..1254 '{ ... } }': ()
|
||||
16..1252 'unsafe... }': ()
|
||||
37..40 'foo': i32
|
||||
43..44 '1': i32
|
||||
58..63 'mut o': i32
|
||||
66..67 '0': i32
|
||||
281..282 'o': i32
|
||||
296..305 'thread_id': usize
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue