pd-vm 0.22.2

RustScript bytecode compiler and VM
Documentation
use super::*;

#[derive(Clone, Copy, Debug)]
enum ScalarValue {
    Int(i64),
    Float(f64),
}

impl ScalarValue {
    #[inline(always)]
    fn from_value(value: &Value) -> Option<Self> {
        match value {
            Value::Int(value) => Some(Self::Int(*value)),
            Value::Float(value) => Some(Self::Float(*value)),
            _ => None,
        }
    }

    #[inline(always)]
    fn into_value(self) -> Value {
        match self {
            Self::Int(value) => Value::Int(value),
            Self::Float(value) => Value::Float(value),
        }
    }
}

impl Vm {
    #[inline(always)]
    fn local_scalar_value_with_hint(&mut self, index: u8) -> Option<ScalarValue> {
        match self.local_type_hint(index) {
            ValueType::Int => {
                let value = match self.locals.get(index as usize)? {
                    Value::Int(value) => *value,
                    _ => return None,
                };
                self.record_local_type_hint_hit();
                Some(ScalarValue::Int(value))
            }
            ValueType::Float => {
                let value = match self.locals.get(index as usize)? {
                    Value::Float(value) => *value,
                    _ => return None,
                };
                self.record_local_type_hint_hit();
                Some(ScalarValue::Float(value))
            }
            _ => self.local_numeric_value(index).map(|value| match value {
                NumericValue::Int(value) => ScalarValue::Int(value),
                NumericValue::Float(value) => ScalarValue::Float(value),
            }),
        }
    }

    #[inline(always)]
    pub(super) fn decoded_ldc_value_at(&self, opcode_ip: usize) -> Option<&Value> {
        self.decoded_instruction_data
            .ldc_values
            .get(opcode_ip)
            .and_then(|value| value.as_ref())
    }

    #[inline(always)]
    pub(super) fn decoded_jump_target_at(&self, opcode_ip: usize) -> Option<usize> {
        self.decoded_instruction_data
            .jump_targets
            .get(opcode_ip)
            .and_then(|target| *target)
    }

    #[inline(always)]
    pub(super) fn decoded_local_index_at(&self, opcode_ip: usize) -> Option<u8> {
        self.decoded_instruction_data
            .local_indices
            .get(opcode_ip)
            .and_then(|index| *index)
    }

