Description
Currently if you newobj a struct via a constructor call the jit assumes the struct's old value may be read during the construction of the new value. So the pattern emitted is:
;; zero temp obj storage
;; call constructor on temp obj
;; copy temp obj to the destination
When the final destination is register promoted all this tends to get cleaned up, but when the final destination can't be register promoted the code the jit produces can be surprisingly large and slow.
For instance, given
ValueTask<int> Work() => new ValueTask<int>(42);
ValueTaskAwaiter<int> vt = Work().GetAwaiter();
... vt.GetResult();
The jit may produce code like:
33C9 xor rcx, rcx
B82A000000 mov eax, 42
488D542420 lea rdx, bword ptr [rsp+20H]
C4E17957C0 vxorpd xmm0, xmm0
C4E17A7F02 vmovdqu qword ptr [rdx], xmm0
488D542420 lea rdx, bword ptr [rsp+20H]
48890A mov gword ptr [rdx], rcx
894208 mov dword ptr [rdx+8], eax
C4E17A6F442420 vmovdqu xmm0, qword ptr [rsp+20H]
C4E17A7F442430 vmovdqu qword ptr [rsp+30H], xmm0
In most cases the aliasing the jit is worried about does not happen, and the new value could be constructed directly into the destination. And also in most cases this construction will fully write the fields of the struct so the zero initialization is not necessary.
category:cq
theme:structs
skill-level:expert
cost:large
impact:large