use crate::Error::InternalError;
use crate::Result;
use cranelift::codegen::ir::{BlockArg, Value};
use cranelift::frontend::FunctionBuilder;
use cranelift::prelude::{Type, types};
const POINTER_SIZE: i32 = 8;
pub struct OperandStack {
stack: Vec<Value>,
}
impl OperandStack {
pub fn with_capacity(capacity: u16) -> Self {
OperandStack {
stack: Vec::with_capacity(usize::from(capacity)),
}
}
pub fn push(&mut self, value: Value) -> Result<()> {
if self.stack.len() >= self.stack.capacity() {
return Err(InternalError("OperandStack overflow".to_string()));
}
self.stack.push(value);
Ok(())
}
fn push_type(
&mut self,
function_builder: &mut FunctionBuilder,
expected_type: Type,
value: Value,
) -> Result<()> {
let value_type = function_builder.func.dfg.value_type(value);
if value_type != expected_type {
return Err(InternalError(format!(
"Expected {expected_type:?}, found {value_type:?}",
)));
}
self.push(value)
}
pub fn push_int(&mut self, function_builder: &mut FunctionBuilder, value: Value) -> Result<()> {
self.push_type(function_builder, types::I32, value)
}
pub fn push_long(
&mut self,
function_builder: &mut FunctionBuilder,
value: Value,
) -> Result<()> {
self.push_type(function_builder, types::I64, value)
}
pub fn push_float(
&mut self,
function_builder: &mut FunctionBuilder,
value: Value,
) -> Result<()> {
self.push_type(function_builder, types::F32, value)
}
pub fn push_double(
&mut self,
function_builder: &mut FunctionBuilder,
value: Value,
) -> Result<()> {
self.push_type(function_builder, types::F64, value)
}
pub fn pop(&mut self) -> Result<Value> {
let Some(value) = self.stack.pop() else {
return Err(InternalError("OperandStack underflow".to_string()));
};
Ok(value)
}
fn pop_type(
&mut self,
function_builder: &mut FunctionBuilder,
expected_type: Type,
) -> Result<Value> {
let value = self.pop()?;
let value_type = function_builder.func.dfg.value_type(value);
if value_type != expected_type {
return Err(InternalError(format!(
"Expected {expected_type:?}, found {value_type:?}",
)));
}
Ok(value)
}
pub fn pop_int(&mut self, function_builder: &mut FunctionBuilder) -> Result<Value> {
self.pop_type(function_builder, types::I32)
}
pub fn pop_long(&mut self, function_builder: &mut FunctionBuilder) -> Result<Value> {
self.pop_type(function_builder, types::I64)
}
pub fn pop_float(&mut self, function_builder: &mut FunctionBuilder) -> Result<Value> {
self.pop_type(function_builder, types::F32)
}
pub fn pop_double(&mut self, function_builder: &mut FunctionBuilder) -> Result<Value> {
self.pop_type(function_builder, types::F64)
}
pub fn len(&self) -> usize {
self.stack.len()
}
pub fn is_empty(&self) -> bool {
self.stack.is_empty()
}
pub fn as_slice(&self) -> &[Value] {
&self.stack
}
pub fn as_block_arguments(&self) -> Vec<BlockArg> {
self.stack
.iter()
.map(|value| BlockArg::Value(*value))
.collect::<Vec<BlockArg>>()
}
pub fn to_type_vec(&self, function_builder: &FunctionBuilder) -> Vec<Type> {
self.stack
.iter()
.map(|value| function_builder.func.dfg.value_type(*value))
.collect()
}
pub fn reset(&mut self, function_builder: &mut FunctionBuilder) -> Result<()> {
let Some(current_block) = function_builder.current_block() else {
return Err(InternalError("No current block".to_string()));
};
let values = function_builder.block_params(current_block);
self.stack.clear();
self.stack.extend(values);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::create_function_builder_contexts;
use cranelift::frontend::FunctionBuilder;
use cranelift::prelude::InstBuilder;
#[test]
fn test_push_and_pop() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let value = function_builder.ins().iconst(types::I32, 42);
assert!(operand_stack.is_empty());
operand_stack.push(value)?;
assert_eq!(operand_stack.len(), 1);
assert_eq!(operand_stack.pop()?, value);
assert!(operand_stack.is_empty());
Ok(())
}
#[test]
fn test_push_overflow() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let value = function_builder.ins().iconst(types::I32, 42);
assert!(operand_stack.is_empty());
operand_stack.push(value)?;
assert_eq!(operand_stack.len(), 1);
assert!(operand_stack.push(value).is_err());
Ok(())
}
#[test]
fn test_push_invalid_type() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let value = function_builder.ins().iconst(types::I32, 42);
assert!(operand_stack.is_empty());
assert!(
operand_stack
.push_type(&mut function_builder, types::F32, value)
.is_err()
);
Ok(())
}
#[test]
fn test_push_and_pop_int() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let value = function_builder.ins().iconst(types::I32, 42);
assert!(operand_stack.is_empty());
operand_stack.push_int(&mut function_builder, value)?;
assert_eq!(operand_stack.len(), 1);
assert_eq!(operand_stack.pop_int(&mut function_builder)?, value);
assert!(operand_stack.is_empty());
Ok(())
}
#[test]
fn test_push_and_pop_long() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let value = function_builder.ins().iconst(types::I64, 42);
assert!(operand_stack.is_empty());
operand_stack.push_long(&mut function_builder, value)?;
assert_eq!(operand_stack.len(), 1);
assert_eq!(operand_stack.pop_long(&mut function_builder)?, value);
assert!(operand_stack.is_empty());
Ok(())
}
#[test]
fn test_push_and_pop_float() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let value = function_builder.ins().f32const(42.1);
assert!(operand_stack.is_empty());
operand_stack.push_float(&mut function_builder, value)?;
assert_eq!(operand_stack.len(), 1);
assert_eq!(operand_stack.pop_float(&mut function_builder)?, value);
assert!(operand_stack.is_empty());
Ok(())
}
#[test]
fn test_push_and_pop_double() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let value = function_builder.ins().f64const(42.1);
assert!(operand_stack.is_empty());
operand_stack.push_double(&mut function_builder, value)?;
assert_eq!(operand_stack.len(), 1);
assert_eq!(operand_stack.pop_double(&mut function_builder)?, value);
assert!(operand_stack.is_empty());
Ok(())
}
#[test]
fn test_pop_underflow() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
assert!(operand_stack.is_empty());
assert!(operand_stack.pop().is_err());
Ok(())
}
#[test]
fn test_pop_invalid_type() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let value = function_builder.ins().iconst(types::I32, 42);
operand_stack.push(value)?;
assert!(operand_stack.pop_long(&mut function_builder).is_err());
Ok(())
}
#[test]
fn test_as_slice() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(4);
let int_value = function_builder.ins().iconst(types::I32, 42);
let long_value = function_builder.ins().iconst(types::I64, 42);
let float_value = function_builder.ins().f32const(42.1);
let double_value = function_builder.ins().f64const(42.1);
operand_stack.push(int_value)?;
operand_stack.push(long_value)?;
operand_stack.push(float_value)?;
operand_stack.push(double_value)?;
assert_eq!(
operand_stack.as_slice(),
&[int_value, long_value, float_value, double_value]
);
Ok(())
}
#[test]
fn test_as_block_arguments() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(4);
let int_value = function_builder.ins().iconst(types::I32, 42);
let long_value = function_builder.ins().iconst(types::I64, 42);
let float_value = function_builder.ins().f32const(42.1);
let double_value = function_builder.ins().f64const(42.1);
operand_stack.push(int_value)?;
operand_stack.push(long_value)?;
operand_stack.push(float_value)?;
operand_stack.push(double_value)?;
assert_eq!(
operand_stack.as_block_arguments(),
&[
BlockArg::Value(int_value),
BlockArg::Value(long_value),
BlockArg::Value(float_value),
BlockArg::Value(double_value),
]
);
Ok(())
}
#[test]
fn test_to_type_vec() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(4);
let int_value = function_builder.ins().iconst(types::I32, 42);
let long_value = function_builder.ins().iconst(types::I64, 42);
let float_value = function_builder.ins().f32const(42.1);
let double_value = function_builder.ins().f64const(42.1);
operand_stack.push(int_value)?;
operand_stack.push(long_value)?;
operand_stack.push(float_value)?;
operand_stack.push(double_value)?;
let types = operand_stack.to_type_vec(&function_builder);
assert_eq!(types.len(), 4);
assert_eq!(types, vec![types::I32, types::I64, types::F32, types::F64]);
Ok(())
}
#[test]
fn test_reset() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let block = function_builder.create_block();
function_builder.switch_to_block(block);
let mut operand_stack = OperandStack::with_capacity(1);
let int_value = function_builder.ins().iconst(types::I32, 42);
operand_stack.push(int_value)?;
assert_eq!(operand_stack.len(), 1);
operand_stack.reset(&mut function_builder)?;
assert!(operand_stack.is_empty());
Ok(())
}
#[test]
fn test_reset_no_current_block_error() -> Result<()> {
let (mut module_context, mut function_context) = create_function_builder_contexts()?;
let mut function_builder =
FunctionBuilder::new(&mut module_context.func, &mut function_context);
let mut operand_stack = OperandStack::with_capacity(1);
assert!(operand_stack.reset(&mut function_builder).is_err());
Ok(())
}
}