Yes, if compiler is sure that such a optimization does not change any visible semantic of the program (see __attribute__((pure)) example below).
No in other cases. Note that function bodies from different compilation units (different .cpp file) are unknown: they are linked into binary after compilation process.
The compiler must assume worst-case scenarios such as calcCondition1 throws an exception or executes exit(). Or mutate some global state. And in such cases, the semantics must be preserved and everything must be invoked in the written order
That code
bool calcCondition1();
bool calcCondition2();
bool calcCondition3();
bool foo() {
const bool condition1 = calcCondition1();
const bool condition2 = calcCondition2();
const bool condition3 = calcCondition3();
if (condition1 && condition2 && condition3) return true;
return false;
}
and example assembly output:
foo(): # @foo()
push rbp
push rbx
push rax
call calcCondition1()@PLT
mov ebp, eax
call calcCondition2()@PLT
mov ebx, eax
call calcCondition3()@PLT
and bl, bpl
and al, bl
add rsp, 8
pop rbx
pop rbp
ret
Notice that all functions are called one by one
If compiler is sure that it is not a case (I am using a gcc extension):
__attribute__((pure)) bool calcCondition1();
__attribute__((pure)) bool calcCondition2();
__attribute__((pure)) bool calcCondition3();
bool foo() {
const bool condition1 = calcCondition1();
const bool condition2 = calcCondition2();
const bool condition3 = calcCondition3();
if (condition1 && condition2 && condition3) return true;
return false;
}
Then the generated code use the short circuit evaluation
foo(): # @foo()
push rbx
call calcCondition1()@PLT
test al, al
je .LBB0_4
call calcCondition3()@PLT
mov ebx, eax
call calcCondition2()@PLT
test bl, bl
je .LBB0_4
mov ecx, eax
mov al, 1
test cl, cl
je .LBB0_4
pop rbx
ret
.LBB0_4:
xor eax, eax
pop rbx
ret
, because compiler is sure that functions are pure, so there is a more rooms for optimizations.