GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — gh-98831: Support conditional effects; use for LOAD_ATTR by gvanrossum · Pull Request #101333 · python/cpython Conversation
|
|
|
||
| // error: LOAD_ATTR has irregular stack effect | ||
| inst(LOAD_ATTR) { | ||
| inst(LOAD_ATTR, (unused/9, owner -- res, res2 if (oparg & 1))) { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Check out my comment in the generated code about 'res2'.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| PREDICTED(LOAD_ATTR); | ||
| PyObject *owner = PEEK(1); | ||
| PyObject *res; | ||
| PyObject *res2; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Should this initialize res2 = NULL? Right now I get compiler warnings (in GitHub) about a possibly uninitialized variable:
‘res2’ may be used uninitialized in this function [-Wmaybe-uninitialized]
presumably from the PUSH() on L1798.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think the issue is that the compiler can't see that if (oparg & 1) is the same as if (_cond_res2), so it can't tell that res2 is always initialised when it is used. Not sure what to do about it, add a syntax in the DSL to access the condition from within the bytecode definition?
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'll just initialize it to NULL. Perhaps some later phase of optimization will recognize that for the dead value it is.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think if we rename _cond_res2 to res2_exists and then in the body of the bytecode we do if(res2_exists) /* use res2 */ instead of if(oparg &1) /* use res2 */ then it would make the code clearer. It's also safer, because if you change the args to the instr then oparg&1 might not be correct anymore, but res2_exists will be automatically updated.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Okay, let's try that. I had a commit in the queue that initializes the variable to NULL, which I think would also get rid of the problem (if not I'd be very surprised) but since I didn't push that let's first try it with your idea.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Hm, looks like it doesn't help, the error is still there. So instead let me try this with the (much simpler) "= NULL" solution, which has to work. In fact, I may also drop the flag variable idea, and solve the warning about bool by writing X ? 1 : 0 instead of X != 0.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
| JUMPBY(INLINE_CACHE_ENTRIES_LOAD_ATTR); | ||
| POKE(1, res); | ||
| if (oparg & 1) { PUSH(res2); } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Here (I think).
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
- UndefinedLocalError when generating metadata for an 'op' - Accidental newline inserted in test_generator.py
| POKE(1, xx); | ||
| if (oparg & 2) { PUSH(output); } | ||
| PUSH(zz); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Could we stick with PEEK and POKE and here have something like this?
| POKE(1, xx); | |
| if (oparg & 2) { PUSH(output); } | |
| PUSH(zz); | |
| POKE((oparg & 2) + 2, xx); | |
| if (oparg & 2) { POKE((oparg & 2) + 1, output); } | |
| POKE(1, zz); |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Interesting. This would favor cases where the conditional is at the bottom of the inputs (e.g. a if (...), b, c). But I think it would become quite horrible if you have several conditions, like we have in MAKE_FUNCTION.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
If there are several conditions then the concatenation of the conditions (and the unconditional 1's) gives you the index.
The generator will be simpler, the generated code will have these expressions for indices, but I think that's probably easier to follow than a mixture of pushes and pokes.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I'll play around with that.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Have a look. The generator sure is a lot simpler, I think I can live with the more complicated generated code. What made this solution so nice is that we already had the symbolic size -- the condition just gets added into that. Thanks for the nudge!
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Notes to self.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| At most one of these will be non-zero / non-empty. | ||
| """ | ||
| if effect.size: | ||
| assert not effect.cond, "Manual effects should be conditional" |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
This message is incorrect.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| if "boerenkool" in dst.name or "boerenkool" in src.name: | ||
| breakpoint() |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Debug output accidentally left in.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| # elif m := re.match(r"^POP\(\)$", dst.name): | ||
| # if src.cond: | ||
| # self.emit(f"if ({src.cond}) {{ PUSH({cast}{src.name}); }}") | ||
| # else: | ||
| # self.emit(f"PUSH({cast}{src.name});") |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Delete commented-out block.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| else: | ||
| # Write output register assignments | ||
| for oeffect, reg in zip(self.output_effects, self.output_registers): | ||
| src = StackEffect(reg, "") |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Don't need this.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| STACK_GROW(((oparg & 1) != 0)); | ||
| POKE(1, res); | ||
| if (oparg & 1) { POKE(1 + ((oparg & 1) != 0), res2); } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
It's unfortunate that all new versions test for oparg & 1 several times, whereas the original version tested that condition exactly once.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I assumed the compiler would do this, but we could assign ((oparg& i) != 0) to something. I think it’s only used as a condition in one place, in other places it’s just int arithmetic.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I don't understand what this warning is about:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
I assume it's about this line (L1797): The compiler probably reasons that That's silly since it doesn't warn us about the equally trivial I'm not sure what to do about it. PS. I'm still looking into your suggestion to assign the condition to a flag variable, it's a bit messy. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Here's the new version. Let me know if you like it enough to go ahead -- if not, I can easily roll it back. Note that I also fixed a bunch of typing issues (which caught one genuine bug). |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I think this is easier to read. The warning about the bool comparison is gone too. There's just the one about res being potentially unassigned. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| // Dummy variables for stack effects. | ||
| static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub; | ||
| static PyObject *container, *start, *stop, *v, *lhs, *rhs; | ||
| static PyObject *container, *start, *stop, *v, *lhs, *rhs, *res2; |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
We will also need static int res2_exists, and a bunch of other things, before I land this.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
This hopefully avoids a silly compiler warning.
8f4219a to
81e5236
Compare
|
Finally this has no compiler warnings on GitHub. I prefer this version, the changes to generate_cases.py are simpler than the version with flags. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
There was an error while loading. Please reload this page.