Sorry for the vague title, trying to find an efficient way of doing something.
Optimization phase of a compiler, in the constant folding pass. I have a bunch of operations on a bunch of values. If both operands (for a binary op) are constants then the operation can be replaced a constant (int x = 4 + 5
for example). I can obviously make a huge set of multiply nested matches but it seems like I could simplify it
the values are
pub enum Value {
Int32(i32),
Int64(i64),
UInt32(u32),
UInt64(u64),
Double(f64),
Char(i8),
UChar(u8),
String(String),
Variable(String, SymbolType),
Void,
}
the first 7 are all constants of the given type. The binary operators are (I also have Unary ops)
pub enum BinaryOperator {
Add,
Subtract,
Multiply,
Divide,
Remainder,
BitAnd,
BitOr,
BitXor,
ShiftLeft,
ShiftRight,
Equal,
NotEqual,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
}
so for example if the operator is Add and the Values are int32 I need to do (using EnumAsInner for the as_xxx
calls)
let new_constant = Value::Int32(left.as_int32().unwrap().wrapping_add right.as_int32().unwrap()));
(Note the wrapping add)
I wrote this macro
macro_rules! binop_num {
($left:ident,$right:ident, $op:path) => {
match $left.stype() {
SymbolType::Int32 => {
Value::Int32($op($left.as_int32().unwrap(), $right.as_int32().unwrap()))
}
SymbolType::UInt32 => Value::UInt32($op(
$left.as_u_int32().unwrap(),
$right.as_u_int32().unwrap(),
)),
SymbolType::Int64 => {
Value::Int64($op($left.as_int64().unwrap(), $right.as_int64().unwrap()))
}
SymbolType::UInt64 => Value::UInt64($op(
$left.as_u_int64().unwrap(),
$right.as_u_int64().unwrap(),
)),
// SymbolType::Double => {
// Value::Double($op($left.as_double().unwrap(), $right.as_double().unwrap()))
// }
SymbolType::Char | SymbolType::SChar => {
Value::Char($op($left.as_char().unwrap(), $right.as_char().unwrap()))
}
SymbolType::UChar => {
Value::UChar($op($left.as_u_char().unwrap(), $right.as_u_char().unwrap()))
}
_ => panic!("Unsupported type for binop: {:?}", $left.stype()),
}
};
}
used like this
let result = match op {
BinaryOperator::Add => {
binop_num!(left, right, WrappingAdd::wrapping_add)
}
BinaryOperator::Subtract => {
binop_num!(left, right, WrappingSub::wrapping_sub)
}
....
the problems are
- doesnt compile for double, because double doesnt support wrapping add or bitwise ops
- the logic ones are different - they need to return boolean or 0/1
- the shift operations right hand are always int32 (for other operations left and right are the same type)
Any suggestions about the best way to do this, like I said I can simply type out enormous matches for each combination of Instruction (BinaryOperator, UnaryOperator,..), sub op (BinaryOperator::Add, Unary::Negate,..) and value type