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
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//! Predicate functions for testing instruction fields.
//!
//! This module defines functions that are used by the instruction predicates defined by
//! `cranelift-codegen/meta/src/cdsl/instructions.rs` classes.
//!
//! The predicates the operate on integer fields use `Into<i64>` as a shared trait bound. This
//! bound is implemented by all the native integer types as well as `Imm64`.
//!
//! Some of these predicates may be unused in certain ISA configurations, so we suppress the
//! dead code warning.

use crate::ir;
use crate::ir::ConstantData;

/// Check that an integer value is zero.
#[allow(dead_code)]
pub fn is_zero_int<T: Into<i64>>(x: T) -> bool {
    x.into() == 0
}

/// Check that a 64-bit floating point value is zero.
#[allow(dead_code)]
pub fn is_zero_64_bit_float<T: Into<ir::immediates::Ieee64>>(x: T) -> bool {
    let x64 = x.into();
    x64.bits() == 0
}

/// Check that a 32-bit floating point value is zero.
#[allow(dead_code)]
pub fn is_zero_32_bit_float<T: Into<ir::immediates::Ieee32>>(x: T) -> bool {
    let x32 = x.into();
    x32.bits() == 0
}

/// Check that a constant contains all zeroes.
#[allow(dead_code)]
pub fn is_all_zeroes(x: &ConstantData) -> bool {
    x.iter().all(|&f| f == 0)
}

/// Check that a constant contains all ones.
#[allow(dead_code)]
pub fn is_all_ones(x: &ConstantData) -> bool {
    x.iter().all(|&f| f == 0xff)
}

/// Check that `x` is the same as `y`.
#[allow(dead_code)]
pub fn is_equal<T: Eq + Copy, O: Into<T> + Copy>(x: T, y: O) -> bool {
    x == y.into()
}

/// Check that `x` can be represented as a `wd`-bit signed integer with `sc` low zero bits.
#[allow(dead_code)]
pub fn is_signed_int<T: Into<i64>>(x: T, wd: u8, sc: u8) -> bool {
    let s = x.into();
    s == (s >> sc << (64 - wd + sc) >> (64 - wd))
}

/// Check that `x` can be represented as a `wd`-bit unsigned integer with `sc` low zero bits.
#[allow(dead_code)]
pub fn is_unsigned_int<T: Into<i64>>(x: T, wd: u8, sc: u8) -> bool {
    let u = x.into() as u64;
    // Bit-mask of the permitted bits.
    let m = (1 << wd) - (1 << sc);
    u == (u & m)
}

#[allow(dead_code)]
pub fn is_colocated_func(func_ref: ir::FuncRef, func: &ir::Function) -> bool {
    func.dfg.ext_funcs[func_ref].colocated
}

#[allow(dead_code)]
pub fn is_colocated_data(global_value: ir::GlobalValue, func: &ir::Function) -> bool {
    match func.global_values[global_value] {
        ir::GlobalValueData::Symbol { colocated, .. } => colocated,
        _ => panic!("is_colocated_data only makes sense for data with symbolic addresses"),
    }
}

#[allow(dead_code)]
pub fn has_length_of(value_list: &ir::ValueList, num: usize, func: &ir::Function) -> bool {
    value_list.len(&func.dfg.value_lists) == num
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn cvt_u32() {
        let x1 = 0u32;
        let x2 = 1u32;
        let x3 = 0xffff_fff0u32;

        assert!(is_signed_int(x1, 1, 0));
        assert!(is_signed_int(x1, 2, 1));
        assert!(is_signed_int(x2, 2, 0));
        assert!(!is_signed_int(x2, 2, 1));

        // `u32` doesn't sign-extend when converted to `i64`.
        assert!(!is_signed_int(x3, 8, 0));

        assert!(is_unsigned_int(x1, 1, 0));
        assert!(is_unsigned_int(x1, 8, 4));
        assert!(is_unsigned_int(x2, 1, 0));
        assert!(!is_unsigned_int(x2, 8, 4));
        assert!(!is_unsigned_int(x3, 1, 0));
        assert!(is_unsigned_int(x3, 32, 4));
    }

    #[test]
    fn cvt_imm64() {
        use crate::ir::immediates::Imm64;

        let x1 = Imm64::new(-8);
        let x2 = Imm64::new(8);

        assert!(is_signed_int(x1, 16, 2));
        assert!(is_signed_int(x2, 16, 2));
        assert!(!is_signed_int(x1, 16, 4));
        assert!(!is_signed_int(x2, 16, 4));
    }

    #[test]
    fn check_is_all_zeroes() {
        assert!(is_all_zeroes(&[0; 16].as_ref().into()));
        assert!(is_all_zeroes(
            &vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].into()
        ));
        assert!(!is_all_zeroes(&[1; 16].as_ref().into()));
    }

    #[test]
    fn check_is_all_ones() {
        assert!(!is_all_ones(&[0; 16].as_ref().into()));
        assert!(is_all_ones(&[0xff; 16].as_ref().into()));
    }
}