Not really. Inlining can be done by mechanically incorporating the called function into the caller, in a way that respects lexical scopes. This will result in identical semantics. Then the resulting bloated code is optimized: subject to caching optimizations, common subexpression optimizations and whatnot. These have to obey the usual rules for not deleting or reordering side effects. The issue you're referring to boils down to the fact that in an imperative language, we cannot assume that two occurrences of a variable X can be replaced by the same value, because X may have been assigned in between. So compilers have to do flow analysis to discover in what part of a call graph does a given variable have a stable value.
E.g. if we have a silly C function like
int accumulate(int x, int y)
{
global += (x + y);
}
and then call it in two places:
accumulate(s, t);
accumulate(u, v);
the inlining is almost like just macro-expansion of the above into:
{
int x = s; /* "parameter" passing */
int y = t;
global += (x + y);
}
{
int x = u; /* "parameter" passing */
int y = v;
global += (x + y);
}
we don't have to care about side effects when we are doing this expansion. That's the job of later optimization.
The later optimization pass can worry about things like whether global is volatile-qualified. If it is, then the best that can be done is:
global += s + t;
global += u + v;
these can't be merged or re-ordered. And so this stays faithful to the original function calls. If global isn't volatile, then more can be done:
global += s + t + u + v;