implement more instructions and functions

This commit is contained in:
Jacob Lifshay 2026-04-03 19:07:28 -07:00
parent f64b5d7120
commit 2fa256098d
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ

View file

@ -30,7 +30,7 @@ use std::{
fmt,
marker::PhantomData,
mem,
num::NonZeroU32,
num::{NonZeroU32, NonZeroU128},
ops::{ControlFlow, IndexMut},
path::Path,
rc::Rc,
@ -109,6 +109,10 @@ declare_intrinsics! {
LifetimeEnd,
#[name = "llvm.memset"]
Memset,
#[name = "llvm.ctpop"]
CtPop,
#[name = "llvm.cttz"]
Cttz,
}
macro_rules! declare_known_functions {
@ -147,6 +151,10 @@ declare_known_functions! {
SysConf,
#[name = c"getauxval"]
GetAuxVal,
#[name = c"pthread_atfork"]
PThreadAtFork,
#[name = c"pthread_mutex_init"]
PThreadMutexInit,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@ -197,15 +205,36 @@ impl TargetLayout {
}
i += 1;
}
let Some(size) = retval
.size
.checked_next_multiple_of(retval.align.get() as u64)
else {
panic!("struct too big");
};
retval.size = size;
retval.round_size_up_to_multiple_of_align();
retval
}
#[track_caller]
const fn round_size_up_to_multiple_of_align(&mut self) {
let Some(size) = self.size.checked_next_multiple_of(self.align.get() as u64) else {
panic!("type too big");
};
self.size = size;
}
#[must_use]
#[track_caller]
const fn with_min_align(mut self, min_align: NonZeroU32) -> Self {
if self.align.get() >= min_align.get() {
return self;
}
self.align = min_align;
self.round_size_up_to_multiple_of_align();
self
}
#[must_use]
#[track_caller]
const fn with_min_size(mut self, min_size: u64) -> Self {
if self.size >= min_size {
return self;
}
self.size = min_size;
self.round_size_up_to_multiple_of_align();
self
}
}
trait TargetType {
@ -557,12 +586,14 @@ impl<Struct: TargetStruct, Field: TargetType, const INDEX: usize, const OFFSET:
trait TargetStruct: TargetType {
const FIELD_LAYOUTS: &[TargetLayout];
const FIELD_OFFSETS: &[u64];
const MIN_ALIGN: NonZeroU32;
const MIN_SIZE: u64;
}
macro_rules! declare_target_struct {
(
#[field_ints = $FieldInts:ident, value = $Value:ident]
#[repr(C)]
#[repr(C$(, align($min_align:literal))?$(, min_size($min_size:literal))?)]
struct $Struct:ident {
$($field:ident: $field_ty:ty,)*
}
@ -604,9 +635,17 @@ macro_rules! declare_target_struct {
TargetLayout::struct_layout(Self::FIELD_LAYOUTS, &mut offsets);
offsets
};
const MIN_ALIGN: NonZeroU32 = {
NonZeroU32::new(($($min_align,)? 1,).0).unwrap()
};
const MIN_SIZE: u64 = {
($($min_size,)? 0,).0
};
}
impl TargetType for $Struct {
const LAYOUT: TargetLayout = TargetLayout::struct_layout(Self::FIELD_LAYOUTS, &mut []);
const LAYOUT: TargetLayout = TargetLayout::struct_layout(Self::FIELD_LAYOUTS, &mut [])
.with_min_align(Self::MIN_ALIGN)
.with_min_size(Self::MIN_SIZE);
type Value = $Value;
fn llvm_type<'ctx>(parser: &Parser<'ctx>) -> AnyTypeEnum<'ctx> {
parser.context.struct_type(&[$(<$field_ty as TargetType>::llvm_basic_type(parser).expect("invalid field type")),*], false).into()
@ -703,6 +742,30 @@ declare_target_struct! {
}
}
declare_target_struct! {
#[field_ints = TargetPThreadListTFieldInts, value = TargetPThreadListTValue]
#[repr(C, align(8), min_size(24))]
struct TargetPThreadListT {
prev: *mut TargetPThreadListT,
next: *mut TargetPThreadListT,
}
}
declare_target_struct! {
#[field_ints = TargetPThreadMutexTFieldInts, value = TargetPThreadMutexTValue]
#[repr(C, align(8), min_size(24))]
struct TargetPThreadMutexT {
lock: i32,
count: u32,
owner: i32,
nusers: u32,
kind: i32,
spins: i16,
elision: i16,
list: TargetPThreadListT,
}
}
fn get_called_value<'ctx>(instruction: InstructionValue<'ctx>) -> AnyValueEnum<'ctx> {
match instruction.get_opcode() {
InstructionOpcode::Call | InstructionOpcode::Invoke => {}
@ -1693,6 +1756,20 @@ impl<'ctx> State<'ctx> {
}
Ok(())
}
Intrinsic::CtPop => {
self.int_un_op_unsigned(|_, src| Ok(src.count_ones() as u128))?;
Ok(())
}
Intrinsic::Cttz => {
self.int_un_op_unsigned(|bit_width, src| {
Ok(if let Some(src) = NonZeroU128::new(src) {
src.trailing_zeros()
} else {
bit_width
} as u128)
})?;
Ok(())
}
Intrinsic::Unknown(_) => todo!("{function_value}"),
}
}
@ -1800,6 +1877,49 @@ impl<'ctx> State<'ctx> {
)?;
Ok(())
}
fn run_call_pthread_atfork(&mut self) -> Result<()> {
// we don't implement forking, so ignore the passed-in function pointers and just return 0
self.insert_local_value(
self.next_instruction.as_any_value_enum(),
Value::ConstantInt(0 as u128),
)?;
Ok(())
}
fn run_call_pthread_mutex_init(&mut self) -> Result<()> {
let [mutex, mutexattr] = self.get_call_arg_values()?;
let Value::Pointer(mutex) = mutex else {
bail!("pthread_mutex_init `mutex` argument must be a pointer");
};
let Value::Pointer(mutexattr) = mutexattr else {
bail!("pthread_mutex_init `mutexattr` argument must be a pointer");
};
if mutexattr.base.address() != Some(0) {
todo!("pthread_mutex_init with non-null `mutexattr`");
};
let null_ptr = Pointer {
base: self.global.null_ptr_base.clone(),
offset: 0,
};
let init = TargetPThreadMutexTValue {
lock: 0,
count: 0,
owner: 0,
nusers: 0,
kind: 0,
spins: 0,
elision: 0,
list: TargetPThreadListTValue {
prev: null_ptr.clone(),
next: null_ptr,
},
};
TargetPThreadMutexT::store(self, mutex, init)?;
self.insert_local_value(
self.next_instruction.as_any_value_enum(),
Value::ConstantInt(0),
)?;
Ok(())
}
fn run_call_known_function(
&mut self,
function_value: FunctionValue<'ctx>,
@ -1810,6 +1930,8 @@ impl<'ctx> State<'ctx> {
KnownFunction::ClockGetTime => self.run_call_clock_gettime(),
KnownFunction::SysConf => self.run_call_sysconf(),
KnownFunction::GetAuxVal => self.run_call_getauxval(),
KnownFunction::PThreadAtFork => self.run_call_pthread_atfork(),
KnownFunction::PThreadMutexInit => self.run_call_pthread_mutex_init(),
}
}
fn run_normal_call(mut self: Rc<Self>, function: FunctionValue<'ctx>) -> Result<Rc<Self>> {
@ -1920,6 +2042,51 @@ impl<'ctx> State<'ctx> {
Ok(op(bit_width, lhs, rhs)? as u128)
})
}
fn int_un_op_unmasked_inputs(
&mut self,
op: impl FnOnce(u32, u128) -> Result<u128>,
) -> Result<()> {
let src = self
.next_instruction
.get_operand(0)
.expect("known to have src operand")
.unwrap_value();
let bit_width = src.get_type().into_int_type().get_bit_width();
let src = self.get_value(src)?;
let Value::ConstantInt(src) = src else {
todo!("{src:?}");
};
let mut retval = op(bit_width, src)?;
let dest_bit_width = self
.next_instruction
.as_any_value_enum()
.into_int_value()
.get_type()
.get_bit_width();
retval &= 1u128.unbounded_shl(dest_bit_width).wrapping_sub(1);
self.insert_local_value(
self.next_instruction.as_any_value_enum(),
Value::ConstantInt(retval),
)
}
fn int_un_op_unsigned(&mut self, op: impl FnOnce(u32, u128) -> Result<u128>) -> Result<()> {
self.int_un_op_unmasked_inputs(|bit_width, src| {
let shift = u128::BITS
.checked_sub(bit_width)
.expect("bit width too big");
let src = (src << shift) >> shift;
op(bit_width, src)
})
}
fn int_un_op_signed(&mut self, op: impl FnOnce(u32, i128) -> Result<i128>) -> Result<()> {
self.int_un_op_unmasked_inputs(|bit_width, src| {
let shift = i128::BITS
.checked_sub(bit_width)
.expect("bit width too big");
let src = ((src as i128) << shift) >> shift;
Ok(op(bit_width, src)? as u128)
})
}
fn get_next_states(
mut self: Rc<Self>,
new_states: &mut Vec<Rc<Self>>,
@ -2231,7 +2398,40 @@ impl<'ctx> State<'ctx> {
}
}
InstructionOpcode::SDiv => todo!("{}", this.next_instruction),
InstructionOpcode::Select => todo!("{}", this.next_instruction),
InstructionOpcode::Select => {
let cond = this
.next_instruction
.get_operand(0)
.expect("known to have condition operand")
.unwrap_value();
let cond = this.get_value(cond)?;
let true_value = this
.next_instruction
.get_operand(1)
.expect("known to have true_value operand")
.unwrap_value();
let true_value = this.get_value(true_value)?;
let false_value = this
.next_instruction
.get_operand(2)
.expect("known to have false_value operand")
.unwrap_value();
let false_value = this.get_value(false_value)?;
let value = if let Value::ConstantInt(cond) = cond {
if cond & 1 != 0 {
true_value
} else {
false_value
}
} else {
todo!(
"unimplemented select condition: {cond:?}\n{}",
this.next_instruction
)
};
this.insert_local_value(this.next_instruction.as_any_value_enum(), value)?;
return_after_non_term!();
}
InstructionOpcode::SExt | InstructionOpcode::Trunc | InstructionOpcode::ZExt => {
let value = this
.next_instruction
@ -2303,7 +2503,10 @@ impl<'ctx> State<'ctx> {
InstructionOpcode::UserOp1 => todo!("{}", this.next_instruction),
InstructionOpcode::UserOp2 => todo!("{}", this.next_instruction),
InstructionOpcode::VAArg => todo!("{}", this.next_instruction),
InstructionOpcode::Xor => todo!("{}", this.next_instruction),
InstructionOpcode::Xor => {
this.int_bin_op_unsigned(|_, lhs, rhs| Ok(lhs ^ rhs))?;
return_after_non_term!();
}
}
}
fn run_states(