Description
Description
The runtime is not optimizing the instantiation of a struct (by default
) when it's actually not used at all (due to optimizations of the code that make the instance not required).
The following minimal snippet:
using SharpLab.Runtime;
public class C
{
[JitGeneric(typeof(One))]
public int New<T>()
where T : struct, IValue
=> new T().Value;
[JitGeneric(typeof(One))]
public int Default<T>()
where T : struct, IValue
=> default(T).Value;
}
public interface IValue
{
int Value { get; }
}
public struct One : IValue
{
public int Value => 1;
}
Produces this JIT asm:
C.New[[One, _]]()
L0000: mov eax, 1
L0005: ret
C.Default[[One, _]]()
L0000: push eax
L0001: xor eax, eax
L0003: mov [esp], eax
L0006: lea eax, [esp]
L0009: xor edx, edx
L000b: mov [eax], dl
L000d: mov eax, 1
L0012: pop ecx
L0013: ret
The expected code generation of C.Default[[One, _]]()
was the same as C.New[[One, _]]()
.
Configuration
Run on SharpLab (Core CLR v5.0.421.11614 on x86) on release mode.
Regression?
Don't know. Sorry.
Other information
No idea how to fix it. Sorry.
Though you currently can use new T()
instead of default(T)
to get the optimized code as a workaround.
By the way, I noticed the generated code is optimized if the type is actually returned to the caller.
For example:
[JitGeneric(typeof(One))]
public T New2<T>()
where T : struct, IValue
=> new T();
[JitGeneric(typeof(One))]
public T Default2<T>()
where T : struct, IValue
=> default(T);
Produces:
C.New2[[One, _]]()
L0000: xor eax, eax
L0002: ret
C.Default2[[One, _]]()
L0000: xor eax, eax
L0002: ret
Or when it's used as a parameter to another function (even if the function is actually inlined):
[JitGeneric(typeof(One))]
public int New3<T>()
where T : struct, IValue
=> Consume(new T());
[JitGeneric(typeof(One))]
public int Default3<T>()
where T : struct, IValue
=> Consume(default(T));
public int Consume<T>(T t)
where T : IValue
=> t.Value;
Produces:
C.New3[[One, _]]()
L0000: mov eax, 1
L0005: ret
C.Default3[[One, _]]()
L0000: mov eax, 1
L0005: ret
Or if the instance is mutated:
[JitGeneric(typeof(One))]
public int New4<T>()
where T : struct, IValue
{
T t = new T();
t.Value = 4;
return t.Value;
}
[JitGeneric(typeof(One))]
public int Default4<T>()
where T : struct, IValue
{
T t = default(T);
t.Value = 4;
return t.Value;
}
public interface IValue
{
int Value { get; set; }
}
public struct One : IValue
{
public int Value { get; set; }
}
Produces:
C.New4[[One, _]]()
L0000: mov eax, 4
L0005: ret
C.Default4[[One, _]]()
L0000: mov eax, 4
L0005: ret
So, I think the problem only happens when an instance is created but not returned, passed as parameter nor mutated.
category:cq
theme:generics
skill-level:intermediate
cost:medium
impact:small
Metadata
Metadata
Assignees
Type
Projects
Status