I was playing with C# and stumbled upon this case:
static int F(int n)
{
if (n == 1)
return 1;
return 1;
}
this generates what you would expect:
<Program>$.<<Main>$>g__F|0_0(Int32)
L0000: mov eax, 1
L0005: ret
as you can see the compiler understood that the if is pretty much useless and "removed" that.
now, let's try adding more ifs:
static int G(int n)
{
if (n == 1)
return 1;
if (n == 1)
return 1;
return 1;
}
now this generates the following ASM:
<Program>$.<<Main>$>g__G|0_1(Int32)
L0000: cmp ecx, 1 ; do we need this?
L0003: jne short L000b ; do we need this?
L0005: mov eax, 1
L000a: ret
L000b: mov eax, 1
L0010: ret
The weird thing is: when you add >= 5 branches then it again understands that they are not needed.
static int H(int n)
{
if (n == 1)
return 1;
if (n == 1)
return 1;
if (n == 1)
return 1;
if (n == 1)
return 1;
if (n == 1)
return 1;
return 1;
}
output:
<Program>$.<<Main>$>g__H|0_2(Int32)
L0000: mov eax, 1
L0005: ret
Question
- Is there a reason why it generates extra instructions in the second case?
Notes
- SharpLab link if you want to play with it.
- This is what
GCC (-O2)generates usingC:
int
f(int n) {
if (n == 1)
return 1;
return 1;
}
int
g(int n) {
if (n == 1)
return 1;
if (n == 1)
return 1;
return 1;
}
int
h(int n) {
if (n == 1)
return 1;
if (n == 1)
return 1;
if (n == 1)
return 1;
if (n == 1)
return 1;
if (n == 1)
return 1;
return 1;
}
which generates:
f:
mov eax, 1
ret
g:
mov eax, 1
ret
h:
mov eax, 1
ret
and here is the Godbolt link.
- IL Code of the functions:
.method assembly hidebysig static
int32 '<<Main>$>g__F|0_0' (
int32 n
) cil managed
{
// Method begins at RVA 0x2052
// Code size 6 (0x6)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: pop
IL_0003: pop
IL_0004: ldc.i4.1
IL_0005: ret
} // end of method '<Program>$'::'<<Main>$>g__F|0_0'
.method assembly hidebysig static
int32 '<<Main>$>g__G|0_1' (
int32 n
) cil managed
{
// Method begins at RVA 0x2059
// Code size 12 (0xc)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: bne.un.s IL_0006
IL_0004: ldc.i4.1
IL_0005: ret
IL_0006: ldarg.0
IL_0007: ldc.i4.1
IL_0008: pop
IL_0009: pop
IL_000a: ldc.i4.1
IL_000b: ret
} // end of method '<Program>$'::'<<Main>$>g__G|0_1'
.method assembly hidebysig static
int32 '<<Main>$>g__H|0_2' (
int32 n
) cil managed
{
// Method begins at RVA 0x2066
// Code size 30 (0x1e)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.1
IL_0002: bne.un.s IL_0006
IL_0004: ldc.i4.1
IL_0005: ret
IL_0006: ldarg.0
IL_0007: ldc.i4.1
IL_0008: bne.un.s IL_000c
IL_000a: ldc.i4.1
IL_000b: ret
IL_000c: ldarg.0
IL_000d: ldc.i4.1
IL_000e: bne.un.s IL_0012
IL_0010: ldc.i4.1
IL_0011: ret
IL_0012: ldarg.0
IL_0013: ldc.i4.1
IL_0014: bne.un.s IL_0018
IL_0016: ldc.i4.1
IL_0017: ret
IL_0018: ldarg.0
IL_0019: ldc.i4.1
IL_001a: pop
IL_001b: pop
IL_001c: ldc.i4.1
IL_001d: ret
} // end of method '<Program>$'::'<<Main>$>g__H|0_2'