Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-ty/src/mir/eval/shim.rs')
-rw-r--r--crates/hir-ty/src/mir/eval/shim.rs186
1 files changed, 182 insertions, 4 deletions
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs
index 78938af15b..ac295b5837 100644
--- a/crates/hir-ty/src/mir/eval/shim.rs
+++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -51,6 +51,24 @@ impl Evaluator<'_> {
)?;
return Ok(true);
}
+ let is_extern_c = match def.lookup(self.db.upcast()).container {
+ hir_def::ItemContainerId::ExternBlockId(block) => {
+ let id = block.lookup(self.db.upcast()).id;
+ id.item_tree(self.db.upcast())[id.value].abi.as_deref() == Some("C")
+ }
+ _ => false,
+ };
+ if is_extern_c {
+ self.exec_extern_c(
+ function_data.name.as_text().unwrap_or_default().as_str(),
+ args,
+ generic_args,
+ destination,
+ &locals,
+ span,
+ )?;
+ return Ok(true);
+ }
let alloc_fn = function_data
.attrs
.iter()
@@ -72,7 +90,7 @@ impl Evaluator<'_> {
if let Some(x) = self.detect_lang_function(def) {
let arg_bytes =
args.iter().map(|x| Ok(x.get(&self)?.to_owned())).collect::<Result<Vec<_>>>()?;
- let result = self.exec_lang_item(x, &arg_bytes)?;
+ let result = self.exec_lang_item(x, generic_args, &arg_bytes, locals, span)?;
destination.write_from_bytes(self, &result)?;
return Ok(true);
}
@@ -118,13 +136,20 @@ impl Evaluator<'_> {
use LangItem::*;
let candidate = lang_attr(self.db.upcast(), def)?;
// We want to execute these functions with special logic
- if [PanicFmt, BeginPanic, SliceLen].contains(&candidate) {
+ if [PanicFmt, BeginPanic, SliceLen, DropInPlace].contains(&candidate) {
return Some(candidate);
}
None
}
- fn exec_lang_item(&self, x: LangItem, args: &[Vec<u8>]) -> Result<Vec<u8>> {
+ fn exec_lang_item(
+ &mut self,
+ x: LangItem,
+ generic_args: &Substitution,
+ args: &[Vec<u8>],
+ locals: &Locals<'_>,
+ span: MirSpan,
+ ) -> Result<Vec<u8>> {
use LangItem::*;
let mut args = args.iter();
match x {
@@ -139,10 +164,114 @@ impl Evaluator<'_> {
let ptr_size = arg.len() / 2;
Ok(arg[ptr_size..].into())
}
+ DropInPlace => {
+ let ty =
+ generic_args.as_slice(Interner).get(0).and_then(|x| x.ty(Interner)).ok_or(
+ MirEvalError::TypeError(
+ "generic argument of drop_in_place is not provided",
+ ),
+ )?;
+ let arg = args
+ .next()
+ .ok_or(MirEvalError::TypeError("argument of drop_in_place is not provided"))?;
+ self.run_drop_glue_deep(
+ ty.clone(),
+ locals,
+ Address::from_bytes(&arg[0..self.ptr_size()])?,
+ &arg[self.ptr_size()..],
+ span,
+ )?;
+ Ok(vec![])
+ }
x => not_supported!("Executing lang item {x:?}"),
}
}
+ fn exec_extern_c(
+ &mut self,
+ as_str: &str,
+ args: &[IntervalAndTy],
+ _generic_args: &Substitution,
+ destination: Interval,
+ locals: &Locals<'_>,
+ _span: MirSpan,
+ ) -> Result<()> {
+ match as_str {
+ "write" => {
+ let [fd, ptr, len] = args else {
+ return Err(MirEvalError::TypeError("libc::write args are not provided"));
+ };
+ let fd = u128::from_le_bytes(pad16(fd.get(self)?, false));
+ let interval = Interval {
+ addr: Address::from_bytes(ptr.get(self)?)?,
+ size: from_bytes!(usize, len.get(self)?),
+ };
+ match fd {
+ 1 => {
+ self.write_to_stdout(interval)?;
+ }
+ 2 => {
+ self.write_to_stderr(interval)?;
+ }
+ _ => not_supported!("write to arbitrary file descriptor"),
+ }
+ Ok(())
+ }
+ "pthread_key_create" => {
+ let key = self.thread_local_storage.create_key();
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_key_create arg0 is not provided"));
+ };
+ let arg0_addr = Address::from_bytes(arg0.get(self)?)?;
+ let key_ty = if let Some((ty, ..)) = arg0.ty.as_reference_or_ptr() {
+ ty
+ } else {
+ return Err(MirEvalError::TypeError(
+ "pthread_key_create arg0 is not a pointer",
+ ));
+ };
+ let arg0_interval = Interval::new(
+ arg0_addr,
+ self.size_of_sized(key_ty, locals, "pthread_key_create key arg")?,
+ );
+ arg0_interval.write_from_bytes(self, &key.to_le_bytes()[0..arg0_interval.size])?;
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_getspecific" => {
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_getspecific arg0 is not provided"));
+ };
+ let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]);
+ let value = self.thread_local_storage.get_key(key)?;
+ destination.write_from_bytes(self, &value.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_setspecific" => {
+ let Some(arg0) = args.get(0) else {
+ return Err(MirEvalError::TypeError("pthread_setspecific arg0 is not provided"));
+ };
+ let key = from_bytes!(usize, &pad16(arg0.get(self)?, false)[0..8]);
+ let Some(arg1) = args.get(1) else {
+ return Err(MirEvalError::TypeError("pthread_setspecific arg1 is not provided"));
+ };
+ let value = from_bytes!(u128, pad16(arg1.get(self)?, false));
+ self.thread_local_storage.set_key(key, value)?;
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ "pthread_key_delete" => {
+ // we ignore this currently
+ // return 0 as success
+ destination.write_from_bytes(self, &0u64.to_le_bytes()[0..destination.size])?;
+ Ok(())
+ }
+ _ => not_supported!("unknown external function {as_str}"),
+ }
+ }
+
fn exec_intrinsic(
&mut self,
as_str: &str,
@@ -288,7 +417,7 @@ impl Evaluator<'_> {
let ans = lhs.get(self)? == rhs.get(self)?;
destination.write_from_bytes(self, &[u8::from(ans)])
}
- "wrapping_add" => {
+ "wrapping_add" | "unchecked_add" => {
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("wrapping_add args are not provided"));
};
@@ -297,6 +426,39 @@ impl Evaluator<'_> {
let ans = lhs.wrapping_add(rhs);
destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
}
+ "wrapping_sub" | "unchecked_sub" | "ptr_offset_from_unsigned" | "ptr_offset_from" => {
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("wrapping_sub args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.wrapping_sub(rhs);
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "unchecked_rem" => {
+ // FIXME: signed
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("unchecked_rem args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.checked_rem(rhs).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned())
+ })?;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
+ "unchecked_div" | "exact_div" => {
+ // FIXME: signed
+ let [lhs, rhs] = args else {
+ return Err(MirEvalError::TypeError("unchecked_div args are not provided"));
+ };
+ let lhs = u128::from_le_bytes(pad16(lhs.get(self)?, false));
+ let rhs = u128::from_le_bytes(pad16(rhs.get(self)?, false));
+ let ans = lhs.checked_div(rhs).ok_or_else(|| {
+ MirEvalError::UndefinedBehavior("unchecked_rem with bad inputs".to_owned())
+ })?;
+ destination.write_from_bytes(self, &ans.to_le_bytes()[0..destination.size])
+ }
"add_with_overflow" => {
let [lhs, rhs] = args else {
return Err(MirEvalError::TypeError("const_eval_select args are not provided"));
@@ -373,6 +535,22 @@ impl Evaluator<'_> {
};
destination.write_from_interval(self, arg.interval)
}
+ "ctpop" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("likely arg is not provided"));
+ };
+ let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).count_ones();
+ destination
+ .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
+ }
+ "cttz" | "cttz_nonzero" => {
+ let [arg] = args else {
+ return Err(MirEvalError::TypeError("likely arg is not provided"));
+ };
+ let result = u128::from_le_bytes(pad16(arg.get(self)?, false)).trailing_zeros();
+ destination
+ .write_from_bytes(self, &(result as u128).to_le_bytes()[0..destination.size])
+ }
"const_eval_select" => {
let [tuple, const_fn, _] = args else {
return Err(MirEvalError::TypeError("const_eval_select args are not provided"));