-
Notifications
You must be signed in to change notification settings - Fork 10.2k
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
[IRGen] Improvements to function type metadata #12500
Conversation
@rjmccall Do changes so far make sense to you? It seems like the next step would be to modify |
179768f
to
a39658b
Compare
include/swift/AST/Types.h
Outdated
return ParameterTypeFlags() | ||
.withVariadic(value & Variadic) | ||
.withAutoClosure(value & AutoClosure) | ||
.withEscaping(value & Escaping) |
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.
I don't feel like autoclosure or escaping belong in the parameter flags, but that concern is more-or-less independent of your improvements to function type metadata.
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.
@DougGregor I think I can mask them out from the metadata with ease, do you think the approach here is viable overall?
include/swift/Runtime/Metadata.h
Outdated
uint32_t getParameterFlags(unsigned index) const { | ||
auto numParams = getNumArguments(); | ||
assert(index < numParams); | ||
auto *flags = reinterpret_cast<const uint32_t *>(this + 1 + numParams); |
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 pointer computation seems very wrong; the + numParams
is scaled by sizeof(FunctionTypeMetadata).
Also, I'm not sure I understand why you're being incremental about changing the ABI here, at least as regards the layout of FunctionTypeMetadata. Is there a good reason not to design and implement the desired ABI at this point, since you're revising the ABI anyway? Why force someone else to come by and do all these same updates again?
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.
I will redo the pointer but I don’t understand what do you mean by incremental changes here, can you please elaborate? I am thinking of doing everything related to function metadata ABI, this is just a first changes wrt the layout I wanted input on...
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 seems to be implementing a flat, always-present array. The things we talked about:
- maybe not representing this as an array at all
- maybe only including elements in the array if they have flags
- or at least allowing the array to be optional
Every change you make here has to be quite involved in terms of e.g. updating all the runtime functions and the compiler's calls to them and how MetadataReader interprets the runtime structures, so I'm questioning whether it really makes sense to do all these changes in separate patches.
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.
Ah sure, I understand. I'm planning to do all this next as one step and update existing commit, just wanted to make sure that I haven't done something obviously terrible since I'm pretty new to LLVM...
include/swift/Runtime/Metadata.h
Outdated
swift_getFunctionTypeMetadata1(FunctionTypeFlags flags, | ||
const void *arg0, | ||
swift_getFunctionTypeMetadata1(FunctionTypeFlags flags, const void *arg0, | ||
const void *paramFlags, |
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.
If we're no longer mangling things into the low bits of the parameter types, we can just use 'const Metadata *' instead of 'void *', since we're not trying to encourage caution.
Why is paramFlags a void*?
getFunctionTypeMetadataN is a convenience function for common cases. Do we have any reason to think that parameter type flags are a common enough case to justify making these functions less convenient?
You also need to update all the getFooFunctionTypeMetadata functions below this, or at least their most-general cases.
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.
I wasn’t sure what to you for parameter flags here, should it be uint32_t *
instead? I think parameter flags for single parameter are pretty common, I’ve seen examples in compatibility suite.
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.
I don't doubt that they occur, just that they occur in enough frequency to warrant adding extra requirements to the common case. Can you get counts here?
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.
I've found 87 occurrences of functions with inout
and 1-3 arguments in source compatibility projects, most of them in GRDB
project. I'm pretty sure there is going to be much more with __shared
when that's available... I'm also curious - if not array of parameters how else should we track flags in this case, with individual arguments?
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.
Do you happen to have the full histogram for the source compatibility suite? Specifically I'd like to know the cross table of (convention) x (argument count) x (has any flags).
I agree that __shared/__owned will make flags more common. (We'll have to decide which is the default, and obviously it should track the language decision because the language decision will make the default much more common than the other.)
Our original decision to go up to 3 was not based on any particular insight that I remember. The purpose of these functions is to optimize code size for common cases, and setting up a call with 7 arguments is already a ton of code, so the benefit might be negligible. Maybe we'll want to provide specializations for (1,false), (2, false), (3, false) but just (1, true). Certainly it seems questionable whether we should do these common-path entrypoints for each of the secondary @convention
s; maybe we should just have common-path entrypoints that take the convention as an argument. Anyway, data is the key here.
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.
Oh, are you just grepping for typealiases of function type or something?
Yes, that's exactly what I was doing, I wish we had some tools to generate reports like that but I'm afraid we don't, or maybe I just don't know about them. My thinking was that if there aren't many functions with inout
(overall) in the project then and even fewer are going to require metadata generation that says something about distribution...
RE: swift_getFunctionTypeMetadataWithFlags1
- Sure! I got that idea as well, just wasn't sure if you are proposing something even more sophisticated... I think it makes sense because many 1/2/3 parameter functions are likely not to have any flags, judging by compatibility suite. Which brings us to question about having separate "function parameter metadata" which is going to include pointer to type + uint32_t for flags and not going to require any changes to swift_getFunctionTypeMetadata*
functions and MetadataCache
, WDYT?
RE: MetadataCache - Ah, I see, that make sense to me!
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.
I think having a separate metadata object for each parameter would be rather gratuitously wasteful, and I can't think of anything that would benefit from it.
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.
Not always but only if there are flags present, if that’s not a very common occurance it’s not going to waste too much space, right?
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.
I think there's some value in having the layout of the component types not vary that drastically based on whether flags are present. This is why I was suggesting having separate arrays.
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.
I totally agree! The reason why I bring up this parameter type ref metadata idea is because it makes it much easier to work with function metadata and doesn't require any specific optimizations, like in case of the array of flags, we'd like to keep only flags for parameters which have them, and don't use any additional place, such most likely would be more complicated comparing to extracting a single uint32_t
from metadata ref and this approach wouldn't require adding even more optimized calls for 1/2/3 parameter functions with and w/e flags...
a39658b
to
d0c7240
Compare
@rjmccall Can you please take another look? I've updated everything according to our discussion - added new |
include/swift/AST/Types.h
Outdated
ParameterTypeFlags withAutoClosure(bool isAutoClosure) const { | ||
return ParameterTypeFlags(isAutoClosure | ||
? value | ParameterTypeFlags::AutoClosure | ||
: value - ParameterTypeFlags::AutoClosure); |
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.
Is this an actual int subtraction? You can't do that safely unless you're asserting that the flag is already set in 'value'.
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 comment is still applicable.
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.
I overlooked that, it's not longer necessary for function metadata anyway, so I'll just remove all of the methods I've added to ParameterTypeFlags
.
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.
And I've looked it up - it's actually not a int substitution, but rather an OptionSet
operation, so it should be safe to do that.
Param.setType(ParamTypeRef); | ||
Param.setFlags(ParameterTypeFlags::fromRaw(ParameterFlags)); |
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.
It is really not okay to assume that the AST's ParameterTypeFlags type matches the runtime-metadata type. Please make these separate types with their own internal representation of all the various flags. I'm sure I asked you to do this before.
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.
Ok, will do.
include/swift/Runtime/Metadata.h
Outdated
const Metadata *resultMetadata); | ||
|
||
SWIFT_RUNTIME_EXPORT | ||
const FunctionTypeMetadata * | ||
swift_getFunctionTypeMetadata1WithFlags(FunctionTypeFlags flags, | ||
const Metadata *arg0, | ||
const uint32_t *paramFlags, |
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 just take a uint32_t, not a pointer.
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.
Ok, will do.
include/swift/Runtime/Metadata.h
Outdated
swift_getFunctionTypeMetadata2WithFlags(FunctionTypeFlags flags, | ||
const Metadata *arg0, | ||
const Metadata *arg1, | ||
const uint32_t *paramFlags, |
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 take two uint32_t's.
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.
If we are going to go this route I going to re-arrange it so it’s param1, flags1, param2, flags2 etc.
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.
Yes, that seems reasonable.
include/swift/Runtime/Metadata.h
Outdated
const Metadata *arg1, | ||
const Metadata *arg2, | ||
const uint32_t *paramFlags, | ||
const Metadata *resultMetadata); |
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.
Are you planning to collect that histogram of actual uses and then figure out what flags we really want in a follow-up?
Regardless, you should follow the same pattern here and take three uint32_t's. Only the most general entrypoint should take it indirectly.
stdlib/public/runtime/Metadata.cpp
Outdated
auto numArguments = getFlags().getNumArguments(); | ||
assert(index < numArguments); | ||
auto *flags = reinterpret_cast<const uint32_t *>( | ||
FlagsArgsAndResult[numArguments + 1]); |
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.
Wait, are you storing a pointer in the metadata? What is this pointer pointing to? Is the assumption that callers of getFunctionTypeMetadata will permanently allocate a flags array?
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.
Currently I have it created as constant global array and passed as a pointer, I think it makes sense to copy or re-arrange it somehow when stored in the cache, I just didn’t do that part yet...
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.
If I wanted to avoid array allocation and just encode flags inline after each parameter, how should I go avoid storing uint32_t as void*?
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.
I would suggest storing it as two separate arrays in the metadata object, one after the other.
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.
Sorry but I don’t think that I fully understand what you mean, I am talking about void ** we pass to swift_getFunctionTypeMetadata
, do you mean the same thing by metadata object? The reason I decided to go with this const array is because it’s easy to pass it in, but I would really rather do it as param1,flags1, etc. here the same way as 1/2/3 functions are going to be. I just don’t know how to encode uint32_t value as an element of void ** ...
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.
Well, you could extend it to intptr_t and then cast, which is easy enough to do in LLVM. But I wasn't thinking about the fact that swift_getFunctionTypeMetadata has such an opaque interface. Frankly, I'm not sure why it does, vs. taking the FunctionTypeFlags and a types (and now a flags) pointer.
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.
So here is how I am thinking of encoding arbitrary parameters since it’s easy to use uint32_t: represent each of the parameter’s metadata as void * with last bit set to 1 if it has flags, 0 otherwise, format is going to be paramN, flagsN?, ... if the last bit is not set consider next pointer in the array to be new parameter, otherwise it’s uint3_t representing flags for current parameter, WDYT?
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.
Do you mean in the interface to swift_getFunctionTypeMetadata or do you mean in the storage in a FunctionTypeMetadata? I tend to think that separate arrays, where the flags array is optional, is a better representation overall for both cases.
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.
In the metadata, we can store whether there are any parameter flags in the FunctionTypeFlags.
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.
I mean as swift_getFunctionTypeMetadata argument, but this might be beneficial for cache too because it avoids storing a bunch of zeros in the array.
include/swift/Runtime/Metadata.h
Outdated
@@ -1760,7 +1758,15 @@ struct TargetFunctionTypeMetadata : public TargetMetadata<Runtime> { | |||
TargetPointer<Runtime, const Argument> getArguments() const { | |||
return reinterpret_cast<TargetPointer<Runtime, const Argument>>(this + 1); |
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.
@rjmccall I'm working on the changes to separate parameters from flags when stored in the entry of the cache but I need some help understanding how to do that, especially what this + 1
really means here? I've read comment in SimpleGlobalCache
which talks about how getExtraAllocationSize
is used, so here is what I think I need to do to make it work, please correct me if I'm wrong:
- Modify
getExtraAllocationSize
so it tail-allocates enough space for array ofuint32_t
equal to number of parameters like this
static size_t getExtraAllocationSize(Key key) {
auto numParams = key.getFlags().getNumArguments();
return key.getFlags().getNumArguments() * (sizeof(FunctionTypeMetadata::Argument) + sizeof(uint32_t));
}
- Add a method to
FunctionCacheEntry
:
TargetPointer<Runtime, uint32_t *> getParameterFlags() {
return reinterpret_cast<TargetPointer<Runtime, uint32_t *>>(__address here__);
}
Should address here be this + (numParams * sizeof(TargetFunctionTypeMetadata::Argument)) + 1
?
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.
Remember that pointer arithmetic in C is scaled by the size of the pointee type. this + 1
is an idiom used when you allocate extra memory after an object ("extra" meaning "not just sizeof(T)
"), because it very conveniently creates a pointer to that extra memory without needing to write out the type of this
. You then generally need to cast that pointer to some other type in order to do useful pointer arithmetic within the tail-allocation.
So the address needs to be reinterpret_cast<char*>(this + 1) + (numParams * sizeof(TargetFunctionTypeMetadata::Argument))
.
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.
Or, alternatively, reinterpret_cast<Argument*>(this + 1) + numParams
.
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.
Thank you!
@@ -812,22 +813,17 @@ class MetadataReader { | |||
sizeof(TargetFunctionTypeMetadata<Runtime>); | |||
for (StoredPointer i = 0; i < Function->getNumArguments(); ++i, | |||
ArgumentAddress += sizeof(StoredPointer)) { | |||
StoredPointer FlaggedArgumentAddress; | |||
StoredPointer ParameterAddress; |
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.
I would call this ParamMetadata or something like that.
ee44e65
to
fb3d757
Compare
@rjmccall Does this look better? It's not yet complete but 98% done I think, I made it so parameters and flags are stored separately and flags are encoded independently from AST and converted when needed. |
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.
Yes, I'm much happier with the basic approach here, thank you. We can revisit the question of which specialized getFunctionTypeMetadata functions to provide in a follow-up patch. I've left some comments, and I'm sure I'll have more with the complete patch, but please proceed with this design.
return BuiltType(); | ||
|
||
FunctionParam<BuiltType> Param; | ||
if (auto ParamTypeRef = readTypeFromMetadata(ParameterAddress)) { | ||
//const auto ParameterFlags = Function->getParameterFlags()[i]; |
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.
Okay. Is this not ready for me to review?
include/swift/ABI/MetadataValues.h
Outdated
ConventionMask = 0x0F000000U, | ||
ConventionShift = 24U, | ||
ThrowsMask = 0x10000000U, | ||
ParamFlagsMask = 0x01000000U, |
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.
The indentation before was better because it made it a lot clearer that the bitmasks didn't interfere.
16 million formal arguments is a bit ridiculous; let's drop that to 16 bits, since 65535 arguments is still a rather absurd number to support. We should probably make the convention at least 8 bits, though. That leaves us with 8 bits, of which we've claiming two already. We can always use one of those bits to mean "there are more flags" and then pass those in the arguments array / store them somewhere else in the metadata. Does that seem reasonable?
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.
Sorry, this is what git clang-format
does, I've tried to fix indentation of enums where possible but failed in some places still :(
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.
Will do the changes to size of the flags after I'm done with the flags and other changes, if that's ok?
include/swift/ABI/MetadataValues.h
Outdated
InOutMask = 0x10000000U, | ||
SharedMask = 0x01000000U, | ||
VariadicMask = 0x00100000U, | ||
}; |
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.
I don't know why you're building these from the high bits. All else being the same, it's generally cheaper for the compiler to materialize small integers, not large ones.
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.
I've just looked at some other places, and I didn't know that it's more expensive, will fix, thanks!
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.
It's pretty minor, but yes, it is often the case that you can do something with a very small integer in one instruction that takes multiple instructions with a larger integer, so if it's otherwise an arbitrary choice, prefer to use smaller integers for more important flags.
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.
Understood, thanks!
include/swift/Runtime/Metadata.h
Outdated
|
||
|
||
void setParameterFlags(unsigned index, ParameterFlags flags) { | ||
assert(index <= getNumArguments()); |
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.
Just <
, not <=
.
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.
Ah wow :(
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.
I should stop doing coding at night :)
include/swift/Runtime/Metadata.h
Outdated
} | ||
|
||
ParameterFlags getParameterFlags(unsigned index) const { | ||
assert(index <= getNumArguments()); |
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.
Same thing here.
|
||
for (StoredPointer i = 0; i < numParameters; ++i, | ||
ArgumentAddress += sizeof(StoredPointer), | ||
ParameterFlagsAddress += sizeof(StoredPointer)) { |
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.
I believe you need to be adding sizeof(uint32_t) here.
But in general, none of this should be necessary, since it's not reading memory outside of the true bounds of the FunctionTypeMetadata. You just need to update the Function case in readMetadata so that it computes the true bounds correctly, just like how it's done for Tuples. If you do that, you should be able to just use the normal access functions on TargetFunctionTypeMetadata (as long as they don't assert anything you haven't already checked!).
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.
I think I understand now what you are talking about - it's done for parameters this way because we need to read a point to metadata itself from the argument position, but for flags we don't have to do that because they are values themselves. Thanks again!
include/swift/Runtime/Metadata.h
Outdated
const Metadata *resultMetadata); | ||
swift_getFunctionTypeMetadata2WithFlags(FunctionTypeFlags flags, | ||
const Metadata *arg0, | ||
const uint32_t flags0, |
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.
Don't const-qualify a parameter like this. Also, this should be ParameterTypeFlags, right?
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.
Got it!
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.
I think it's fine to make it ParameterFlags
for 1/2/3 functions but should it be const ParameterFlags *
for the general case too?
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.
The way I see is it that the use of a target-specific type is specifically just for passing these values as direct arguments to the runtime functions. Arguably that implies that we should make the enums ({Function,Parameter}TypeFlags
) non-templated and just make the runtime functions take a size_t with a comment about what enum they really are. The place where we take an array of them by reference should definitely use a pointer to a 32-bit type.
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.
Alright, this means that my changes are correct :)
include/swift/ABI/MetadataValues.h
Outdated
@@ -606,6 +615,55 @@ class TargetFunctionTypeFlags { | |||
}; | |||
using FunctionTypeFlags = TargetFunctionTypeFlags<size_t>; | |||
|
|||
template <typename int_type> class TargetParameterTypeFlags { |
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.
We seem to be assuming that the backing storage for this is always a uint32_t. I think it's still maybe a good idea to have it be templated, because it solves a lot of annoying calling-convention portability concerns to always pass a parameter as a full word instead of a smaller integer, but I think it merits a comment about why we're doing it.
2f3a021
to
be79e95
Compare
be79e95
to
f5b0699
Compare
include/swift/Runtime/Metadata.h
Outdated
|
||
TargetFunctionTypeFlags<StoredSize> Flags; | ||
TargetPointer<Runtime, const uint32_t> ParamFlags; |
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.
@rjmccall Just wanted to double check if that's what you were talking about when referring to how tuple metadata is done?
Looks like after I've added new
@rjmccall Any ideas how should I approach this? |
auto flags = FunctionTypeFlags() | ||
.withConvention(Function->getConvention()) | ||
.withThrows(Function->throws()) | ||
.withParameterFlags(Function->hasParameterFlags()); | ||
auto BuiltFunction = | ||
Builder.createFunctionType(Parameters, Result, flags); |
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.
I think you'll need to change the createFunctionType API to also take parameter flags, which probably means remapping them to the AST flags.
Also, what I mean about TupleMetadata is that you should modify the MetadataKind::Function
case in readMetadata
to start by reading the FunctionTypeMetadata and then use that to figure out the complete size to read, like the case for MetadataKind::Tuple
does.
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.
Ah ok, I think I understand know, I thought you meant treat flags like labels are treated in case of tuple...
Regarding createFunctionType
API - each of the elements in Parameters
already has flags attached to it, so I think flags are covered that way?...
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.
Oh, yes, I see, that makes sense.
I would guess that your bug is that you're scribbling out of bounds of the metadata object somehow. |
21d51b6
to
9e67482
Compare
89bb61c
to
191f1e7
Compare
@rjmccall I think I've made all of the changes required to make it work, please take a look one more time, thanks! |
Currently only single 'inout' flag has been encoded into function metadata, these changes extend function metadata to support up to 32 flags per parameter.
…gs arguments Switch most general endpoint to be `flags, parameters, parameterFlags, result`, instead of opaque `void **`, more specialized ones to use follow argument scheme: `flags, param0, [flags0], ..., paramN, [flagsN], result` and store parameter/flags information separately in `FunctionCacheEntry::{Key, Data}` as well.
119a3fa
to
0685106
Compare
@swift-ci please test |
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.
Okay, in addition to the change I request above, it looks like you're still on the hook for two things:
- Changing the layout and size of the different flags as discussed.
- Coming up with good data for which convenience accessors we should actually provide.
But it's fine to handle those in follow-ups.
include/swift/AST/Types.h
Outdated
ParameterTypeFlags withAutoClosure(bool isAutoClosure) const { | ||
return ParameterTypeFlags(isAutoClosure | ||
? value | ParameterTypeFlags::AutoClosure | ||
: value - ParameterTypeFlags::AutoClosure); |
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 comment is still applicable.
@rjmccall Yes, I was thinking to do that after this changes are merged as a follow up PR at least for function flags. |
Address argPtr = IGF.Builder.CreateStructGEP(buffer, i + 1, | ||
|
||
ConstantInitBuilder paramFlags(IGF.IGM); | ||
auto flagsArr = paramFlags.beginArray(); |
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.
@rjmccall One last question I have is if we should keep flags as constant array of stack allocated one? After digging into all this more it seems like we might better off with stack allocated array flags the same way as for parameters because we don't keep them around that long anyway, WDYT?
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.
It is almost certainly less overall binary size to use a const global array than to create one fresh on the stack.
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.
Ok, sounds good, I'll leave it as is then! Thanks again!
@swift-ci please test |
@swift-ci please test source compatibility |
Congrats! |
Thanks, John! I will make couple of follow-up PRs as we discussed. |
@rjmccall looks like it was too early to celebrate since this changes broke LLDB :( Do you have any idea where should I start in that project to fix it? |
You almost certainly don't need to modify anything in LLDB; instead, there's probably something broken in the RemoteAST code, i.e. MetadataReader. I would look at the test case, try to figure out what function type metadata is being parsed, and then try to duplicate that failure in the remoteast test harness. |
@rjmccall Could be it as simple as lldb requiring a clean build instead of incremental to resolve this? |
I've tried using |
Currently only single 'inout' flag has been encoded into function
metadata, these changes extend function metadata to support up to
32 flags per parameter.