use alloc::{
string::{String, ToString},
vec,
};
use pliron::{
builtin::{
attributes::BoolAttr,
op_interfaces::{
NOpdsInterface, OneOpdInterface, ResultNOfType, SymbolOpInterface,
verify_get_operand_n, verify_get_result_n,
},
type_interfaces::FloatTypeInterface,
},
derive::op_interface,
dict_key,
printable::Printable,
r#type::{Type, TypeInterfaceHandle, TypeInterfaceMarker, TypedHandle, type_impls},
utils::const_bound_n::I,
};
use thiserror::Error;
use pliron::{
builtin::{
op_interfaces::{OneResultInterface, SameOperandsAndResultType},
types::{IntegerType, Signedness},
},
context::Context,
location::{Located, Location},
op::{Op, op_cast},
operation::Operation,
result::Result,
r#type::{TypeHandle, Typed},
value::Value,
verify_err,
};
use crate::{
attributes::{AlignmentAttr, FastmathFlagsAttr, SyncScopeAttr},
types::{VectorType, VectorTypeKind},
};
use super::{attributes::IntegerOverflowFlagsAttr, types::PointerType};
#[op_interface]
pub trait BinArithOp: SameOperandsAndResultType + OneResultInterface + NOpdsInterface<2> {
fn new(ctx: &mut Context, lhs: Value, rhs: Value) -> Self
where
Self: Sized,
{
let op = Operation::new(
ctx,
Self::get_concrete_op_info(),
vec![lhs.get_type(ctx)],
vec![lhs, rhs],
vec![],
0,
);
Self::from_operation(op)
}
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
fn lhs(&self, ctx: &Context) -> Value
where
Self: Sized,
{
self.get_operand_i(ctx, I::<0>.into())
}
fn rhs(&self, ctx: &Context) -> Value
where
Self: Sized,
{
self.get_operand_i(ctx, I::<1>.into())
}
}
#[derive(Error, Debug)]
#[error("Integer binary arithmetic Op can only have signless integer result/operand type")]
pub struct IntBinArithOpErr;
#[op_interface]
pub trait IntBinArithOp: BinArithOp + ScalarOrVectorOpd<IntegerType, 0> {
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let int_ty = op_cast::<dyn ScalarOrVectorOpd<IntegerType, 0>>(op)
.expect("Op must impl ScalarOrVectorOpd<IntegerType, 0>")
.scalar_or_vector_elem_ty(ctx);
let int_ty = int_ty.deref(ctx);
if int_ty.signedness() != Signedness::Signless {
return verify_err!(op.loc(ctx), IntBinArithOpErr);
}
Ok(())
}
}
dict_key!(
ATTR_KEY_INTEGER_OVERFLOW_FLAGS,
"llvm_integer_overflow_flags"
);
#[derive(Error, Debug)]
#[error("IntegerOverflowFlag missing on Op")]
pub struct IntBinArithOpWithOverflowFlagErr;
#[op_interface]
pub trait IntBinArithOpWithOverflowFlag: IntBinArithOp {
fn new_with_overflow_flag(
ctx: &mut Context,
lhs: Value,
rhs: Value,
flag: IntegerOverflowFlagsAttr,
) -> Self
where
Self: Sized,
{
let op = Self::new(ctx, lhs, rhs);
op.set_integer_overflow_flag(ctx, flag);
op
}
fn integer_overflow_flag(&self, ctx: &Context) -> IntegerOverflowFlagsAttr
where
Self: Sized,
{
self.get_operation()
.deref(ctx)
.attributes
.get::<IntegerOverflowFlagsAttr>(&ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
.expect("Integer overflow flag missing or is of incorrect type")
.clone()
}
fn set_integer_overflow_flag(&self, ctx: &Context, flag: IntegerOverflowFlagsAttr)
where
Self: Sized,
{
self.get_operation()
.deref_mut(ctx)
.attributes
.set(ATTR_KEY_INTEGER_OVERFLOW_FLAGS.clone(), flag);
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let op = op.get_operation().deref(ctx);
if op
.attributes
.get::<IntegerOverflowFlagsAttr>(&ATTR_KEY_INTEGER_OVERFLOW_FLAGS)
.is_none()
{
return verify_err!(op.loc(), IntBinArithOpWithOverflowFlagErr);
}
Ok(())
}
}
#[op_interface]
pub trait FloatBinArithOp: BinArithOp + ScalarOrVectorOpdImpls<dyn FloatTypeInterface, 0> {
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
dict_key!(
ATTR_KEY_FAST_MATH_FLAGS,
"llvm_fast_math_flags"
);
#[derive(Error, Debug)]
#[error("Fastmath flag missing on Op")]
pub struct FastMathFlagMissingErr;
#[op_interface]
pub trait FastMathFlags {
fn fast_math_flags(&self, ctx: &Context) -> FastmathFlagsAttr
where
Self: Sized,
{
*self
.get_operation()
.deref(ctx)
.attributes
.get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
.expect("Fast math flags missing or is of incorrect type")
}
fn set_fast_math_flags(&self, ctx: &Context, flag: FastmathFlagsAttr)
where
Self: Sized,
{
self.get_operation()
.deref_mut(ctx)
.attributes
.set(ATTR_KEY_FAST_MATH_FLAGS.clone(), flag);
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let op = op.get_operation().deref(ctx);
if op
.attributes
.get::<FastmathFlagsAttr>(&ATTR_KEY_FAST_MATH_FLAGS)
.is_none()
{
return verify_err!(op.loc(), FastmathFlagMissingErr);
}
Ok(())
}
}
dict_key!(
ATTR_KEY_SYNC_SCOPE,
"llvm_syncscope"
);
#[derive(Error, Debug)]
#[error("Synchronization scope missing on Op")]
pub struct SyncScopeMissingErr;
#[op_interface]
pub trait SyncScopeInterface {
fn syncscope(&self, ctx: &Context) -> SyncScopeAttr
where
Self: Sized,
{
self.get_operation()
.deref(ctx)
.attributes
.get::<SyncScopeAttr>(&ATTR_KEY_SYNC_SCOPE)
.expect("Synchronization scope missing or is of incorrect type")
.clone()
}
fn set_syncscope(&self, ctx: &Context, syncscope: SyncScopeAttr)
where
Self: Sized,
{
self.get_operation()
.deref_mut(ctx)
.attributes
.set(ATTR_KEY_SYNC_SCOPE.clone(), syncscope);
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let op = op.get_operation().deref(ctx);
if op
.attributes
.get::<SyncScopeAttr>(&ATTR_KEY_SYNC_SCOPE)
.is_none()
{
return verify_err!(op.loc(), SyncScopeMissingErr);
}
Ok(())
}
}
#[op_interface]
pub trait FloatBinArithOpWithFastMathFlags: FloatBinArithOp + FastMathFlags {
fn new_with_fast_math_flags(
ctx: &mut Context,
lhs: Value,
rhs: Value,
flag: FastmathFlagsAttr,
) -> Self
where
Self: Sized,
{
let op = Self::new(ctx, lhs, rhs);
op.set_fast_math_flags(ctx, flag);
op
}
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
#[derive(Error, Debug)]
#[error("Fastmath flag missing on Op")]
pub struct FastmathFlagMissingErr;
dict_key!(
ATTR_KEY_NNEG_FLAG,
"llvm_nneg_flag"
);
#[op_interface]
pub trait NNegFlag {
fn nneg(&self, ctx: &Context) -> bool {
self.get_operation()
.deref(ctx)
.attributes
.get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG)
.expect("NNEG flag missing or is of incorrect type")
.clone()
.into()
}
fn set_nneg(&self, ctx: &Context, flag: bool) {
self.get_operation()
.deref_mut(ctx)
.attributes
.set(ATTR_KEY_NNEG_FLAG.clone(), BoolAttr::new(flag));
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let op = op.get_operation().deref(ctx);
if op.attributes.get::<BoolAttr>(&ATTR_KEY_NNEG_FLAG).is_none() {
return verify_err!(op.loc(), NNegFlagMissingErr);
}
Ok(())
}
}
#[derive(Error, Debug)]
#[error("NNEG flag missing on Op")]
pub struct NNegFlagMissingErr;
#[derive(Error, Debug)]
#[error("Result must be a pointer type, but is not")]
pub struct PointerTypeResultVerifyErr;
#[op_interface]
pub trait PointerTypeResult: OneResultInterface + ResultNOfType<0, PointerType> {
fn result_pointee_type(&self, ctx: &Context) -> TypeHandle;
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
if !op_cast::<dyn OneResultInterface>(op)
.expect("An Op here must impl OneResultInterface")
.result_type(ctx)
.deref(ctx)
.is::<PointerType>()
{
return verify_err!(op.loc(ctx), PointerTypeResultVerifyErr);
}
Ok(())
}
}
#[op_interface]
pub trait CastOpInterface: OneResultInterface + OneOpdInterface {
fn new(ctx: &mut Context, operand: Value, res_type: TypeHandle) -> Self
where
Self: Sized,
{
let op = Operation::new(
ctx,
Self::get_concrete_op_info(),
vec![res_type],
vec![operand],
vec![],
0,
);
Self::from_operation(op)
}
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
#[op_interface]
pub trait CastOpWithNNegInterface: CastOpInterface + NNegFlag {
fn new_with_nneg(ctx: &mut Context, operand: Value, res_type: TypeHandle, nneg: bool) -> Self
where
Self: Sized,
{
let op = Self::new(ctx, operand, res_type);
op.set_nneg(ctx, nneg);
op
}
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
#[op_interface]
pub trait IsDeclaration {
fn is_declaration(&self, ctx: &Context) -> bool
where
Self: Sized;
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
dict_key!(
ATTR_KEY_LLVM_SYMBOL_NAME,
"llvm_symbol_name"
);
#[op_interface]
pub trait LlvmSymbolName: SymbolOpInterface {
fn llvm_symbol_name(&self, ctx: &Context) -> Option<String> {
self.get_operation()
.deref(ctx)
.attributes
.get::<pliron::builtin::attributes::StringAttr>(&ATTR_KEY_LLVM_SYMBOL_NAME)
.map(|attr| attr.clone().into())
}
fn set_llvm_symbol_name(&self, ctx: &Context, name: String) {
self.get_operation().deref_mut(ctx).attributes.set(
ATTR_KEY_LLVM_SYMBOL_NAME.clone(),
pliron::builtin::attributes::StringAttr::new(name),
);
}
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
dict_key!(
ATTR_KEY_LLVM_ALIGNMENT,
"llvm_alignment"
);
#[op_interface]
pub trait AlignableOpInterface {
fn alignment(&self, ctx: &Context) -> Option<u32>
where
Self: Sized,
{
self.get_operation()
.deref(ctx)
.attributes
.get::<AlignmentAttr>(&ATTR_KEY_LLVM_ALIGNMENT)
.map(|attr| attr.0)
}
fn set_alignment(&self, ctx: &Context, alignment: u32)
where
Self: Sized,
{
self.get_operation()
.deref_mut(ctx)
.attributes
.set(ATTR_KEY_LLVM_ALIGNMENT.clone(), AlignmentAttr(alignment));
}
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
dict_key!(
ATTR_KEY_LLVM_VOLATILE,
"llvm_volatile"
);
#[op_interface]
pub trait VolatilityOpInterface {
fn is_volatile(&self, ctx: &Context) -> bool
where
Self: Sized,
{
self.get_operation()
.deref(ctx)
.attributes
.get::<BoolAttr>(&ATTR_KEY_LLVM_VOLATILE)
.map(|attr| attr.clone().into())
.unwrap_or(false)
}
fn set_volatile(&self, ctx: &Context, is_volatile: bool)
where
Self: Sized,
{
self.get_operation()
.deref_mut(ctx)
.attributes
.set(ATTR_KEY_LLVM_VOLATILE.clone(), BoolAttr::new(is_volatile));
}
fn verify(_op: &dyn Op, _ctx: &Context) -> Result<()>
where
Self: Sized,
{
Ok(())
}
}
#[derive(Debug, Error)]
pub enum ScalarOrVectorErr {
#[error("{0} is not {1} or a vector of it")]
NotTOrVectorOfT(String, String),
#[error("{0} or its vector element type does not implement interface")]
TyOrElemNotImplsI(String),
}
fn vector_shape_of(ty: TypeHandle, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
ty.deref(ctx)
.downcast_ref::<VectorType>()
.map(|vec_ty| (vec_ty.num_elements(), vec_ty.kind()))
}
fn elem_ty_of<T: Type>(ty: TypeHandle, ctx: &Context) -> TypedHandle<T> {
if let Ok(typed_handle) = TypedHandle::from_handle(ty, ctx) {
return typed_handle;
}
let ty_ref = &*ty.deref(ctx);
let elem_ty = ty_ref
.downcast_ref::<VectorType>()
.expect("verify() guarantees type is T or a vector of T")
.elem_type();
TypedHandle::from_handle(elem_ty, ctx).expect("verify() guarantees element type is T")
}
fn verify_t_or_vec_of_t<T: Type>(loc: Location, ty: &dyn Type, ctx: &Context) -> Result<()> {
if ty.is::<T>() {
return Ok(());
}
let Some(vec_ty) = ty.downcast_ref::<VectorType>() else {
return verify_err!(
loc,
ScalarOrVectorErr::NotTOrVectorOfT(
ty.get_type_id().disp(ctx).to_string(),
T::get_type_id_static().disp(ctx).to_string()
)
);
};
let elem_ty = &*vec_ty.elem_type().deref(ctx);
if !elem_ty.is::<T>() {
return verify_err!(
loc,
ScalarOrVectorErr::NotTOrVectorOfT(
ty.get_type_id().disp(ctx).to_string(),
T::get_type_id_static().disp(ctx).to_string()
)
);
}
Ok(())
}
fn elem_ty_of_impls<I: ?Sized + TypeInterfaceMarker + 'static>(
ty: TypeHandle,
ctx: &Context,
) -> TypeInterfaceHandle<I> {
if let Ok(interface_handle) = TypeInterfaceHandle::from_handle(ty, ctx) {
return interface_handle;
}
let ty_ref = &*ty.deref(ctx);
let elem_ty = ty_ref
.downcast_ref::<VectorType>()
.expect("verify() guarantees type impls I or is a vector whose elem impls I")
.elem_type();
TypeInterfaceHandle::from_handle(elem_ty, ctx)
.expect("verify() guarantees element type impls I")
}
fn verify_impls_i_or_vec_of_impls_i<I: ?Sized + TypeInterfaceMarker + 'static>(
loc: Location,
ty: &dyn Type,
ctx: &Context,
) -> Result<()> {
if type_impls::<I>(ty) {
return Ok(());
}
let Some(vec_ty) = ty.downcast_ref::<VectorType>() else {
return verify_err!(
loc,
ScalarOrVectorErr::TyOrElemNotImplsI(ty.get_type_id().disp(ctx).to_string())
);
};
let elem_ty = &*vec_ty.elem_type().deref(ctx);
if !type_impls::<I>(elem_ty) {
return verify_err!(
loc,
ScalarOrVectorErr::TyOrElemNotImplsI(ty.get_type_id().disp(ctx).to_string())
);
}
Ok(())
}
#[op_interface]
pub trait ScalarOrVectorOpd<T: Type, const N: usize> {
fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypedHandle<T> {
let op = &*self.get_operation().deref(ctx);
elem_ty_of(op.get_operand(N).get_type(ctx), ctx)
}
fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
let op = &*self.get_operation().deref(ctx);
vector_shape_of(op.get_operand(N).get_type(ctx), ctx)
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let opd = verify_get_operand_n::<N>(op.get_operation(), ctx)?;
verify_t_or_vec_of_t::<T>(op.loc(ctx), &*opd.get_type(ctx).deref(ctx), ctx)
}
}
#[op_interface]
pub trait ScalarOrVectorOpdImpls<I: ?Sized + TypeInterfaceMarker + 'static, const N: usize> {
fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypeInterfaceHandle<I> {
let op = &*self.get_operation().deref(ctx);
elem_ty_of_impls::<I>(op.get_operand(N).get_type(ctx), ctx)
}
fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
let op = &*self.get_operation().deref(ctx);
vector_shape_of(op.get_operand(N).get_type(ctx), ctx)
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let opd = verify_get_operand_n::<N>(op.get_operation(), ctx)?;
verify_impls_i_or_vec_of_impls_i::<I>(op.loc(ctx), &*opd.get_type(ctx).deref(ctx), ctx)
}
}
#[op_interface]
pub trait ScalarOrVectorRes<T: Type, const N: usize> {
fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypedHandle<T> {
let op = &*self.get_operation().deref(ctx);
elem_ty_of(op.get_result(N).get_type(ctx), ctx)
}
fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
let op = &*self.get_operation().deref(ctx);
vector_shape_of(op.get_result(N).get_type(ctx), ctx)
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let res = verify_get_result_n::<N>(op.get_operation(), ctx)?;
verify_t_or_vec_of_t::<T>(op.loc(ctx), &*res.get_type(ctx).deref(ctx), ctx)
}
}
#[op_interface]
pub trait ScalarOrVectorResImpls<I: ?Sized + TypeInterfaceMarker + 'static, const N: usize> {
fn scalar_or_vector_elem_ty(&self, ctx: &Context) -> TypeInterfaceHandle<I> {
let op = &*self.get_operation().deref(ctx);
elem_ty_of_impls::<I>(op.get_result(N).get_type(ctx), ctx)
}
fn vector_shape(&self, ctx: &Context) -> Option<(u32, VectorTypeKind)> {
let op = &*self.get_operation().deref(ctx);
vector_shape_of(op.get_result(N).get_type(ctx), ctx)
}
fn verify(op: &dyn Op, ctx: &Context) -> Result<()>
where
Self: Sized,
{
let res = verify_get_result_n::<N>(op.get_operation(), ctx)?;
verify_impls_i_or_vec_of_impls_i::<I>(op.loc(ctx), &*res.get_type(ctx).deref(ctx), ctx)
}
}