#include <bits/stdc++.h>
// #include <stdio.h>
// #include <iostream>
// #include <vector>
// #include <string>
// #include <tuple>
// #include <map>
// #include <math.h>
// #include <algorithm> // std::sort, std::min, std::next_permutation, std::lower_bound
// #include <iomanip> // std::setprecision
// #include <cmath> // std::ceil
// #include <numeric> // std::gcd, std::lcm
// #include <limits> // std::numeric_limits<long int>::max()
// #include <unordered_map>
// #include <map>
// #include <cmath> // std::atan2
// #include <deque>
// #include <queue>
// #include <bitset>
// #include <functional>
// #include <cassert>

using namespace std;
typedef long long ll;
typedef std::vector<long long> vll;
typedef std::vector<std::vector<long long>> vvll;
typedef std::vector<bool> vb;
typedef std::vector<std::vector<bool>> vvb;
#define INF 1999999999
#define MODA 1000000007 
#define rep(i,n) for (long long i = 0; i < (n); ++i)
#define rep1(i,n) for (long long i = 1; i <= (n); ++i)
#define all(x) (x).begin(),(x).end()
#define errvn1(x) cerr << #x <<" "<< x << endl;
#define errvn2(x, y) cerr << #x <<" "<< x <<" "<< #y <<" "<< y << endl;
#define errvn3(x, y, z) cerr << #x <<":"<< x <<" "<< #y <<":"<< y <<" "<< #z <<":"<< z << endl;
#define errvn4(x, y, z, a) cerr << #x <<":"<< x <<" "<< #y <<":"<< y <<" "<< #z <<":"<< z <<" "<< #a <<":"<< a << endl;
#define errop2(x, y) cerr << x <<" "<< y << endl;
#define errop3(x, y, z) cerr << x <<" "<< y <<" "<< z << endl;
#define errop4(x, y, z, a) cerr << x <<" "<< y <<" "<< z <<" "<< a << endl;

template<typename T>
T powint (const T& x, const T& y) {
  T ret = 1;
  for(int i=0; i<y; ++i) ret *= x;
  return ret;
}

long long factorial(long long X) {
  long long ans = 1;
  for(int i=1; i<X+1 ; ++i) ans *= i;
  return ans;
}

// This function seems to be able to use upto n=50.
long long combination(long long n, long long r) {
  long long ans = 1;
  for (long long i = 1; i <= r; ++i) {
    ans *= n+1-i;
    ans /= i;
  }
  return ans;
}

std::map<long long, long long> prime_factor(long long n) {
  std::map<long long, long long> ret; // ret.first:prime_number, ret.second:count
  for(long long i = 2; i * i <= n; ++i) {
    while(n % i == 0) {
      ret[i]++;
      n /= i;
    }
  }
  if(n != 1) ret[n] = 1; // if n is prime number.
  return ret;
}

long long modinv(long long a, long long m) {
    long long b = m, u = 1, v = 0;
    while (b) {
        long long t = a / b;
        a -= t * b; swap(a, b);
        u -= t * v; swap(u, v);
    }
    u %= m;
    if (u < 0) u += m;
    return u;
}

template<typename T = long long, typename T2 = long long>
std::vector<std::vector<T>> convert_rooted_tree(const std::vector<std::vector<T>> &tree, T2 root = 0) {
  T N = tree.size();
  std::vector<std::vector<T>> rooted_tree(N);
  vector<bool> foundflag(N);
  foundflag[root] = true;
  queue<T> que;
  que.emplace(root);
  while(!que.empty()) {
    auto p = que.front();
    que.pop();
    for(auto &to : tree[p]) {
      // The case using vector<vector<pair<ll, ll> > > as tree
      // (ex, need to set the cost of links to pair.second),
      // replace index of foundflag and que from to -> to.first
      if(foundflag[to] == false) {
        foundflag[to] = true;
        que.emplace(to);
        rooted_tree[p].emplace_back(to);
      }
    }
  }
  return rooted_tree;
}

template <typename T = long long>
struct UnionFind {
  vector<T> data;
  UnionFind(T sz) {
    data.assign(sz, -1);
  }
  bool unite(T x, T y) {
    x = find(x), y = find(y);
    if(x == y) return (false);
    if(data[x] > data[y]) swap(x, y);
    data[x] += data[y];
    data[y] = x;
    return (true);
  }
  T find(T k) {
    if(data[k] < 0) return (k);
    return (data[k] = find(data[k]));
  }
  T size(T k) {
    return (-data[find(k)]);
  }
};

