10BC0 bpo-30353 Fix pass by value for structs on 64-bit Cygwin/MinGW by embray · Pull Request #1559 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

Conversation

@embray
Copy link
Contributor
@embray embray commented May 12, 2017

Fixes bpo-30353 by making additional stack allocations for structs and copying the struct data (as libffi would do otherwise).

@mention-bot
Copy link

@embray, thanks for your PR! By analyzing the history of the files in this pull request, we identified @theller, @Yhg1s and @asvetlov to be potential reviewers.

@brettcannon brettcannon added the type-bug An unexpected behavior, bug, or error label May 12, 2017
@embray
Copy link
Contributor Author
embray commented May 19, 2017

Per discussion on bpo-30353, I've updated this to apply the same workaround for builds on arm64, which has a similar bug in libffi (bpo-29804).

@codecov
Copy link
codecov bot commented May 19, 2017

Codecov Report

Merging #1559 into master will decrease coverage by 1.02%.
The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1559      +/-   ##
==========================================
- Coverage    83.7%   82.67%   -1.03%     
==========================================
  Files        1371     1432      +61     
  Lines      346545   353139    +6594     
==========================================
+ Hits       290065   291964    +1899     
- Misses      56480    61175    +4695
Impacted Files Coverage Δ
Lib/sre_compile.py 84.06% <0%> (-4.53%) ⬇️
Lib/heapq.py 95.03% <0%> (-2.3%) ⬇️
Lib/idlelib/idle_test/test_textview.py 2.94% <0%> (-1.61%) ⬇️
Lib/test/libregrtest/cmdline.py 93.18% <0%> (-1.4%) ⬇️
Lib/idlelib/textview.py 25% <0%> (-0.93%) ⬇️
Lib/threading.py 81.83% <0%> (-0.7%) ⬇️
Lib/concurrent/futures/_base.py 92.36% <0%> (-0.37%) ⬇️
Lib/socketserver.py 83.81% <0%> (-0.36%) ⬇️
Lib/nntplib.py 81.21% <0%> (-0.36%) ⬇️
Lib/bdb.py 26.84% <0%> (-0.31%) ⬇️
... and 90 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 82a6384...cec12bc. Read the comment docs.

#if (defined(__x86_64__) && (defined(__MINGW64__) || defined(__CYGWIN__))) || \
defined(__aarch64__)
void *tmp;
if (pa->ffi_type->type == FFI_TYPE_STRUCT) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this definitely the correct test? Similar code to fix bpo-29565 uses a test of the type's size being greater than 8 bytes. Have you tested with structs both under and over this size limit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't see that issue--interesting. I might be able to adapt the existing fix there to this issue as well.

As to whether or not it impacts just structs or any value > 8 bytes I'm not sure off the top of my head anymore. I'll have to double-check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vsajip Okay, I think you're right here--the issue is basically the same as the old libffi_msvc code that's backported into ctypes--my changes actually cause a segfault if a small (<=8 bytes) struct is passed by value, so I should add a test for that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hide comment

Oops, I should say, it doesn't cause a segfault (that was just a minor typo on my part). But regardless the fix is not needed for smaller structs which are, as expected, passed by value in registers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should correct myself again--actually it is needed for smaller structs if they are not register-aligned.

}
#if (defined(__x86_64__) && (defined(__MINGW64__) || defined(__CYGWIN__))) || \
defined(__aarch64__)
void *tmp;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason this declaration can't be inside the if block? It's only ever used in that block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean. It is inside the if block.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

By my reading, it's inside the #if block, but not inside the if (pa->ffi_type->type == FFI_TYPE_STRUCT) { block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You you meant the actual if statement. I don't mind moving it, but I don't see what difference it makes--it generates the exact same assembly either way.

Copy link
Member
@vsajip vsajip May 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a little tidier. The reference (tmp) stays in the scope in which it's used. In fact, you can just do void * tmp =alloca(pa->ffi_type->size); and don't need a separate line just for the declaration.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really agree--I find variable declarations in an if statement pretty ugly, but I can't disagree it at least limits it scope (even if it's ultimately irrelevant). Maybe I'll change it if/when there are other more substantive changes to make.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I think my last comment came off as disrespectful--to be clear I have no problem with changing this later (I just meant I won't bother making a separate commit for it).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No offence taken 😄

I find variable declarations in an if statement pretty ugly

As far as I know, it's considered good practice for declarations to be limited to the scope of the blocks where they're used, as long as the version of C in use permits it. There's nothing special about an if block here - one would do the same for for, while, do etc.

I won't bother making a separate commit for it

I wouldn't worry too much about that; commits are cheap and would normally be squashed on merge, anyway.

In fact, *any* struct (or other type such as long double) that does not
fit in a 1/2/4/8 byte register should be passed by reference.  Structs
that do fit in a register are passed by value and we need to make sure
this works properly too (it previously did not).
@embray
Copy link
Contributor Author
embray commented May 23, 2017

Okay, I reworked this a bit. I realized while figuring out that was going on that smaller structs that do fit in a register were actually not working properly (and I added a test for this). This is because libffi is currently pretty dumb about how it treats arguments. It only looks at the argument size, and if it's not 1, 2, 4, or 8 bytes it assumes the argument is a pointer and it puts that pointer on its stack. However, for structs of size 1/2/4/8 bytes the existing behavior (before my earlier fix) was correct because it libffi is passed the pointer to the struct value, and it treats this (e.g. for an 8 byte struct) as a uint64 pointer which it dereferences and copies the value.

@embray
Copy link
Contributor Author
embray commented May 24, 2017

Looks like the Windows build on appveyor died for seemingly unrelated reasons.

@vsajip vsajip merged commit 9ba3aa4 into python:master Jun 7, 2017
@embray embray deleted the issue-30353 branch June 8, 2017 07:41
@embray
Copy link
Contributor Author
embray commented Jun 8, 2017

Thanks!

@stratakis
Copy link
Contributor

@embray would you be able to create a PR for the 3.6 branch?

@vsajip
Copy link
Member
vsajip commented Jun 12, 2017

There's no need, the way to do it is to cherry-pick that commit to the 3.6 branch - I'll look at it soon.

@miss-islington
Copy link
Contributor

Thanks @embray for the PR, and @vsajip for merging it 🌮🎉.. I'm working now to backport this PR to: 3.6.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Mar 1, 2018
…onGH-1559)

(cherry picked from commit 9ba3aa4)

Co-authored-by: Erik Bray <erik.m.bray@gmail.com>
@bedevere-bot
Copy link

GH-5954 is a backport of this pull request to the 3.6 branch.

ned-deily pushed a commit that referenced this pull request Mar 8, 2018
…GH-1559) (GH-5954)

(cherry picked from commit 9ba3aa4)

Co-authored-by: Erik Bray <erik.m.bray@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type-bug An unexpected behavior, bug, or error

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants

0