[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Lightning] Example with putarg
From: |
Paulo César Pereira de Andrade |
Subject: |
Re: [Lightning] Example with putarg |
Date: |
Wed, 13 Sep 2017 16:29:26 -0400 |
2017-09-07 4:30 GMT-04:00 Marc Nieper-Wißkirchen <address@hidden>:
> Hi Paulo,
Hi Marc,
I said I would look into it recently in another (private) email, and had some
ideas of what could be wrong.
> I've did some more experiments with putarg. To recapitulate: The manual
> warns that register arguments have volatile values but does not specify
> exactly when the value may be overwritten.
Actually, I believe there is nothing wrong. But likely there is some
misinterpretation of what happens when there is a jump to a register
or an indirect jump to an absolute address (not a jit_label_t*), or, what
lightning consider as live range or how it computes it.
> In the factorial example in the manual, the following code snippet is
> contained:
>
> putargr R0, ac ! Update the accumulator
> subi R1, R1, 1 ! argument -= 1
> putargr R1, in ! Update the argument
>
> Thus, it is assumed that the "subi" instruction does not overwrite the value
> in "ac". But this is a very dangerous assumption.
>
> If the constant 1 was replaced by a constant not fitting in a
> 32-bit-integer, at least on x64, a scratch register is needed, which may be
> taken from the list of argument registers. And in fact, the following
> (stupid) example produces "wrong" code on x64:
>
> proc:
> prolog
> tramp 64
>
> arg $a
> arg $b
> arg $c
> arg $d
> arg $e
> arg $f
>
> getarg %r0 $a
> getarg %r1 $b
> getarg %r(3) $c
>
> putargr %r0 $d
> putargr %r0 $e
> putargr %r0 $f
>
> putargr %r0 $c
> subi %r1 %r1 123456789123456 // Overwrites %r9, which is $f
> putargr %r1 $a
> putargr %r2 $b
>
> jmpr %r(3)
> epilog
>
> I see only two ways out here: Either the documentation should state that
> register arguments can be overwritten by *any* instruction except for
> "jmpr", "st(x)" and "ld(x)" (the latter are needed to transfer back the
> register arguments to non-volatile memory), or GNU lightning should make
> register arguments non-volatile if it cannot prove that they are dead.
>
> In the first case, the factorial example should be rewritten, by moving the
> subi instruction before the first putargr.
>
> The latter case is, in my opinion, a much better solution. First of all, it
> abstracts away part of the difference between register arguments and stack
> arguments because the latter are always non-volatile. Secondly, a GNU
> lightning's functions are modeled as C functions (without varargs, though),
> it would meet the expectation that arguments are preserved throughout a
> function (can be read using getarg and written using putarg, throughout)
> much like callee-saved register as arguments to C functions can also be
> accessed at any time.
If an argument is not used, and it is in a register, the register is used
for temporaries, but argument registers are used last, when running
out of temporaries. This was a tough decision, but without these assumptions,
lightning would need to spill/reload *all* temporaries.
BTW, actually, not yet released version of lightning, with several bug
fixes, supports varargs functions :) See the test case at:
http://git.savannah.gnu.org/cgit/lightning.git/tree/check/va_list.tst
> (On machines with register arguments, this also allows user code to make use
> of these registers, which were otherwise mostly wasted.)
Lets suppose I run your example, with --enable-devel-disassembler, and
in the check subdirectory of a git checkout:
"""
$ cat t.tst
.disasm
.code
proc:
prolog
tramp 64
arg $a
arg $b
arg $c
arg $d
arg $e
arg $f
getarg %r0 $a
getarg %r1 $b
getarg %r(3) $c
putargr %r0 $d
putargr %r0 $e
putargr %r0 $f
putargr %r0 $c
subi %r1 %r1 123456789123456 // Overwrites %r9, which is $f
putargr %r1 $a
putargr %r2 $b
jmpr %r(3)
epilog
$ ./lightning -v t.tst
#note t.tst:3
L1: %r11 %rbx %r13 %r14 %r15 %rdx %rsi %rdi %rsp %rbp /* prolog */
arg #1
arg #2
arg #3
arg #4
arg #5
arg #6
getarg_l %rax #1
\__ movr %rax %rdi
getarg_l %r10 #2
\__ movr %r10 %rsi
getarg_l %r12 #3
\__ movr %r12 %rdx
putargr %rax #4
\__ movr %rcx %rax
putargr %rax #5
\__ movr %r8 %rax
putargr %rax #6
\__ movr %r9 %rax
putargr %rax #3
\__ movr %rdx %rax
subi %r10 %r10 0x7048860f9180
putargr %r10 #1
\__ movr %rdi %r10
putargr %r11 #2
\__ movr %rsi %r11
jmpr %r12
L2: /* epilog */
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#note t.tst:3
L1: %r11 %rbx %r13 %r14 %r15 %rdx %rsi %rdi %rsp %rbp /* prolog */
arg #1
arg #2
arg #3
arg #4
arg #5
arg #6
getarg_l %rax #1
\__ movr %rax %rdi
# :t.tst:3
0x7fb1b537f000 mov %rdi,%rax
getarg_l %r10 #2
\__ movr %r10 %rsi
0x7fb1b537f003 mov %rsi,%r10
getarg_l %r12 #3
\__ movr %r12 %rdx
0x7fb1b537f006 mov %rdx,%r12
putargr %rax #4
\__ movr %rcx %rax
0x7fb1b537f009 mov %rax,%rcx
putargr %rax #5
\__ movr %r8 %rax
0x7fb1b537f00c mov %rax,%r8
putargr %rax #6
\__ movr %r9 %rax
0x7fb1b537f00f mov %rax,%r9
putargr %rax #3
\__ movr %rdx %rax
0x7fb1b537f012 mov %rax,%rdx
subi %r10 %r10 0x7048860f9180
0x7fb1b537f015 movabs $0x7048860f9180,%r9
0x7fb1b537f01f sub %r9,%r10
putargr %r10 #1
\__ movr %rdi %r10
0x7fb1b537f022 mov %r10,%rdi
putargr %r11 #2
\__ movr %rsi %r11
0x7fb1b537f025 mov %r11,%rsi
jmpr %r12
0x7fb1b537f028 rex.WB jmpq *%r12
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"""
%r9 is clobbered, but as you can see, its value is not used.
Now suppose I pretend %r9 value is used:
"""
$ diff -u t.tst t1.tst
--- t.tst 2017-09-13 13:05:18.899883342 -0400
+++ t1.tst 2017-09-13 13:07:52.984889243 -0400
@@ -17,6 +17,7 @@
putargr %r0 $f
putargr %r0 $c
subi %r1 %r1 123456789123456 // Overwrites %r9, which is $f
+ getarg %r0 $f
putargr %r1 $a
putargr %r2 $b
jmpr %r(3)
$ ./lightning -v t1.tst
#note t1.tst:3
L1: %r11 %rbx %r13 %r14 %r15 %rdx %rsi %rdi %rsp %rbp /* prolog */
arg #1
arg #2
arg #3
arg #4
arg #5
arg #6
getarg_l %rax #1
\__ movr %rax %rdi
getarg_l %r10 #2
\__ movr %r10 %rsi
getarg_l %r12 #3
\__ movr %r12 %rdx
putargr %rax #4
\__ movr %rcx %rax
putargr %rax #5
\__ movr %r8 %rax
putargr %rax #6
\__ movr %r9 %rax
putargr %rax #3
\__ movr %rdx %rax
subi %r10 %r10 0x7048860f9180
getarg_l %rax #6
\__ movr %rax %r9
putargr %r10 #1
\__ movr %rdi %r10
putargr %r11 #2
\__ movr %rsi %r11
jmpr %r12
L2: /* epilog */
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#note t1.tst:3
L1: %r11 %rbx %r13 %r14 %r15 %rdx %rsi %rdi %rsp %rbp /* prolog */
arg #1
arg #2
arg #3
arg #4
arg #5
arg #6
getarg_l %rax #1
\__ movr %rax %rdi
# :t1.tst:3
0x7f2198a82000 mov %rdi,%rax
getarg_l %r10 #2
\__ movr %r10 %rsi
0x7f2198a82003 mov %rsi,%r10
getarg_l %r12 #3
\__ movr %r12 %rdx
0x7f2198a82006 mov %rdx,%r12
putargr %rax #4
\__ movr %rcx %rax
0x7f2198a82009 mov %rax,%rcx
putargr %rax #5
\__ movr %r8 %rax
0x7f2198a8200c mov %rax,%r8
putargr %rax #6
\__ movr %r9 %rax
0x7f2198a8200f mov %rax,%r9
putargr %rax #3
\__ movr %rdx %rax
0x7f2198a82012 mov %rax,%rdx
subi %r10 %r10 0x7048860f9180
0x7f2198a82015 movabs $0x7048860f9180,%r8
0x7f2198a8201f sub %r8,%r10
getarg_l %rax #6
\__ movr %rax %r9
0x7f2198a82022 mov %r9,%rax
putargr %r10 #1
\__ movr %rdi %r10
0x7f2198a82025 mov %r10,%rdi
putargr %r11 #2
\__ movr %rsi %r11
0x7f2198a82028 mov %r11,%rsi
jmpr %r12
0x7f2198a8202b rex.WB jmpq *%r12
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"""
Now it overwrites %r8, because it is not used. So, lets
pretend all argument registers are used *after* the subi:
"""
$ diff -u t.tst t2.tst
--- t.tst 2017-09-13 13:05:18.899883342 -0400
+++ t2.tst 2017-09-13 13:09:44.664893519 -0400
@@ -17,6 +17,12 @@
putargr %r0 $f
putargr %r0 $c
subi %r1 %r1 123456789123456 // Overwrites %r9, which is $f
+ getarg %r0 $a
+ getarg %r0 $b
+ getarg %r0 $c
+ getarg %r0 $d
+ getarg %r0 $e
+ getarg %r0 $f
putargr %r1 $a
putargr %r2 $b
jmpr %r(3)
$ ./lightning -v t2.tst
#note t2.tst:3
L1: %r11 %rbx %r13 %r14 %r15 %rdx %rsi %rdi %rsp %rbp /* prolog */
arg #1
arg #2
arg #3
arg #4
arg #5
arg #6
getarg_l %rax #1
\__ movr %rax %rdi
getarg_l %r10 #2
\__ movr %r10 %rsi
getarg_l %r12 #3
\__ movr %r12 %rdx
putargr %rax #4
\__ movr %rcx %rax
putargr %rax #5
\__ movr %r8 %rax
putargr %rax #6
\__ movr %r9 %rax
putargr %rax #3
\__ movr %rdx %rax
subi %r10 %r10 0x7048860f9180
getarg_l %rax #1
\__ movr %rax %rdi
getarg_l %rax #2
\__ movr %rax %rsi
getarg_l %rax #3
\__ movr %rax %rdx
getarg_l %rax #4
\__ movr %rax %rcx
getarg_l %rax #5
\__ movr %rax %r8
getarg_l %rax #6
\__ movr %rax %r9
putargr %r10 #1
\__ movr %rdi %r10
putargr %r11 #2
\__ movr %rsi %r11
jmpr %r12
L2: /* epilog */
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#note t2.tst:3
L1: %r11 %rbx %r13 %r14 %r15 %rdx %rsi %rdi %rsp %rbp /* prolog */
arg #1
arg #2
arg #3
arg #4
arg #5
arg #6
getarg_l %rax #1
\__ movr %rax %rdi
# :t2.tst:3
0x7f7e99eb6000 mov %rdi,%rax
getarg_l %r10 #2
\__ movr %r10 %rsi
0x7f7e99eb6003 mov %rsi,%r10
getarg_l %r12 #3
\__ movr %r12 %rdx
0x7f7e99eb6006 mov %rdx,%r12
putargr %rax #4
\__ movr %rcx %rax
0x7f7e99eb6009 mov %rax,%rcx
putargr %rax #5
\__ movr %r8 %rax
0x7f7e99eb600c mov %rax,%r8
putargr %rax #6
\__ movr %r9 %rax
0x7f7e99eb600f mov %rax,%r9
putargr %rax #3
\__ movr %rdx %rax
0x7f7e99eb6012 mov %rax,%rdx
subi %r10 %r10 0x7048860f9180
0x7f7e99eb6015 mov %rax,-0x10(%rbp)
0x7f7e99eb6019 movabs $0x7048860f9180,%rax
0x7f7e99eb6023 sub %rax,%r10
0x7f7e99eb6026 mov -0x10(%rbp),%rax
getarg_l %rax #1
\__ movr %rax %rdi
0x7f7e99eb602a mov %rdi,%rax
getarg_l %rax #2
\__ movr %rax %rsi
0x7f7e99eb602d mov %rsi,%rax
getarg_l %rax #3
\__ movr %rax %rdx
0x7f7e99eb6030 mov %rdx,%rax
getarg_l %rax #4
\__ movr %rax %rcx
0x7f7e99eb6033 mov %rcx,%rax
getarg_l %rax #5
\__ movr %rax %r8
0x7f7e99eb6036 mov %r8,%rax
getarg_l %rax #6
\__ movr %rax %r9
0x7f7e99eb6039 mov %r9,%rax
putargr %r10 #1
\__ movr %rdi %r10
0x7f7e99eb603c mov %r10,%rdi
putargr %r11 #2
\__ movr %rsi %r11
0x7f7e99eb603f mov %r11,%rsi
jmpr %r12
0x7f7e99eb6042 rex.WB jmpq *%r12
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"""
And now, it uses %rax as a temporary, saving/restoring it
during the operation.
I believe the problem was that your initial example never called "getarg $f".
That would be enough, either before or after the "subi", to make the code
consider it live.
You cannot expect it to follow a "jmpr %r(3)" when computing the liveness
state of a register, *but* it will follow a "jmpi proc" or equivalent
jump to label,
when computing a live register.
> Marc
Thanks,
Paulo