In functional style, the flow of values through a program is very, very visible (to both the compiler and the programmer). This gives the compiler a lot of leeway to decide where to store values, how long to keep them around, and so on.
In an imperative language, the compiler promises the programmer a model where most variables correspond to actual locations in memory which stay around for a defined lifetime. Potentially, any statement may read from (or write to!) any of these locations, so the compiler can only replace memory locations with register allocation, merge two variables into a single storage location, or perform similar optimizations after performing a painstaking analysis of where else in the program that variable may be referenced.
Now, there are two caveats:
- The programming language community has spent (wasted?) a lot of effort over the last fifty years on developing clever ways to do this analysis. There are well-known tricks like register-coloring and so forth to get some of the easier cases done most of the time; but this makes for big, slow compilers, and a constant tradeoff of complexity of compilation for quality of resulting code
- At the same time, most functional programming languages are not purely functional either; a lot of the things programs actually need to do, like respond to I/O are inherently non-functional, so no compiler can be completely free of these tricks, and no language avoids them entirely -- even Haskell, which is a bit too pure for my taste (Your Mileage May Vary) can only control and wall-off the non-functional parts of your code, not avoid them altogether.
But to answer the general question, yes, a functional paradigm gives the compiler a lot of freedom to optimize that it does not have in an imperative setting.