    #[inline(always)]
    pub(super) fn try_fuse_scalar_sequence(
        &mut self,
        src: u8,
        allow_superinstructions: bool,
    ) -> VmResult<bool> {
        if !allow_superinstructions {
            return Ok(false);
        }
        let Some(initial) = self.local_scalar_value_with_hint(src) else {
            return Ok(false);
        };
        let mut cursor = self.ip;
        let mut stack = [None; 8];
        let mut stack_len = 1usize;
        stack[0] = Some(initial);
        let mut steps = 0usize;
        while cursor < self.program.code.len() && steps < 16 {
            let opcode = match self.program.code.get(cursor).copied() {
                Some(raw) => match OpCode::try_from(raw) {
                    Ok(opcode) => opcode,
                    Err(_) => return Ok(false),
                },
                None => return Ok(false),
            };
            match opcode {
                OpCode::Ldc => {
                    let Some(value) = self
                        .decoded_ldc_value_at(cursor)
                        .and_then(ScalarValue::from_value)
                    else {
                        return Ok(false);
                    };
                    if stack_len == stack.len() {
                        return Ok(false);
                    }
                    stack[stack_len] = Some(value);
                    stack_len += 1;
                    cursor += 5;
                }
                OpCode::Ldloc => {
                    let Some(index) = self.decoded_local_index_at(cursor) else {
                        return Ok(false);
                    };
                    let Some(value) = self.local_scalar_value_with_hint(index) else {
                        return Ok(false);
                    };
                    if stack_len == stack.len() {
                        return Ok(false);
                    }
                    stack[stack_len] = Some(value);
                    stack_len += 1;
                    cursor += 2;
                }
                OpCode::Add
                | OpCode::Sub
                | OpCode::Mul
                | OpCode::Div
                | OpCode::Mod
                | OpCode::Shl => {
                    if stack_len < 2 {
                        return Ok(false);
                    }
                    let rhs = stack[stack_len - 1]
                        .take()
                        .expect("rhs scalar should exist");
                    let lhs = stack[stack_len - 2]
                        .take()
                        .expect("lhs scalar should exist");
                    stack_len -= 2;
                    let lhs_f = match lhs {
                        ScalarValue::Int(value) => value as f64,
                        ScalarValue::Float(value) => value,
                    };
                    let rhs_f = match rhs {
                        ScalarValue::Int(value) => value as f64,
                        ScalarValue::Float(value) => value,
                    };
                    let result = match opcode {
                        OpCode::Add => match (lhs, rhs) {
                            (ScalarValue::Int(lhs), ScalarValue::Int(rhs)) => {
                                ScalarValue::Int(lhs.wrapping_add(rhs))
                            }
                            _ => ScalarValue::Float(lhs_f + rhs_f),
                        },
                        OpCode::Sub => match (lhs, rhs) {
                            (ScalarValue::Int(lhs), ScalarValue::Int(rhs)) => {
                                ScalarValue::Int(lhs.wrapping_sub(rhs))
                            }
                            _ => ScalarValue::Float(lhs_f - rhs_f),
                        },
                        OpCode::Mul => match (lhs, rhs) {
                            (ScalarValue::Int(lhs), ScalarValue::Int(rhs)) => {
                                ScalarValue::Int(lhs.wrapping_mul(rhs))
                            }
                            _ => ScalarValue::Float(lhs_f * rhs_f),
                        },
                        OpCode::Div => match (lhs, rhs) {
                            (ScalarValue::Int(lhs), ScalarValue::Int(rhs)) => {
                                ScalarValue::Int(checked_int_div(lhs, rhs)?)
                            }
                            _ => ScalarValue::Float(lhs_f / rhs_f),
                        },
                        OpCode::Mod => match (lhs, rhs) {
                            (ScalarValue::Int(lhs), ScalarValue::Int(rhs)) => {
                                ScalarValue::Int(checked_int_rem(lhs, rhs)?)
                            }
                            _ => ScalarValue::Float(lhs_f % rhs_f),
                        },
                        OpCode::Shl => match (lhs, rhs) {
                            (ScalarValue::Int(lhs), ScalarValue::Int(rhs)) => {
                                if !(0..=63).contains(&rhs) {
                                    return Err(VmError::InvalidShift(rhs));
                                }
                                ScalarValue::Int(lhs.wrapping_shl(rhs as u32))
                            }
                            _ => return Ok(false),
                        },
                        _ => unreachable!(),
                    };
                    stack[stack_len] = Some(result);
                    stack_len += 1;
                    cursor += 1;
                }
                OpCode::Stloc => {
                    if stack_len != 1 {
                        return Ok(false);
                    }
                    let Some(dst) = self.decoded_local_index_at(cursor) else {
                        return Ok(false);
                    };
                    let value = stack[0]
                        .take()
                        .expect("result scalar should exist")
                        .into_value();
                    self.store_local_with_drop_contract(dst, value)?;
                    self.record_scalar_superinstruction();
                    self.ip = cursor + 2;
                    return Ok(true);
                }
                OpCode::Clt | OpCode::Cgt => {
                    if stack_len != 2 {
                        return Ok(false);
                    }
                    if self.program.code.get(cursor + 1).copied() != Some(OpCode::Brfalse as u8) {
                        return Ok(false);
                    }
                    let Some(target) = self.decoded_jump_target_at(cursor + 1) else {
                        return Ok(false);
                    };
                    let rhs = stack[stack_len - 1]
                        .take()
                        .expect("rhs scalar should exist");
                    let lhs = stack[stack_len - 2]
                        .take()
                        .expect("lhs scalar should exist");
                    let lhs_f = match lhs {
                        ScalarValue::Int(value) => value as f64,
                        ScalarValue::Float(value) => value,
                    };
                    let rhs_f = match rhs {
                        ScalarValue::Int(value) => value as f64,
                        ScalarValue::Float(value) => value,
                    };
                    let condition = match opcode {
                        OpCode::Clt => match (lhs, rhs) {
                            (ScalarValue::Int(lhs), ScalarValue::Int(rhs)) => lhs < rhs,
                            _ => lhs_f < rhs_f,
                        },
                        OpCode::Cgt => match (lhs, rhs) {
                            (ScalarValue::Int(lhs), ScalarValue::Int(rhs)) => lhs > rhs,
                            _ => lhs_f > rhs_f,
                        },
                        _ => unreachable!(),
                    };
                    self.ip = cursor + 6;
                    if !condition {
                        self.jump_to(target)?;
                    }
                    self.record_scalar_superinstruction();
                    return Ok(true);
                }
                _ => return Ok(false),
            }
            steps += 1;
        }
        Ok(false)
    }
}