Clean up CUDA executable finding logic
Clean up CUDA executable finding logic

This is cleaning up the logic around `FindCudaExecutable` with the eventual
goal to add version checking (not in this change).

1. I move static caching from `GetToolVersionString` into `GetToolVersion` because
`GetToolVersion` is the only caller of `GetToolVersionString`. It also means we cache the much smaller version number instead an arbitrary long string.

2. I move the check whether a binary exists from `FindCudaExecutable` into `GetToolVersionString`.
I also added an explanation why we are doing this in the first place.

3. I remove caching from `FindCudaExecutable` entirely. The only expensive operation in `FindCudaExecutable`
is the call to `GetToolVersion` which is already cached. What remains is a loop over 10-ish binary paths
that is executed once per compilation. If it turns out that we really need some caching we should move it into NVPTXCompiler and determine the paths only once per lifetime of NVPTXCompiler.
