Hi,
My understanding of a "dead" register is a def that is never used.
However,
when I dump the MI after reg alloc on a simple program I see the following
sequence:
ADJCALLSTACKDOWN64 0, 0, 0, *implicit-def dead %rsp*, implicit-def dead
%eflags, implicit-def dead %ssp, implicit %rsp, implicit %ssp
CALL64pcrel32 @foo, <regmask %bh %bl %bp %bpl %bx %ebp %ebx %rbp %rbx %r12
%r13 %r14 %r15 %r12b %r13b %r14b %r15b %r12d %r13d %r14d %r15d %r12w %r13w
%r14w %r15w>, *implicit %rsp*, implicit %ssp, implicit-def %rsp,
implicit-def %ssp
ADJCALLSTACKUP64 0, 0, implicit-def dead %rsp, implicit-def dead %eflags,
implicit-def dead %ssp, implicit %rsp, implicit %ssp
RET 0
The ADJCALLSTACKDOWN64 has implicit-def dead %rsp. However the next
instruction,
CALL64pcrel32 has an implicit use of %rsp. This would be a use of %rsp as
defined
in ADJCALLSTACKDOWN64 making that non-dead.
So I guess my understanding of dead is incorrect. Could you please explain
what dead means?
For reference:
Source file(a.c):
void foo(void);
void boo(){ foo(); }
Commands:
clang -S -emit-llvm -Xclang -disable-O0-optnone a.c
llc -print-after="stack-slot-coloring" a.ll
--
Regards
Bhatu
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20180206/22946cd3/attachment.html>
Gordon Keiser via llvm-dev
2018-Feb-06 11:38 UTC
[llvm-dev] What does a dead register mean?
A better explanation than I would have given is here:
https://groups.google.com/forum/#!topic/llvm-dev/NlGopW6_QxE
The optimization pass that would have eliminated that was turned off by -O0
so you see that behavior. At -O2 you get a tail call
TCRETURNdi64 <ga:@foo>, 0, <regmask %BH %BL %BP %BPL %BX %DI
%DIL
%EBP %EBX %EDI %ESI %RBP %RBX %RDI %RSI %SI %SIL %R12 %R13 %R14 %R15 %XMM6
%XMM7 %XMM8 %XMM9 %XMM10 %XMM11 %XMM12 %XMM13 %XMM14 %XMM15 %R12B %R13B
%R14B %R15B %R12D %R13D %R14D %R15D %R12W %R13W %R14W %R15W>,
%RSP<imp-use>
O0 didn't optimize away the usage with no side effects, so it produces the
assembly:
boo: # @boo
# BB#0:
call foo
nop
add rsp, 40
ret
O2 recognizes that the ret and rsp adjustment can move straight into foo()
which then does all of the things boo would have done if it had locals that
were used or parameters. The
boo: # @boo
# BB#0:
jmp foo # TAILCALL
It's also related to why you see both sp and rsp being marked as
modified. They technically are, but since one is a subregister it doesn't
need to be explicitly marked. Here's an O0 example of how bad O0 generated
code can end up. Note that there aren't any uses of the return from foo,
and it doesn't have side effects:
C file:
int foo(long x, long y, long z)
{
int retVal = x * y + z;
int* unused = &retVal;
return retVal;
}
void boo()
{
int x = 1;
int* y = &x;
int z = *y;
foo(x, *y, z);
}
-O0 version
============================================================foo:
# @foo
.Lcfi0:
.seh_proc foo
# BB#0:
sub rsp, 24
.Lcfi1:
.seh_stackalloc 24
.Lcfi2:
.seh_endprologue
mov dword ptr [rsp + 8], r8d
mov dword ptr [rsp + 4], edx
mov dword ptr [rsp + 12], ecx
imul ecx, dword ptr [rsp + 4]
add ecx, dword ptr [rsp + 8]
mov dword ptr [rsp], ecx
mov rax, rsp
mov qword ptr [rsp + 16], rax
mov eax, dword ptr [rsp]
add rsp, 24
ret
.seh_handlerdata
.text
.Lcfi3:
.seh_endproc
.def boo;
.scl 2;
.type 32;
.endef
.globl boo
.p2align 4, 0x90
boo: # @boo
.Lcfi4:
.seh_proc boo
# BB#0:
sub rsp, 56
.Lcfi5:
.seh_stackalloc 56
.Lcfi6:
.seh_endprologue
mov dword ptr [rsp + 36], 1
lea rax, [rsp + 36]
mov qword ptr [rsp + 40], rax
mov r8d, dword ptr [rsp + 36]
mov dword ptr [rsp + 52], r8d
mov rax, qword ptr [rsp + 40]
mov edx, dword ptr [rax]
mov ecx, dword ptr [rsp + 36]
call foo
nop
add rsp, 56
ret
.seh_handlerdata
.text
.Lcfi7:
.seh_endproc
==================================-O2 version
Note. EDX is neither kill or def here because it always has the same value
as x in this case, but if it didn't it would still get passed in by calling
convention.
foo: # @foo
# BB#0:
# kill: %R8D<def> %R8D<kill>
%R8<def>
# kill: %ECX<def> %ECX<kill>
%RCX<def>
imul ecx, edx
lea eax, [rcx + r8]
ret
.def boo;
.scl 2;
.type 32;
.endef
.globl boo
.p2align 4, 0x90
boo: # @boo
# BB#0:
ret
The only reason the body of foo exists at all is because the optimizer
can't be certain that another function won't call it and expect to get
the
address of the local. It does know that boo() does nothing with the value,
doesn't return anything, and thus doesn't need to call the function.
SO
basically the short version of all that is that O0 accepts the code at
face value and does things that aren't necesssary because it doesn't
have
the analysis it needs to remove them. It also produces things like the
following sequence:
mov dword ptr [rsp], ecx
mov rax, rsp
mov qword ptr [rsp + 16], rax
mov eax, dword ptr [rsp]
which is due to an old C standard item that states that non ptr / ref input
parameters may be used as temporaries. It calculated the local retVal into
ecx, had to store it in the last slot because there was nothing telling it
it wouldn't be used later, then stored the pointer to itself (esp) at
[esp], which was never used.
Apologies if that was a simple example, but I like using it to show people
how much optimizing compilers do. I've been able to use a similar
description to teach a couple of people why optimizations were needed.
Back to your question the implicit / imp-def / dead / etc you're seeing are
just artifacts of all that clang knows when you disable its capabilities.
They would normally be used in passes later to produce the more optimal
form.
It takes getting used to the syntax but it makes sense.
Cheers,
Gordon Keiser
Software Development Engineer, Supposedly
On Mon, Feb 5, 2018 at 11:14 PM, Bhatu via llvm-dev <llvm-dev at
lists.llvm.org> wrote:
> Hi,
>
> My understanding of a "dead" register is a def that is never
used. However,
> when I dump the MI after reg alloc on a simple program I see the following
> sequence:
>
> ADJCALLSTACKDOWN64 0, 0, 0, *implicit-def dead %rsp*, implicit-def dead
> %eflags, implicit-def dead %ssp, implicit %rsp, implicit %ssp
> CALL64pcrel32 @foo, <regmask %bh %bl %bp %bpl %bx %ebp %ebx %rbp %rbx
%r12
> %r13 %r14 %r15 %r12b %r13b %r14b %r15b %r12d %r13d %r14d %r15d %r12w %r13w
> %r14w %r15w>, *implicit %rsp*, implicit %ssp, implicit-def %rsp,
> implicit-def %ssp
> ADJCALLSTACKUP64 0, 0, implicit-def dead %rsp, implicit-def dead %eflags,
> implicit-def dead %ssp, implicit %rsp, implicit %ssp
> RET 0
>
>
> The ADJCALLSTACKDOWN64 has implicit-def dead %rsp. However the next
> instruction,
> CALL64pcrel32 has an implicit use of %rsp. This would be a use of %rsp as
> defined
> in ADJCALLSTACKDOWN64 making that non-dead.
>
> So I guess my understanding of dead is incorrect. Could you please explain
> what dead means?
>
>
> For reference:
> Source file(a.c):
> void foo(void);
> void boo(){ foo(); }
>
> Commands:
> clang -S -emit-llvm -Xclang -disable-O0-optnone a.c
> llc -print-after="stack-slot-coloring" a.ll
>
> --
> Regards
> Bhatu
>
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL:
<http://lists.llvm.org/pipermail/llvm-dev/attachments/20180206/c4984d12/attachment.html>
Krzysztof Parzyszek via llvm-dev
2018-Feb-06 14:10 UTC
[llvm-dev] What does a dead register mean?
You are right about your interpretation of "dead". The case here is that RSP is a reserved register and so its liveness isn't really tracked. The "implicit-def dead" is an idiom used to mean that the register (reserved or not) is clobbered. The other implicit uses/defs can come from instruction definitions to indicate that this instruction uses and/or modifies a given register (regardless of its explicit operands), but for reserved registers there doesn't need to be any continuity between the defs and the uses. -Krzysztof On 2/5/2018 11:14 PM, Bhatu via llvm-dev wrote:> Hi, > > My understanding of a "dead" register is a def that is never used. However, > when I dump the MI after reg alloc on a simple program I see the > following sequence: > > ADJCALLSTACKDOWN64 0, 0, 0, *implicit-def dead %rsp*, implicit-def dead > %eflags, implicit-def dead %ssp, implicit %rsp, implicit %ssp > CALL64pcrel32 @foo, <regmask %bh %bl %bp %bpl %bx %ebp %ebx %rbp %rbx > %r12 %r13 %r14 %r15 %r12b %r13b %r14b %r15b %r12d %r13d %r14d %r15d > %r12w %r13w %r14w %r15w>, *implicit %rsp*, implicit %ssp, implicit-def > %rsp, implicit-def %ssp > ADJCALLSTACKUP64 0, 0, implicit-def dead %rsp, implicit-def dead > %eflags, implicit-def dead %ssp, implicit %rsp, implicit %ssp > RET 0 > > > The ADJCALLSTACKDOWN64 has implicit-def dead %rsp. However the next > instruction, > CALL64pcrel32 has an implicit use of %rsp. This would be a use of %rsp > as defined > in ADJCALLSTACKDOWN64 making that non-dead. > > So I guess my understanding of dead is incorrect. Could you please > explain what dead means? > > > For reference: > Source file(a.c): > void foo(void); > void boo(){ foo(); } > > Commands: > clang -S -emit-llvm -Xclang -disable-O0-optnone a.c > llc -print-after="stack-slot-coloring" a.ll > > -- > Regards > Bhatu > > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-- Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
Thank you for the explanation! On Tue, Feb 6, 2018 at 7:40 PM, Krzysztof Parzyszek via llvm-dev < llvm-dev at lists.llvm.org> wrote:> You are right about your interpretation of "dead". The case here is that > RSP is a reserved register and so its liveness isn't really tracked. The > "implicit-def dead" is an idiom used to mean that the register (reserved or > not) is clobbered. The other implicit uses/defs can come from instruction > definitions to indicate that this instruction uses and/or modifies a given > register (regardless of its explicit operands), but for reserved registers > there doesn't need to be any continuity between the defs and the uses. > > -Krzysztof > > > On 2/5/2018 11:14 PM, Bhatu via llvm-dev wrote: > >> Hi, >> >> My understanding of a "dead" register is a def that is never used. >> However, >> when I dump the MI after reg alloc on a simple program I see the >> following sequence: >> >> ADJCALLSTACKDOWN64 0, 0, 0, *implicit-def dead %rsp*, implicit-def dead >> %eflags, implicit-def dead %ssp, implicit %rsp, implicit %ssp >> CALL64pcrel32 @foo, <regmask %bh %bl %bp %bpl %bx %ebp %ebx %rbp %rbx >> %r12 %r13 %r14 %r15 %r12b %r13b %r14b %r15b %r12d %r13d %r14d %r15d %r12w >> %r13w %r14w %r15w>, *implicit %rsp*, implicit %ssp, implicit-def %rsp, >> implicit-def %ssp >> ADJCALLSTACKUP64 0, 0, implicit-def dead %rsp, implicit-def dead %eflags, >> implicit-def dead %ssp, implicit %rsp, implicit %ssp >> RET 0 >> >> >> The ADJCALLSTACKDOWN64 has implicit-def dead %rsp. However the next >> instruction, >> CALL64pcrel32 has an implicit use of %rsp. This would be a use of %rsp as >> defined >> in ADJCALLSTACKDOWN64 making that non-dead. >> >> So I guess my understanding of dead is incorrect. Could you please >> explain what dead means? >> >> >> For reference: >> Source file(a.c): >> void foo(void); >> void boo(){ foo(); } >> >> Commands: >> clang -S -emit-llvm -Xclang -disable-O0-optnone a.c >> llc -print-after="stack-slot-coloring" a.ll >> >> -- >> Regards >> Bhatu >> >> >> _______________________________________________ >> LLVM Developers mailing list >> llvm-dev at lists.llvm.org >> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >> >> > -- > Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted > by The Linux Foundation > > _______________________________________________ > LLVM Developers mailing list > llvm-dev at lists.llvm.org > http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >-- Regards Bhatu -------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20180207/ca34f9d7/attachment.html>