Test an escaped parameter read before its slot exists - #339
Conversation
4bcb7fc to
b69a985
Compare
|
Rebased on Stage 0 for All three functions in the test come out right on master, so the |
b69a985 to
9ac11c1
Compare
| @@ -0,0 +1,41 @@ | |||
| /* | |||
There was a problem hiding this comment.
The file opens with an empty line where its sibling tests/strength-reduce.c opens with a comment naming the shape it guards. This test depends on a narrow shape (a parameter still sitting in its arrival register, its address taken only after the expression reads it), and nothing in the file says so. A later cleanup could drop the unused p or move &b above the arithmetic, and the test would still pass while covering nothing. Move the explanation from the pull request description into the file:
| /* | |
| /* A parameter whose address is taken only after an expression has | |
| * read it is still in the register it arrived in, and its stack slot may not | |
| * exist yet. Reloading it from that slot read whatever sat at offset | |
| * zero of the frame. Keep the `&` after the arithmetic and the | |
| * pointers in place: both are what make this path reachable. | |
| */ |
There was a problem hiding this comment.
Applied. The file has carried a header comment since the first push, but it explained why no store through the pointer is needed rather than warning against moving the & or dropping p, which is the part a later cleanup would break — your wording covers that, so I took it as is.
One deviation: .ci/check-commentflow.sh reflows the opening /* onto its own line, matching tests/strength-reduce.c. The text is yours; the layout is commentflow's output.
A parameter arrives in a register and is given a stack slot only when
something spills it. prepare_operand() reloads an address-taken variable
from its slot rather than reading the register it is already in, and for
a parameter whose address is taken after the expression that reads it,
that reload came before any slot existed:
int f(int a, int b)
{
int x = a - b;
int *p = &b;
return x;
}
emitted "load %x2, 0(sp)" and subtracted the frame's first word, so f
returned a. Marking an incoming argument register polluted on entry made
prepare_operand() keep the register, which fixed this before the shape
was covered; the test pins it down.
9ac11c1 to
c7c5392
Compare
|
Thank @alanhc for contributing! |
Rebased on
2d38107. The rebase conflicted insrc/reg-alloc.c, and theconflict turned out to be the point: master already fixes #337.
pin_registers()'s caller marks an incoming argument registerpolluted = 1on function entry, so the
|| REGS[i].pollutedarm ofprepare_operand()returns the register instead of reloading it, and the reload that read a slot
the parameter never had is no longer reached. The comment above that condition
already names this case.
Comparing what stage 0 emits for
sub_then_escape():The three functions in the test all come out right on master, so the
address_taken && !space_is_allocatedhunk this PR carried is dead code now.Dropped it.
What is left is the regression test:
tests/escaped-param.ccovers thesubtraction, the same shape around an addition, and the address taken of the
first operand rather than the second. The shape is narrow -- it needs the
escaped variable to be a parameter, and the address taken after the expression
rather than before -- which is why it went uncovered until it broke.
Close this instead if the test is not worth a file of its own.
Regression test for #337, which master already fixes.