Skip to content
Snippets Groups Projects
Select Git revision
  • 4a1f1a267837d555be0d9efb9cc2f4974ad65099
  • student default protected
2 results

build.rs

Blame
  • Forked from Per Lindgren / e7020e_2019
    Source project has a limited visibility.
    • Per Lindgren's avatar
      7d1753a9
      prepared for design of embedded systems 2019, first commit · 7d1753a9
      Per Lindgren authored
      bt broken?
      
      Cargo.toml no nightly dep
      
      more examples
      
      preLaunchTask
      
      crash re-added
      
      more preLaunchTasks
      
      example/device and SVD
      
      openocd.gdb comment fixed and ext itm launch config added
      
      bare0
      
      bare1
      
      bare1
      
      bare2 and bare3
      
      bare2 and bare3
      
      bare2 and bare3
      
      bare4 and bare5
      
      bare6 wip
      
      bare6 wip
      
      serial wip
      
      serial wip
      
      bare7
      
      bare8
      
      bare8
      
      to fresk
      
      to fresk
      
      to fresk
      
      to fresk
      
      bare8, 9
      
      to fresk
      
      bare7
      
      polishing
      
      bare9
      
      rtfm_blinky wip
      
      rtfm_blinky
      
      rtfm_blinky
      
      marcus
      
      eq implemented and tested
      
      eq implemented and tested
      
      bare10
      
      bare updates
      
      bare 6 assignment
      
      cargo fix
      
      various fixes
      7d1753a9
      History
      prepared for design of embedded systems 2019, first commit
      Per Lindgren authored
      bt broken?
      
      Cargo.toml no nightly dep
      
      more examples
      
      preLaunchTask
      
      crash re-added
      
      more preLaunchTasks
      
      example/device and SVD
      
      openocd.gdb comment fixed and ext itm launch config added
      
      bare0
      
      bare1
      
      bare1
      
      bare2 and bare3
      
      bare2 and bare3
      
      bare2 and bare3
      
      bare4 and bare5
      
      bare6 wip
      
      bare6 wip
      
      serial wip
      
      serial wip
      
      bare7
      
      bare8
      
      bare8
      
      to fresk
      
      to fresk
      
      to fresk
      
      to fresk
      
      bare8, 9
      
      to fresk
      
      bare7
      
      polishing
      
      bare9
      
      rtfm_blinky wip
      
      rtfm_blinky
      
      rtfm_blinky
      
      marcus
      
      eq implemented and tested
      
      eq implemented and tested
      
      bare10
      
      bare updates
      
      bare 6 assignment
      
      cargo fix
      
      various fixes
    operator.rs 19.30 KiB
    use rustc::mir;
    use rustc::ty::{Ty};
    use rustc_const_math::ConstFloat;
    use syntax::ast::FloatTy;
    use std::cmp::Ordering;
    
    use error::{EvalError, EvalResult};
    use eval_context::{EvalContext, ValTy};
    use lvalue::Lvalue;
    use memory::{MemoryPointer, PointerOffset, SByte};
    use value::{
        PrimVal,
        PrimValKind,
        Value,
        bytes_to_f32,
        bytes_to_f64,
        f32_to_bytes,
        f64_to_bytes,
        bytes_to_bool,
    };
    
    impl<'a, 'tcx> EvalContext<'a, 'tcx> {
        fn binop_with_overflow(
            &mut self,
            op: mir::BinOp,
            left: &mir::Operand<'tcx>,
            right: &mir::Operand<'tcx>,
        ) -> EvalResult<'tcx, (PrimVal, bool)> {
            let left_ty    = self.operand_ty(left);
            let right_ty   = self.operand_ty(right);
            let left_val   = self.eval_operand_to_primval(left)?;
            let right_val  = self.eval_operand_to_primval(right)?;
            self.binary_op(op, left_val, left_ty, right_val, right_ty)
        }
    
        /// Applies the binary operation `op` to the two operands and writes a tuple of the result
        /// and a boolean signifying the potential overflow to the destination.
        pub(super) fn intrinsic_with_overflow(
            &mut self,
            op: mir::BinOp,
            left: &mir::Operand<'tcx>,
            right: &mir::Operand<'tcx>,
            dest: Lvalue<'tcx>,
            dest_ty: Ty<'tcx>,
        ) -> EvalResult<'tcx> {
            let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
            let val = Value::ByValPair(val, PrimVal::from_bool(overflowed));
            self.write_value(ValTy { value: val, ty: dest_ty}, dest)
        }
    
        /// Applies the binary operation `op` to the arguments and writes the result to the
        /// destination. Returns `true` if the operation overflowed.
        pub(super) fn intrinsic_overflowing(
            &mut self,
            op: mir::BinOp,
            left: &mir::Operand<'tcx>,
            right: &mir::Operand<'tcx>,
            dest: Lvalue<'tcx>,
            dest_ty: Ty<'tcx>,
        ) -> EvalResult<'tcx, bool> {
            let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
            self.write_primval(dest, val, dest_ty)?;
            Ok(overflowed)
        }
    }
    
    macro_rules! overflow {
        ($op:ident, $l:expr, $r:expr) => ({
            let (val, overflowed) = $l.$op($r);
            let primval = PrimVal::Bytes(val as u128);
            Ok((primval, overflowed))
        })
    }
    
    macro_rules! int_arithmetic {
        ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
            let l = $l;
            let r = $r;
            match $kind {
                I8  => overflow!($int_op, l as i8,  r as i8),
                I16 => overflow!($int_op, l as i16, r as i16),
                I32 => overflow!($int_op, l as i32, r as i32),
                I64 => overflow!($int_op, l as i64, r as i64),
                I128 => overflow!($int_op, l as i128, r as i128),
                U8  => overflow!($int_op, l as u8,  r as u8),
                U16 => overflow!($int_op, l as u16, r as u16),
                U32 => overflow!($int_op, l as u32, r as u32),
                U64 => overflow!($int_op, l as u64, r as u64),
                U128 => overflow!($int_op, l as u128, r as u128),
                _ => bug!("int_arithmetic should only be called on int primvals"),
            }
        })
    }
    
    macro_rules! int_shift {
        ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
            let l = $l;
            let r = $r;
            match $kind {
                I8  => overflow!($int_op, l as i8,  r),
                I16 => overflow!($int_op, l as i16, r),
                I32 => overflow!($int_op, l as i32, r),
                I64 => overflow!($int_op, l as i64, r),
                I128 => overflow!($int_op, l as i128, r),
                U8  => overflow!($int_op, l as u8,  r),
                U16 => overflow!($int_op, l as u16, r),
                U32 => overflow!($int_op, l as u32, r),
                U64 => overflow!($int_op, l as u64, r),
                U128 => overflow!($int_op, l as u128, r),
                _ => bug!("int_shift should only be called on int primvals"),
            }
        })
    }
    
    impl<'a, 'tcx> EvalContext<'a, 'tcx> {
    
        /// Returns the result of the specified operation and whether it overflowed.
        pub fn binary_op(
            &mut self,
            bin_op: mir::BinOp,
            left: PrimVal,
            left_ty: Ty<'tcx>,
            right: PrimVal,
            right_ty: Ty<'tcx>,
        ) -> EvalResult<'tcx, (PrimVal, bool)> {
            use rustc::mir::BinOp::*;
            use value::PrimValKind::*;
    
            let left_kind  = self.ty_to_primval_kind(left_ty)?;
            let right_kind = self.ty_to_primval_kind(right_ty)?;
    
            // I: Handle operations that support pointers
            let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
            let isize = PrimValKind::from_int_size(self.memory.pointer_size());
    
            if !left.is_concrete() || !right.is_concrete() {
                return self.abstract_binary_op(bin_op,
                                               left, left_kind,
                                               right, right_kind);
            }
    
            match (left, right) {
                (PrimVal::Ptr(lptr), PrimVal::Ptr(rptr)) => {
                    if !lptr.has_concrete_offset() || !rptr.has_concrete_offset() {
                        return self.abstract_ptr_ops(bin_op, lptr, left_kind, rptr, right_kind);
                    }
                }
                _ => {
    
                }
            }
    
            if !left_kind.is_float() && !right_kind.is_float() {
                match bin_op {
                    Offset if left_kind == Ptr && right_kind == usize => {
                        let pointee_ty = left_ty.builtin_deref(true).expect("Offset called on non-ptr type").ty;
                        let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?;
                        return Ok((ptr, false));
                    },
                    // These work on anything
                    Eq if left_kind == right_kind => {
                        let result = match (left, right) {
                            (PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right,
                            (PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right,
                            (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
                            _ => false,
                        };
                        return Ok((PrimVal::from_bool(result), false));
                    }
                    Ne if left_kind == right_kind => {
                        let result = match (left, right) {
                            (PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right,
                            (PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right,
                            (PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
                            _ => true,
                        };
                        return Ok((PrimVal::from_bool(result), false));
                    }
                    // These need both pointers to be in the same allocation
                    Lt | Le | Gt | Ge | Sub
                    if left_kind == right_kind
                    && (left_kind == Ptr || left_kind == usize || left_kind == isize)
                    && left.is_ptr() && right.is_ptr() => {
                        let left = left.to_ptr()?;
                        let right = right.to_ptr()?;
                        let (left_offset, right_offset) = match (left.offset, right.offset) {
                            (PointerOffset::Concrete(l), PointerOffset::Concrete(r)) => (l, r),
                            _ => unimplemented!(),
                        };
    
    
                        if left.alloc_id == right.alloc_id {
                            let res = match bin_op {
                                Lt => left_offset < right_offset,
                                Le => left_offset <= right_offset,
                                Gt => left_offset > right_offset,
                                Ge => left_offset >= right_offset,
                                Sub => {
                                    return int_arithmetic!(left_kind, overflowing_sub, left_offset, right_offset);
                                }
                                _ => bug!("We already established it has to be one of these operators."),
                            };
                            return Ok((PrimVal::from_bool(res), false));
                        } else {
                            // Both are pointers, but from different allocations.
                            return Err(EvalError::InvalidPointerMath);
                        }
                    }
                    // These work if one operand is a pointer, the other an integer
                    Add | BitAnd | Sub
                    if left_kind == right_kind && (left_kind == usize || left_kind == isize)
                    && left.is_ptr() && right.is_bytes() => {
                        // Cast to i128 is fine as we checked the kind to be ptr-sized
                        return self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize);
                    }
                    Add | BitAnd
                    if left_kind == right_kind && (left_kind == usize || left_kind == isize)
                    && left.is_bytes() && right.is_ptr() => {
                        // This is a commutative operation, just swap the operands
                        return self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize);
                    }
                    _ => {}
                }
            }
    
            // II: From now on, everything must be bytes, no pointers
            let l = left.to_bytes()?;
            let r = right.to_bytes()?;
    
            // These ops can have an RHS with a different numeric type.
            if bin_op == Shl || bin_op == Shr {
                return match bin_op {
                    Shl => int_shift!(left_kind, overflowing_shl, l, r as u32),
                    Shr => int_shift!(left_kind, overflowing_shr, l, r as u32),
                    _ => bug!("it has already been checked that this is a shift op"),
                };
            }
    
            if left_kind != right_kind {
                let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
                return Err(EvalError::Unimplemented(msg));
            }
    
            let float_op = |op, l, r, ty| {
                let l = ConstFloat {
                    bits: l,
                    ty,
                };
                let r = ConstFloat {
                    bits: r,
                    ty,
                };
                match op {
                    Eq => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Equal),
                    Ne => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Equal),
                    Lt => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Less),
                    Le => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Greater),
                    Gt => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Greater),
                    Ge => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Less),
                    Add => PrimVal::Bytes((l + r).unwrap().bits),
                    Sub => PrimVal::Bytes((l - r).unwrap().bits),
                    Mul => PrimVal::Bytes((l * r).unwrap().bits),
                    Div => PrimVal::Bytes((l / r).unwrap().bits),
                    Rem => PrimVal::Bytes((l % r).unwrap().bits),
                    _ => bug!("invalid float op: `{:?}`", op),
                }
            };
    
            let val = match (bin_op, left_kind) {
                (_, F32) => float_op(bin_op, l, r, FloatTy::F32),
                (_, F64) => float_op(bin_op, l, r, FloatTy::F64),
    
                (Eq, _) => PrimVal::from_bool(l == r),
                (Ne, _) => PrimVal::from_bool(l != r),
                (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
                (Lt, _) => PrimVal::from_bool(l <  r),
                (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
                (Le, _) => PrimVal::from_bool(l <= r),
                (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
                (Gt, _) => PrimVal::from_bool(l >  r),
                (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
                (Ge, _) => PrimVal::from_bool(l >= r),
    
                (BitOr,  _) => PrimVal::Bytes(l | r),
                (BitAnd, _) => PrimVal::Bytes(l & r),
                (BitXor, _) => PrimVal::Bytes(l ^ r),
    
                (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
                (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
                (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
                (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
                (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
    
                _ => {
                    let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
                    return Err(EvalError::Unimplemented(msg));
                }
            };
    
            Ok((val, false))
        }
    
        fn ptr_int_arithmetic(
            &self,
            bin_op: mir::BinOp,
            left: MemoryPointer,
            right: i128,
            signed: bool,
        ) -> EvalResult<'tcx, (PrimVal, bool)> {
            use rustc::mir::BinOp::*;
    
            fn map_to_primval((res, over) : (MemoryPointer, bool)) -> (PrimVal, bool) {
                (PrimVal::Ptr(res), over)
            }
    
            let left_offset = match left.offset {
                PointerOffset::Concrete(n) => n,
                _ => unimplemented!(),
            };
    
            Ok(match bin_op {
                Sub =>
                    // The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
                    map_to_primval(left.overflowing_signed_offset(-right, self.memory.layout)),
                Add if signed =>
                    map_to_primval(left.overflowing_signed_offset(right, self.memory.layout)),
                Add if !signed =>
                    map_to_primval(left.overflowing_offset(right as u64, self.memory.layout)),
    
                BitAnd if !signed => {
                    let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1);
                    let right = right as u64;
                    if right & base_mask == base_mask {
                        // Case 1: The base address bits are all preserved, i.e., right is all-1 there
                        (PrimVal::Ptr(MemoryPointer::new(left.alloc_id, left_offset & right)), false)
                    } else if right & base_mask == 0 {
                        // Case 2: The base address bits are all taken away, i.e., right is all-0 there
                        (PrimVal::from_u128((left_offset & right) as u128), false)
                    } else {
                        return Err(EvalError::ReadPointerAsBytes);
                    }
                }
    
                _ => {
                    let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
                    return Err(EvalError::Unimplemented(msg));
                }
            })
        }
    
        pub fn abstract_binary_op(
            &mut self,
            bin_op: mir::BinOp,
            left: PrimVal,
            left_kind: PrimValKind,
            right: PrimVal,
            mut right_kind: PrimValKind,
        ) -> EvalResult<'tcx, (PrimVal, bool)> {
    
            // These ops can have an RHS with a different numeric type.
            if bin_op == mir::BinOp::Shl || bin_op == mir::BinOp::Shr {
                match (left, right) {
                    (PrimVal::Abstract(abytes), PrimVal::Bytes(rn)) if rn % 8 == 0 => {
                        let num_bytes = (rn / 8) as usize;
                        match bin_op {
                            mir::BinOp::Shl => {
                                let mut buffer = [SByte::Concrete(0); 8];
                                for idx in num_bytes .. 8 {
                                    buffer[idx] = abytes[idx - num_bytes];
                                }
                                return Ok((PrimVal::Abstract(buffer), false));
                            }
                            mir::BinOp::Shr => {
                                if !left_kind.is_signed_int() {
                                    let mut buffer = [SByte::Concrete(0); 8];
                                    for idx in num_bytes .. 8 {
                                        buffer[idx - num_bytes] = abytes[idx];
                                    }
                                    return Ok((PrimVal::Abstract(buffer), false));
                                }
                            }
                            _ => unimplemented!(),
                        }
                    }
                    _ => (),
                }
    
                if right_kind.num_bytes() == left_kind.num_bytes() {
                    right_kind = left_kind;
                } else if right_kind.num_bytes() < left_kind.num_bytes() {
                    right_kind = left_kind;
                } else {
                    if let PrimVal::Bytes(n) = right {
                        if n < 256 {
                            // HACK
                            right_kind = left_kind;
                        } else {
                            unimplemented!()
                        }
                    } else {
                        unimplemented!()
                    }
                }
            }
    
            if left_kind != right_kind {
                let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
                return Err(EvalError::Unimplemented(msg));
            }
            Ok((self.memory.constraints.add_binop_constraint(bin_op, left, right, left_kind), false))
        }
    
        fn abstract_ptr_ops(
            &mut self,
            bin_op: mir::BinOp,
            left: MemoryPointer,
            _left_kind: PrimValKind,
            right: MemoryPointer,
            _right_kind: PrimValKind,
        ) -> EvalResult<'tcx, (PrimVal, bool)> {
            use rustc::mir::BinOp::*;
            use value::PrimValKind::*;
            if left.alloc_id != right.alloc_id {
                if let Eq = bin_op {
                    unimplemented!()
                } else {
                    unimplemented!()
                }
            } else {
                let result = self.memory.constraints.add_binop_constraint(
                    bin_op, left.offset.as_primval(), right.offset.as_primval(), U64);
                Ok((result, false))
            }
        }
    
        pub fn unary_op(
            &mut self,
            un_op: mir::UnOp,
            val: PrimVal,
            val_kind: PrimValKind,
        ) -> EvalResult<'tcx, PrimVal> {
            use rustc::mir::UnOp::*;
            use value::PrimValKind::*;
    
            if !val.is_concrete() {
                return
                    Ok(self.memory.constraints.add_unop_constraint(
                        un_op, val, val_kind))
            }
    
            let bytes = val.to_bytes()?;
    
            let result_bytes = match (un_op, val_kind) {
                (Not, Bool) => !bytes_to_bool(bytes) as u128,
    
                (Not, U8)  => !(bytes as u8) as u128,
                (Not, U16) => !(bytes as u16) as u128,
                (Not, U32) => !(bytes as u32) as u128,
                (Not, U64) => !(bytes as u64) as u128,
                (Not, U128) => !bytes,
    
                (Not, I8)  => !(bytes as i8) as u128,
                (Not, I16) => !(bytes as i16) as u128,
                (Not, I32) => !(bytes as i32) as u128,
                (Not, I64) => !(bytes as i64) as u128,
                (Not, I128) => !(bytes as i128) as u128,
    
                (Neg, I8)  => -(bytes as i8) as u128,
                (Neg, I16) => -(bytes as i16) as u128,
                (Neg, I32) => -(bytes as i32) as u128,
                (Neg, I64) => -(bytes as i64) as u128,
                (Neg, I128) => -(bytes as i128) as u128,
    
                (Neg, F32) => f32_to_bytes(-bytes_to_f32(bytes)),
                (Neg, F64) => f64_to_bytes(-bytes_to_f64(bytes)),
    
                _ => {
                    let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
                    return Err(EvalError::Unimplemented(msg));
                }
            };
    
            Ok(PrimVal::Bytes(result_bytes))
        }
    }