1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
//! The base descriptor for various values within the IR.
//!
//! [`Value`]s can be function arguments, constants and instructions. [`Instruction`]s generally
//! refer to each other and to constants via the [`Value`] wrapper.
//!
//! Like most IR data structures they are `Copy` and cheap to pass around by value. They are
//! therefore also easy to replace, a common practise for optimization passes.
use crate::{constant::Constant, context::Context, instruction::Instruction, irtype::Type};
/// A wrapper around an [ECS](https://github.com/fitzgen/generational-arena) handle into the
/// [`Context`].
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct Value(pub generational_arena::Index);
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum ValueContent {
Argument(Type),
Constant(Constant),
Instruction(Instruction),
}
impl Value {
/// Return a new argument [`Value`].
pub fn new_argument(context: &mut Context, ty: Type) -> Value {
let content = ValueContent::Argument(ty);
Value(context.values.insert(content))
}
/// Return a new constant [`Value`].
pub fn new_constant(context: &mut Context, constant: Constant) -> Value {
let content = ValueContent::Constant(constant);
Value(context.values.insert(content))
}
/// Return a new instruction [`Value`].
pub fn new_instruction(context: &mut Context, instruction: Instruction) -> Value {
let content = ValueContent::Instruction(instruction);
Value(context.values.insert(content))
}
/// Return whether this is a constant value.
pub fn is_constant(&self, context: &Context) -> bool {
matches!(context.values[self.0], ValueContent::Constant(_))
}
/// Return whether this value is an instruction, and specifically a 'terminator'.
///
/// A terminator is always the last instruction in a block (and may not appear anywhere else)
/// and is either a branch or return.
pub fn is_terminator(&self, context: &Context) -> bool {
match &context.values[self.0] {
ValueContent::Instruction(ins) => matches!(
ins,
Instruction::Branch(_)
| Instruction::ConditionalBranch { .. }
| Instruction::Ret(_, _)
),
_ => false,
}
}
//pub fn get_constant(&self, context: &Context) -> Constant {
// if let ValueContent::Constant(c) = &context.values[self.0] {
// c.clone()
// } else {
// panic!("Value is not a constant.")
// }
//}
/// If this value is an instruction and if any of its parameters is `old_val` then replace them
/// with `new_val`.
pub fn replace_instruction_value(&self, context: &mut Context, old_val: Value, new_val: Value) {
if let ValueContent::Instruction(instruction) = &mut context.values.get_mut(self.0).unwrap()
{
instruction.replace_value(old_val, new_val);
}
}
/// Get the type for this value, if found.
///
/// Arguments and constants always have a type, but only some instructions do.
pub fn get_type(&self, context: &Context) -> Option<Type> {
match &context.values[self.0] {
ValueContent::Argument(ty) => Some(*ty),
ValueContent::Constant(c) => Some(c.ty),
ValueContent::Instruction(ins) => ins.get_type(context),
}
}
//pub fn is_bool_ty(&self, context: &Context) -> bool {
// self.get_type(context)
// .map(|ty| ty == Type::Bool)
// .unwrap_or(false)
//}
}