Storage read optimization

Hello, I recently got feedback on a smartcontract to locally cache storage values inside variables to save some gas.

Before:

  function getTotalClaimableRewards() external view override returns (uint256) {
    if (address(_rewardToken) == address(0)) {
      return 0;
    }

    address[] memory assets = new address[](1);
    assets[0] = address(_aToken);
    uint256 freshRewards = _incentivesController.getUserRewards(
      assets,
      address(this),
      address(_rewardToken)
    );
    return _rewardToken.balanceOf(address(this)) + freshRewards;
  }

After:

  function getTotalClaimableRewards() external view override returns (uint256) {
    address rewardToken = address(_rewardToken); // local caching of storage
    if (rewardToken == address(0)) {
      return 0;
    }

    address[] memory assets = new address[](1);
    assets[0] = address(_aToken);
    uint256 freshRewards = _incentivesController.getUserRewards(
      assets,
      address(this),
      rewardToken
    );
    return IERC20(rewardToken).balanceOf(address(this)) + freshRewards;
  }

I was super surprised to see that this actually has meaningful effects on the functions gas consumption. Shouldn’t it be relatively easy for the compiler to optimize this? I would assume static code analysis could easily detect that:

  1. rewardToken isn’t mutated
  2. there’s space left on the stack

What’s the reason for not optimizing this away in the compiler?
(Tested on solc Version: 0.8.10+commit.fc410830.Linux.g++)