// usage:
// foreach_comb<long long, long long>(n, k, [&](vector<long long> ind) {
//   operations here;
// });
template<typename T1 = long long, typename T2 = long long>
void recursive_comb(vector<T2> indexes, T1 s, T1 rest, std::function<void(vector<T2>)> func) {
  if(rest==0) {
    func(indexes);
  }
  else {
    if (s < 0) return;
    recursive_comb(indexes, s-1, rest, func);
    indexes[rest-1] = s;
    recursive_comb(indexes, s-1, rest-1, func);
  }
}
template<typename T1 = long long, typename T2 = long long>
void foreach_comb(T1 n, T1 k, std::function<void(vector<T2>)> func) {
  vector<T2> indexes(k);
  recursive_comb(indexes, n-1, k, func);
}

// usage:
// foreach_permutation<long long, long long>(n, [&](vector<long long> ind) {
//   operations here;
// });
template<typename T1 = long long, typename T2 = long long>
void foreach_permutation(T1 n, std::function<void(vector<T2>)> func) {
  vector<T2> indexes(n);
  for (T2 i = 0; i < n; ++i) indexes[i] = i;
  do {
    func(indexes);
  } while (std::next_permutation(indexes.begin(), indexes.end()));
}

// usage:
// long long ind = binary_search_org<long long>(
//                   -1, vec.size(),
//                   [&](long long mid){
//                     conditions here;
//                   });
// To capture the cases which all indexes satisfy or not satisfy the condition,
// initial ng and ok should be set out of boundary.
template<typename T = long long>
T binary_search_org(T ng, T ok, std::function<bool(T)> func) {
  T out_range = ok;
  while (std::abs(ok - ng) > 1) {
    T mid = ng + (ok - ng) / 2;
    if(func(mid)) ok = mid;
    else ng = mid;
  }
  assert(ok != out_range); // Some cases should return true.
  return ok;
}

// https://qiita.com/ue_sho/items/1ee5c3e665c72c035880
// const int mod = 1000000007;
class mint {
  long long x;
 public:
  mint(long long x=0) : x((x%MODA+MODA)%MODA) {}
  mint operator-() const { 
    return mint(-x);
  }
  mint& operator+=(const mint& a) {
    if ((x += a.x) >= MODA) x -= MODA;
    return *this;
  }
  mint& operator-=(const mint& a) {
    if ((x += MODA-a.x) >= MODA) x -= MODA;
    return *this;
  }
  mint& operator*=(const  mint& a) {
    (x *= a.x) %= MODA;
    return *this;
  }
  mint operator+(const mint& a) const {
    mint res(*this);
    return res+=a;
  }
  mint operator-(const mint& a) const {
    mint res(*this);
    return res-=a;
  }
  mint operator*(const mint& a) const {
    mint res(*this);
    return res*=a;
  }
  mint pow(ll t) const {
    if (!t) return 1;
    mint a = pow(t>>1);
    a *= a;
    if (t&1) a *= *this;
    return a;
  }
  // for prime mod
  mint inv() const {
    return pow(MODA-2);
  }
  mint& operator/=(const mint& a) {
    return (*this) *= a.inv();
  }
  mint operator/(const mint& a) const {
    mint res(*this);
    return res/=a;
  }

  friend ostream& operator<<(ostream& os, const mint& m){
    os << m.x;
    return os;
  }
};

int main() {
  ll H, W;
  cin >> H >> W;
  vector<string> grid(H);
  rep(i, H) cin >> grid[i];

  ll blackcount = 0;
  rep(i, H) {
    rep(j, W) {
      if (grid[i][j] == '#') blackcount++;
    }
  }
  std::vector<std::vector<long long>> mv;
  mv.push_back({1, 0});
  mv.push_back({-1, 0});
  mv.push_back({0, 1});
  mv.push_back({0, -1});
  std::vector<std::vector<long long>> dist(H);
  for(long long i=0; i<H; ++i) dist[i].resize(W, -1);
  queue<vll> que;
  que.push({0, 0});
  dist[0][0] = 1;
  while (!que.empty()) {
    std::vector<long long> yx = que.front();
    que.pop();
    for (auto m : mv) {
      long long my = yx[0] + m[0];
      long long mx = yx[1] + m[1];
      if (my >= 0 && my < H && mx >= 0 && mx < W) {
        if (grid[my][mx] != '#') {
          if (dist[my][mx] > dist[yx[0]][yx[1]] + 1 || dist[my][mx] == -1) {
            dist[my][mx] = dist[yx[0]][yx[1]] + 1;
            que.push({my, mx});
          }
        }
      }
    }
  }
  if (dist[H-1][W-1] != -1) {
    ll ans = H * W - blackcount - dist[H-1][W-1];
    cout << ans << endl;
  }
  else {
    cout << -1 << endl;
  }
  return 0;
}



Measured memory_bytes: 3636