let rec stripCastsDeepForPtrArith (e:exp): exp =
  match e.enode with
  | CastE(t, e') when not(isTypeVolatile t) -> begin
      let e' = stripCastsDeepForPtrArith e' in
      match unrollType (typeOf e'), unrollType t with
      | TPtr (bt1, _), TPtr (bt2, _) -> begin
          try
            if bitsSizeOf bt1 = bitsSizeOf bt2 then (* Okay to strip *)
              e'
            else
              new_exp (CastE(t,e'))
          with SizeOfError _ -> (* bt1 or bt2 is abstract; don't strip. *)
            new_exp (CastE(t,e'))
        end
      | _, _ -> new_exp (CastE(t,e'))
    end
  | UnOp(op,e,t) ->
      let e = stripCastsDeepForPtrArith e in
      new_exp (UnOp(op, e, t))
  | BinOp(MinusPP,e1,e2,t) ->
      let e1 = stripCastsDeepForPtrArith e1 in
      let e2 = stripCastsDeepForPtrArith e2 in
      if not(compareTypesNoAttributes ~ignoreSign:false
               (typeOf e1) (typeOf e2))
      then new_exp (BinOp(MinusPP, mkCast ~e:e1 ~newt:(typeOf e2), e2, t))
      else new_exp (BinOp(MinusPP, e1, e2, t))
  | BinOp(op,e1,e2,t) ->
      let e1 = stripCastsDeepForPtrArith e1 in
      let e2 = stripCastsDeepForPtrArith e2 in
      new_exp (BinOp(op,e1,e2,t))
  | Lval lv -> new_exp (Lval(stripCastsForPtrArithLval lv))
  | AddrOf lv -> new_exp (AddrOf(stripCastsForPtrArithLval lv))
  | StartOf lv -> new_exp (StartOf(stripCastsForPtrArithLval lv))
  | _ -> e

and stripCastsForPtrArithLval (lv : lval) : lval =
  match lv with
  | (Var vi, off) -> (Var vi, stripCastsForPtrArithOff off)
  | (Mem e, off) ->
      let e = stripCastsDeepForPtrArith e in
      let off = stripCastsForPtrArithOff off in
      (Mem e, off)

and stripCastsForPtrArithOff (off : offset ) : offset =
  match off with
  | NoOffset -> NoOffset
  | Field(fi, off) -> Field(fi, stripCastsForPtrArithOff off)
  | Index(e, off) ->
      let e = stripCastsDeepForPtrArith e in
      let off = stripCastsForPtrArithOff off in
      Index(e, off)