-
-
Notifications
You must be signed in to change notification settings - Fork 613
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
flag non-portable bitfields #20840
base: master
Are you sure you want to change the base?
flag non-portable bitfields #20840
Conversation
Thanks for your pull request, @WalterBright! Bugzilla referencesYour PR doesn't reference any Bugzilla issue. If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog. Testing this PR locallyIf you don't have a local development environment setup, you can use Digger to test this PR: dub run digger -- build "master + dmd#20840" |
5fdb9ed
to
9251caa
Compare
@@ -7296,6 +7299,11 @@ private extern(C++) class SetFieldOffsetVisitor : Visitor | |||
fieldState.bitOffset = pastField; | |||
} | |||
|
|||
if (checkPortable && !isPortable) | |||
{ | |||
error(bfd.loc, "bitfield layout not portable among supported C compilers, wrap with `extern (C)` or `extern (C++)`"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably mention other ABI's like Windows i.e.
error(bfd.loc, "bitfield layout not portable among supported C compilers, wrap with `extern (C)` or `extern (C++)`"); | |
error(bfd.loc, "bitfield layout not portable among supported C compilers, wrap with extern (X) where X is C, C++, Windows, or System"); |
9251caa
to
df48ab6
Compare
df48ab6
to
869e702
Compare
* E.g., (not all) ARM ABIs do NOT ignore anonymous (incl. 0-length) | ||
* bit-fields. | ||
*/ | ||
const bool contributesToAggregateAlignment = isMicrosoftStyle || !bfd.isAnonymous; // sufficient for supported architectures |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is target specific though. Each compiler back-end makes its own decision about this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, in GCC, back-ends set a PCC_BITFIELD_TYPE_MATTERS
macro to notify us that type affects alignment. Then TARGET_ALIGN_ANON_BITFIELD
if anonymous bitfields affect alignment as well.
Taking this away would likely regress data layouts on those back-ends.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What matters is which ones are actually different.
I have run into a significant problem. The idea is to use So we're back to the ugly scheme I objected to (this scheme was sold as just putting the
We could rerun all the struct layouts during the semantic phase, but that would be more complexity and runtime cost for what is an issue that is not significant anyway. |
Could we call a function as part of the glue code to detect this? |
It's a good question. The problem is the absurd complexity of the layout code. You'd have to run another pass over the layout system, as I mentioned. We have a long list of things to do, and this feature which will only annoy people is a waste of time. |
I did spend today looking at ways to refactor it into something simple, but with only minimal improvement. |
The assumption we've been working with is that it's a simple matter of looking at a set of field type size + number of bits pair to detect the bad cases. Something like this: uint runningUsedBits;
uint maxInStorage;
foreach(field; *str.fields) {
BitFieldDeclaration bfd = field.isBitFieldDeclaration;
if (!bfd) {
maxInStorage = 0;
continue;
}
const width = bfd.fieldWidth;
const size = bfd.type.size;
if (maxInStorage == 0) {
runningUsedBits = 0;
maxInStorage = size;
}
runningUsedBits += width;
if (runningUsedBits > maxInStorage)
warn;
maxInStorage = 0;
} Am I right in thinking that you are thinking of something very different when you talk about running another pass? |
All I can say is take a look at the implementation of setFieldOffset(). (The layout of a bitfield depends on the layout of the previous fields.) |
I'm reading it, it's adjusting the properties like bit field offset and it's using intermediary data to do so. What information do you need that is specific to a bitfield, that isn't its bit offset + width + alignment/storage size? Nothing I am seeing suggests the need to rerun |
The field prior to it which affects it. |
Does this have all the properties you need accessible to detect non-portability? bool isStructBitFieldSafe(StructDeclaration sd) {
uint runningUsedBits;
uint maxInStorage;
uint prevNonBitFieldOffset;
uint prevNonBitFieldSize;
foreach(field; *sd.fields) {
VarDeclaration vd = field.isVarDeclaration;
if (!vd)
continue;
const offset = vd.offset;
const size = vd.type.size;
BitFieldDeclaration bfd = field.isBitFieldDeclaration;
if (!bfd) {
maxInStorage = 0;
prevNonBitFieldOffset = offset;
prevNonBitFieldSize = size;
continue;
}
const width = bfd.fieldWidth;
if (maxInStorage == 0) {
runningUsedBits = 0;
maxInStorage = target.fieldalign(...);
// check first bit field against prevNonBitFieldSize and prevNonBitFieldOffset
} else {
// check for bit field > 0, to see if it crosses storage boundary
if (runningUsedBits + width > maxInStorage) {
error;
return false;
}
}
runningUsedBits += width;
if (runningUsedBits == maxInStorage)
runningUsedBits = 0;
}
return true;
} |
It depends on the alignment of the previous fields:
gives differing results. Look at the code to setFieldOffset(). You'd have to duplicate its every nuance. You'd also have to walk all the declarations in the module, i.e. it's another pass. |
It's also important to not give an error message when the layout happens to be the same even though different rules are followed. |
For my own benefit, what is meant by non-portable layouts?
Is a non-portable layout between |
For a given ABI, all the different layout engines for bitfields will agree on bit padding. The simplest solution to that (which I implemented) is to simply ban any compiler-added padding. |
Different layout engines meaning dmd interfacing with gcc, clang, msvc? Compared with ldc/gdc who only interface with themselves? |
Interfacing? The frontend would have its own implementation of each engines strategy. And each C compiler could support multiple (i.e. clang certainly supports MSVC and GCC's). |
Discussed at the last DLF mtg.
The bitfield code is not amenable to flagging nonportable layouts, so I'm trying to refactor it to make it more tractable.