Lower asm expressions

This commit is contained in:
Lukas Wirth 2024-09-01 15:17:52 +02:00
parent 86658c66b4
commit 3b11ff8c4d
14 changed files with 612 additions and 97 deletions

View file

@ -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);

View file

@ -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

View file

@ -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);

View file

@ -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
"#]],
)
}