use crate::{error::Error, ident};
use super::FeltConstAttribute;
use melior::ir::{
Location, Operation, Type, Value, ValueLike as _,
operation::{OperationBuilder, OperationLike},
};
fn build_op<'c>(
name: &str,
location: Location<'c>,
result: Type<'c>,
operands: &[Value<'c, '_>],
) -> Result<Operation<'c>, Error> {
OperationBuilder::new(format!("felt.{name}").as_str(), location)
.add_results(&[result])
.add_operands(operands)
.build()
.map_err(Into::into)
}
macro_rules! binop {
($name:ident) => {
binop!($name, stringify!($name));
};
($name:ident, $opname:expr) => {
#[doc = concat!("Creates a `felt.", $opname ,"` operation.")]
pub fn $name<'c>(
location: Location<'c>,
lhs: Value<'c, '_>,
rhs: Value<'c, '_>,
) -> Result<Operation<'c>, Error> {
build_op($opname, location, lhs.r#type(), &[lhs, rhs])
}
paste::paste! {
#[doc = concat!("Return `true` iff the given op is `felt.", $opname ,"`.")]
#[inline]
pub fn [<is_felt_ $name>]<'c: 'a, 'a>(op: &impl OperationLike<'c, 'a>) -> bool {
crate::operation::isa(op, concat!("felt.", $opname))
}
}
};
}
macro_rules! unop {
($name:ident) => {
unop!($name, stringify!($name));
};
($name:ident, $opname:expr) => {
#[doc = concat!("Creates a `felt.", $opname ,"` operation.")]
pub fn $name<'c>(
location: Location<'c>,
value: Value<'c, '_>,
) -> Result<Operation<'c>, Error> {
build_op($opname, location, value.r#type(), &[value])
}
paste::paste! {
#[doc = concat!("Return `true` iff the given op is `felt.", $opname ,"`.")]
#[inline]
pub fn [<is_felt_ $name>]<'c: 'a, 'a>(op: &impl OperationLike<'c, 'a>) -> bool {
crate::operation::isa(op, concat!("felt.", $opname))
}
}
};
}
binop!(add);
binop!(bit_and);
binop!(bit_or);
binop!(bit_xor);
binop!(div);
binop!(mul);
binop!(pow);
binop!(shl);
binop!(shr);
binop!(sintdiv);
binop!(smod);
binop!(sub);
binop!(uintdiv);
binop!(umod);
unop!(bit_not);
unop!(inv);
unop!(neg);
pub fn constant<'c>(
location: Location<'c>,
value: FeltConstAttribute<'c>,
) -> Result<Operation<'c>, Error> {
let ctx = location.context();
OperationBuilder::new("felt.const", location)
.add_results(&[value.r#type().into()])
.add_attributes(&[(ident!(ctx, "value"), value.into())])
.build()
.map_err(Into::into)
}
#[inline]
pub fn is_felt_const<'c: 'a, 'a>(op: &impl OperationLike<'c, 'a>) -> bool {
crate::operation::isa(op, "felt.const")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
use quickcheck_macros::quickcheck;
#[quickcheck]
fn felt_const_op(value: u64) {
let value = value % (i64::MAX as u64 + 1);
let ctx = LlzkContext::new();
let op = constant(
Location::unknown(&ctx),
FeltConstAttribute::new(&ctx, value, None),
)
.unwrap();
assert!(op.verify(), "operation {op:?} failed verification");
}
#[quickcheck]
fn felt_const_op_isa(value: u64) {
let value = value % (i64::MAX as u64 + 1);
let ctx = LlzkContext::new();
let op = constant(
Location::unknown(&ctx),
FeltConstAttribute::new(&ctx, value, None),
)
.unwrap();
assert!(is_felt_const(&op), "operation {op:?} failed isa test");
}
}