Either deprecate or strengthen upc_addrfield()
Originally reported on Google Code with ID 107
Steve's comment, split off from issue 106:
Consider the following code:
shared int *S1;
shared [] int *S2;
S2 = S1;
if ( upc_addrfield(S1) == upc_addrfield(S2) ) {
printf("Match\n");
}
With the proposed change (as well as UPC 1.2 apparently), it is undefined whether or
not anything is printed. We don't require anywhere that pointers-to-shared with different
types that point to the same object produce the same result when passed to upc_addrfield(),
merely that such pointers shall compare equal. Since I don't think this is intended,
we probably need a stronger statement somewhere to make upc_addrfield() useful.
Reported by danbonachea
on 2013-02-27 11:06:44
Comments (97)
-
Account Deleted -
Account Deleted The code fragment in comment #0 calls upc_addrfield on an uninitialized pointer value. Lets assume they are statically allocated and not automatic variables, as the latter would be a case of garbage-in-garbage-out. S1 and S2 are thus initialized to null pointers which are then passed to the function. (Incidentally the assignment is also missing a required cast). Nothing in the specification defines the behavior of upc_addrfield(NULL). We could consider strengthening this, but I don't really see a problem with leaving it implementation-defined, since it's a rather silly thing for the user to do in the first place. The example I think Steve MEANT to provide was one where S1 and S2 point at an actual object, as below. I've also modified one of the pointers types to include phase information to make it more interesting. Example A: ---------- #include <upc.h> #include <stdio.h> shared int x; int main() { shared int *S1 = &x; shared [10] int *S2 = (shared [10] int *)S1; if ( upc_addrfield(S1) == upc_addrfield(S2) ) printf("Match\n"); if ( upc_addrfield(S1) == upc_addrfield(S1) ) printf("Match\n"); return 0; } So the question at hand is whether upc_addrfield is guaranteed to return the same result when passed a pointer to the same location. For the moment lets ignore the fact the user could/should just use a direct pointer equality operator to test PTS equality. I don't think the type of the actual argument matters at all in this discussion, because standard call conversion will automatically convert any argument to (shared void *) before the library function ever sees it. The same behavioral question arises when you pass the exact same argument twice, as in my second test above. I believe that for "free-standing" objects that are not part of an array (as above), the return value is currently completely implementation-defined (which by the way differs from "undefined", in that implementations are required to document their behavior). This means a perverse implementation could potentially return a different value on each call with the same pointer to a free-standing object. We could consider adding stronger requirements here, but I don't really see a motivation to do so. There are better ways to test pointer equality, and the addrfield of the pointer to such an object is essentially a useless piece of information. Now lets consider upc_addrfield() called on pointers to shared array elements. Assume the shared array contains at least two elements with local affinity, otherwise it's essentially equivalent to the "free-standing object" case above. Example B: ---------- #include <upc.h> #include <stdio.h> shared int A[2*THREADS]; int main() { shared int *S1 = &A[MYTHREAD]; // an element with affinity to MYTHREAD shared int *S2 = &A[MYTHREAD+THREADS]; // any other element with affinity to MYTHREAD int *P1 = (int *)S1; int *P2 = (int *)S2; ptrdiff_t S1addr = (ptrdiff_t) upc_addrfield(S1); ptrdiff_t S2addr = (ptrdiff_t) upc_addrfield(S2); if ((S2addr - S1addr) == (P2 - P1)*sizeof(int)) printf("Correct.\n"); // directly required by 6.4.2 if (S2addr == S1addr + (P2 - P1)*sizeof(int)) printf("Correct.\n"); // same equation, mathematically transformed if ((ptrdiff_t) upc_addrfield(S2) == S1addr + (P2 - P1)*sizeof(int)) printf("Correct.\n"); // duplicate call to upc_addrfield return 0; } This example is closely modeled on 6.4.2 to clarify my argument. The first test is exactly the requirement imposed by 6.4.2, and must pass on all compliant implementations. The second test is mathematically identical, I've just added S1addr to both sides of the equation and applied standard algebraic simplification. Thus it also must succeed in all compliant implementations. The third test introduces a second "duplicate" call to upc_addrfield(S2), to test the case Steve is worried about that might return a different answer. However by the equation in the second test, the required return value is already completely constrained by the requirements of 6.4.2. Returning a different value on the second call to upc_addrfield(S2) would thus be non-compliant.
Reported by
danbonachea
on 2013-02-27 12:15:14 -
Account Deleted > The example I think Steve MEANT to provide was one where S1 and S2 point at an actual object, as below. I've also modified one of the pointers types to include phase information to make it more interesting. My code fragment was meant to mimic the code fragment in 6.4.2 5. > I don't think the type of the actual argument matters at all in this discussion, because standard call conversion will automatically convert any argument to (shared void *) before the library function ever sees it. The same behavioral question arises when you pass the exact same argument twice, as in my second test above. Not necessarily. Nothing prohibits encoding the type in the representation of the shared pointer itself (perhaps to have a scaled offset internally and/or much better run-time warnings/errors) and preserving the type info when casting to a generic pointer-to-shared. > This means a perverse implementation could potentially return a different value on each call with the same pointer to a free-standing object. We could consider adding stronger requirements here, but I don't really see a motivation to do so. There are better ways to test pointer equality, and the addrfield of the pointer to such an object is essentially a useless piece of information. The strongest motivation I see is that upc_addrfield()+upc_threadof() is the only way to print the address of a shared object. Requiring that all pointers to the same object get the same result from upc_addrfield() means that users can easily and usefully print the upc_addrfield()+upc_threadof() combination when debugging. > This example is closely modeled on 6.4.2 to clarify my argument. And therein lies the problem. We've ONLY define upc_addrfield() for pointers whose referenced type has block size 1 (block size 0 with the proposed fix for issue 106). Consider the following slightly different code: #include <upc.h> #include <stdio.h> shared [2] int A[2*THREADS]; int main() { shared int *S1 = &A[2*MYTHREAD]; // an element with affinity to MYTHREAD shared int *S2 = &A[2*MYTHREAD+1]; // any other element with affinity to MYTHREAD shared [2] int *SB1 = &A[2*MYTHREAD]; shared [2] int *SB2 = &A[2*MYTHREAD+1]; int *P1 = (int *)S1; int *P2 = (int *)S2; int *PB2 = (int *)S1_1; int *PB2 = (int *)S2_1; ptrdiff_t S1addr = (ptrdiff_t) upc_addrfield(S1); ptrdiff_t S2addr = (ptrdiff_t) upc_addrfield(S2); ptrdiff_t SB1addr = (ptrdiff_t) upc_addrfield(SB1); ptrdiff_t SB2addr = (ptrdiff_t) upc_addrfield(SB2); if ((S2addr - S1addr) == (P2 - P1)*sizeof(int)) printf("Correct.\n"); // directly required by 6.4.2 if (S2addr == S1addr + (P2 - P1)*sizeof(int)) printf("Correct.\n"); // same equation, mathematically transformed if ((ptrdiff_t) upc_addrfield(S2) == S1addr + (P2 - P1)*sizeof(int)) printf("Correct.\n"); // duplicate call to upc_addrfield if ((S2addr - S1addr) == (P2 - P1)*sizeof(int)) printf("Correct?\n"); // directly required by 6.4.2? NO if (S2addr == S1addr + (P2 - P1)*sizeof(int)) printf("Correct?\n"); // same equation, mathematically transformed if ((ptrdiff_t) upc_addrfield(S2) == S1addr + (P2 - P1)*sizeof(int)) printf("Correct?\n"); // duplicate call to upc_addrfield return 0; } Because we use an example with a fixed block size in 6.4.2 5-6, as opposed to something similar to 6.4.2 3-4, the results of upc_addrfield() are only constrained for pointers whose referenced type has that single block size. In the code above, clearly the S1addr and S2addr are constrained by 6.4.2 4. But what about SB1addr and SB2addr? The spec does not currently place ANY constraints on their values.
Reported by
sdvormwa@cray.com
on 2013-02-27 14:37:29 -
Account Deleted >The strongest motivation I see is that upc_addrfield()+upc_threadof() is the only way > to print the address of a shared object. ..print the upc_addrfield()+upc_threadof() >combination when debugging. Yes, this is the debugging motivation I was referring to in comment #1. However upc_addrfield is rather poorly designed to suit that purpose, so if that's the only usage case we can come up with to motivate the existence of this library function, I'd vote to deprecate it and replace it with a function that is better designed for debugging pointer values and formatting them for human consumption, akin to C99's printf("%p"). For a concrete example, see http://upc.lbl.gov/docs/user/index.shtml#bupc_dump_shared > Because we use an example with a fixed block size in 6.4.2 5-6, as opposed to something similar to 6.4.2 3-4, the results of > upc_addrfield() are only constrained for pointers whose referenced type has that single block size. upc_addrfield() is a library function, not a built-in operator. Its actual argument is converted by C99 function calling rules to a (shared void *) before it ever reaches the function. Despite being implicit, this is an ACTUAL type conversion - (shared void *) is an incomplete type, but it is an actual static type, with NO associated blocking factor. We have some magic clauses in UPC to ensure the phase field is preserved when you cast back and forth to (shared void *), but otherwise the blocksize really and truly is gone from the type. A more explicit (but unnecessarily redundant) way to write the call in 6.4.2 would be upc_addrfield((shared void *)S1), but whether you write it explicitly or not, that's what the call MEANS and the language requires that conversion to happen prior to function entry. The function receives an argument value whose referenced type is incomplete and has NO blocksize. Saying the behavior of the function could change based on the static referent type of the pointer operand before argument conversion is like saying the behavior of C99 free() might change based on whether you pass it a (char *) or an (int *). In both cases this information was lost upon implicit conversion to (void *) and is simply is not available to the callee as far as the language spec is concerned. Therefore, specifying a behavioral property using a particular PTS blocking factor in 6.4.2 automatically constrains the behavior for any PTS blocking factor. The fact that some hypothetical implementation might include static type information in the pointer representation does not change the fact that SEMANTICALLY that information was lost upon type conversion.
Reported by
danbonachea
on 2013-02-27 15:54:51 -
Account Deleted > Yes, this is the debugging motivation I was referring to in comment #1. However upc_addrfield is rather poorly designed to suit that purpose, so if that's the only usage case we can come up with to motivate the existence of this library function, I'd vote to deprecate it and replace it with a function that is better designed for debugging pointer values and formatting them for human consumption, akin to C99's printf("%p"). I agree that upc_addrfield() is poorly designed. As far as I can tell, it can't be usefully used for anything but debugging. > The fact that some hypothetical implementation might include static type information in the pointer representation does not change the fact that SEMANTICALLY that information was lost upon type conversion. True, the type information was lost on conversion. But that doesn't mean that two pointers-to-shared with different referenced types that point to the same object produce the same value when converted to a generic pointer-to-shared. The only constraint is that the resulting values shall compare equal. As you noted, the phase may differ. I argue that nothing prohibits the results of upc_addrfield() from differing as well. As currently defined, upc_addrfield() is useless to UPC programers. It's return value is implementation defined and practically unconstrained. It's not guaranteed to produce the same result on all threads when passed the same pointer. The single constraint on it is defined only for a single block size. So why is it in the spec?
Reported by
sdvormwa@cray.com
on 2013-02-27 16:18:29 -
Account Deleted > So why is it in the spec? It predates my involvement, but I suspect it was introduced for symmetry with upc_threadof() and upc_phaseof() to "expose" the third logical component of a PTS. The original UPC implementation on the T3e used a PTS representation where the addrfield was equal to an actual virtual address, so upc_addrfield was interchangeable with cast-to-local. Subsequent revisions of the language made the pointer representation more opaque to allow for alternate implementation strategies, and the function was never removed. Perhaps Bill or Kathy can provide further insight.. > As currently defined, upc_addrfield() is useless to UPC programers. It's return value is > implementation defined and practically unconstrained. I think we agree it's useless for production codes and a poorly-designed tool for debugging. So instead of quibbling about the degree to which the result is or should be implementation-defined, I propose we talk about deprecating it entirely and possibly replacing it with a better debugging aid. As a starting point for discussion, here is the pseudo-spec for the BUPC vendor-specific replacement: (http://upc.lbl.gov/docs/user/index.shtml#bupc_dump_shared) ---------------------------------------------- The 'bupc_dump_shared' function Shared pointers in UPC are logically composed of three fields: the address of the data that the shared pointer currently points to, the UPC thread on which that address is valid, and the 'phase' of the shared pointer (see the official UPC language specification for an explanation of shared pointer phase). Our version of UPC provides a 'bupc_dump_shared' function that will write a description of these fields into a character buffer that the user provides: int bupc_dump_shared(shared const void *ptr, char *buf, int maxlen); Any pointer to a shared type may be passed to this function. The 'maxlen' parameter gives the length of the buffer pointed to by 'buf', and this length must be at least BUPC_DUMP_MIN_LENGTH, or else -1 is returned, and errno set to EINVAL. On success, the function returns 0, The buffer will contain either "<NULL>" if the pointer to shared == NULL, or a string of the form "<address=0x1234 (addrfield=0x1234), thread=4, phase=1>" The 'address' field provides the virtual address for the pointer, while the 'addrfield' contains the actual contents of the shared pointer's address bits. On some configurations these values may be the same (if the full address of the pointer can be fit into the address bits), while on others they may be quite different (if the address bits store an offset from a base initial address that may differ from thread to thread). Both bupc_dump_shared() and BUPC_DUMP_MIN_LENGTH are visible when any of the standard UPC headers (upc.h, upc_relaxed.h, or upc_strict.h) are #included.
Reported by
danbonachea
on 2013-02-27 16:47:21 -
Account Deleted > I think we agree it's useless for production codes and a poorly-designed tool for debugging. So instead of quibbling about the degree to which the result is or should be implementation-defined, I propose we talk about deprecating it entirely and possibly replacing it with a better debugging aid. Sounds reasonable. As part of the deprecation, I'd recommend removing it from 6.4.2 6. I suspect it was originally put there to try to imply the contiguity property that is the subject of issue 106. With the new language in that comment, I don't know that there's a need for it any more, given the lack of any other constraints on upc_addrfield().
Reported by
sdvormwa@cray.com
on 2013-02-27 16:54:16 -
Account Deleted > So why is it in the spec? upc_addrfield is useful primarily for debugging and writing test cases for pointer arithmetic. The test case that Steve uploaded for the pointer to array of shared issue would have been very difficult to write without upc_addrfield, but as written the test may not be portable given the underspecification of upc_addrfield. I'll leave it to UPC historians to explain exactly why upc_addrfield exists, but I suspect when it was introduced no one wanted the alternatives -- to add something to the UPC spec that required implementers to modify their libc printf to support a %P (or whatever letter) for a pointer-to-shared or to provide a upc_printf wrapper around printf. I also suspect it felt odd to have upc_threadof and upc_phaseof but not have any mechanism to access the value of the remaining field; however the address is implementation-specific information whereas the thread or phase are not. We (too) frequently have users attempt this code: shared int* ptr; ... printf( "%p", ptr ); which does not do what they expect (on our current systems, but used to work on older Crays). The conforming alternative, given that upc_threadof and upc_phaseof return a size_t for which there is no format specifier, is the verbose: printf( "%lu %lu %p", (unsigned long)upc_threadof( ptr ), (unsigned long)upc_phaseof( ptr ), upc_addrfield( ptr ) ); I'm sure they'd much rather be able to do this: upc_printf( "%P", ptr ); and I think that would cover all user code that I've seen for upc_addrfield, but the output format would need to be implementation-defined and the printed address value would be just as unspecified as the current upc_addrfield return value. I'm not sure it would be an improvement for implementers given that there would be a whole family of printf functions to wrap, but it may be an improvement for users.
Reported by
johnson.troy.a
on 2013-02-27 17:00:16 -
Account Deleted Responding to this item from comment #8: > given that upc_threadof and upc_phaseof return a size_t for which there is no format specifier, Sure there is: the "z" length modifier (e.g. "%zd", "%zu", "%zx", etc) which was added in C99. And Yes, I agree that extending the printf formats to have one for PTS would be nice, but it may be hard for some vendors. Berkeley, for instance, is at the mercy of somebody else's libc. Only when using glibc do we have register_printf_function().
Reported by
phhargrove@lbl.gov
on 2013-02-27 18:54:03 -
Account Deleted upc_addrfield was devised when the expectation was that a UPC implementation of a pointer-to-shared actually would be a structure with three fields for thread, phase, and addr (and possibly other fields). Thus, while addr was assumed not to be an actual address, it was assumed to act like an address and, more important, be used in a manner similar to an address in the actual implementation. The behavior of upc_addrfield was not so much a description of the values returned by a function, but rather a set of directives for how to implement pointers-to-shared and how to do pointer arithmetic, behavior that was verified by extracting the values out of the structure using the decomposition functions. When the conception of a pointer-to-shared was broadened and the definitions of the pointer decomposition functions loosened to allow them to be calculated rather than extractions of actual portions of the representation of the pointer value, upc_addrfield lost its usefulness.
Reported by
brian.wibecan
on 2013-02-27 19:03:54 -
Account Deleted While I never tried it, I have always imagined that if I was going to hash a PTS value, that I would use upc_addrfield(). If we remove this function, what is the recommended way to generate hashes from a PTS? We can't cast to int (or long, uintptr_t, etc) because we agreed in issue #11 that this has implementation-defined behavior (might always be 0xdeadbeef), and casts to local pointers are only defined when performed by the thread with affinity. So, unless somebody can point out an alternative way to get "bits" suitable for generating hash values from a PTS, I vote to KEEP upc_addrfield (adding any necessary constraints such as a value that is independent of calling thread).
Reported by
phhargrove@lbl.gov
on 2013-02-27 19:04:34 -
Account Deleted Ah, I missed the 'z' option in the man page. Thanks for pointing that out. Hashing without upc_addrfield is not a problem. Many hash functions operate by churning through each byte of data in turn, so one could apply that idea to a PTS via: shared int* p; long my_hash( char* bytes, size_t nbytes ); long hash_value = my_hash( (char*)&p, sizeof( p ) );
Reported by
johnson.troy.a
on 2013-02-27 19:07:56 -
Account Deleted "Hashing without upc_addrfield is not a problem. Many hash functions operate by churning through each byte of data in turn, so one could apply that idea to a PTS" That assumes that two different threads or two different contexts have the same data representation of a pointer value. I don't believe that's required. I think Paul is suggesting a hash mechanism that uses information guaranteed to be the same for the same pointer value in all contexts.
Reported by
brian.wibecan
on 2013-02-27 19:26:15 -
Account Deleted Troy wrote in comment #14: > Hashing without upc_addrfield is not a problem. Many hash functions operate by > churning through each byte of data in turn[....] We all agreed (issue #73) that phase is ignored in pointer comparison. So, hashing on the bytes that comprise the PTS representation might not yield the same hash for two pointers to the same object (which is one of the properties I would probably require). ADDITIONALLY, if the PTS representation has any "padding" that could vary between two pointers to the same object this byte-wise hashing would still fail the uniqueness constraint. I believe that possibility eliminates the possibility of using upc_resetphase() to get around the objection I have in the previous paragraph. The end-user would have no mechanism to "mask" the padding since they (should) know nothing of the internal representation.
Reported by
phhargrove@lbl.gov
on 2013-02-27 19:27:35 -
Account Deleted While it is on my mind, here my thoughts on upc_addrfield() if it is to be useful for something like generating hashes. I believe it also provides the properties desirable for debugging (no "false positives" from properly applied tests for equality) though that property is debatable (noted below)/ Properties I *do* expect: + Caller-independent For a given PTS p0, the value of upc_addrfield(p0) should not depend on the calling thread + Object-specific Given two PTS p1 and p2 that compare equal, their upc_addrfield() must also be equal. Using "->" as "implies": (p1 == p2) -> (upc_addrfield(p1) == upc_addrfield(p2)) + Byte-oriented within an object Given two PTS, p3 and p4, to portions of a SINGLE object with affinity to the SAME thread, the following is true on the thread with affinity: (upc_addrfield(p3) - upc_addrfield(p4)) == ((uint8_t*)(p3) - (uint8_t*)(p4)) [except that size_t vs. ptrdiff_t might need a cast?] + Per-thread uniqueness (debatable: constrains per-thread heap to SIZE_MAX) Given PTS p5 and p6 with affinity to the SAME thread, the upc_addfield() values are equal ONLY IF p5 and p6 compare equal: (upc_addrfield(p5) == upc_addrfield(p6)) && (upc_threadof(p5) == upc_threadof(p6)) -> (p5 == p6) These are "natural" for all reasonable PTS representations, including those that may encode "segment:offset" as bitfields in the addrfield (same object must have same segment, but distinct objects could have distinct segments) in which there is no single "flat" or "linear" space for the shared heap of a given thread. Properties I do NOT expect: + Global uniqueness Equality of upc_addrfield() does NOT imply anything when their threads differ + Constraint on PTS to distinct objects Other than per-thread uniqueness, there is no constraint on the upc_addrfield() values for two PTS with the same (or different) affinity. This is just the same as C pointer subtraction using pointers to different objects - undefined. This is a requirement to permit the "segment:offset" sort of encoding. A note on the "debatable" point: The "per-thread uniqueness" property is a desirable property for debugging, but I don't think it is essential to other applications such as hashing. This property would not normally be a problem, except that upc_addrfield() returns size_t. In the C99 spec it is clear that size_t is intended to be large enough to encode the size of a single OBJECT, but nowhere is it stated that the size of the entire "heap" must fit in size_t. IN FACT, NEC (or is that Cray?) SX-6 had 64-bit pointers but 32-bit size_t. So, for the RARE platform that had a "narrow" size_t the per-thread uniqueness of upc_addrfield() would constrain the per-thread shared heap size to SIZE_MAX. I really doubt that is a problem for anyone (and vaguely recall some other issue in the tracker dismissing such a concern), but wanted to note this for completeness.
Reported by
phhargrove@lbl.gov
on 2013-02-27 22:21:15 -
Account Deleted I am changing the description to reflect the current lack of consensus. Until/unless somebody can offer a portable hash function on PTS (without use of addrfield) that has at least the first 2 properties in my previous comment (caller-independent and object-specific), I am going to remain in "strengthen" camp.
Reported by
phhargrove@lbl.gov
on 2013-02-27 23:04:12 -
Account Deleted For the hash function, could you cast the PTS to a shared [] char* and then subtract a NULL pointer from it to get an address value? I have not tried this yet but just thought of it.
Reported by
johnson.troy.a
on 2013-02-28 02:32:17 -
Account Deleted Troy suggests: > could you cast the PTS to a shared [] char* and then subtract > a NULL pointer from it to get an address value? My initial response is that this suggestion runs afoul of the undefined nature of PTS subtraction when the pointers are not part of the same object. However, it is the best alternative I've heard so far.
Reported by
phhargrove@lbl.gov
on 2013-02-28 02:35:27 -
Account Deleted > My initial response is that this suggestion runs afoul of the undefined > nature of PTS subtraction when the pointers are not part of the same object. Right, C pointer subtraction rules would make it undefined, so the UPC spec would need to define it if we wanted to make that strategy portable. I tried it with Cray UPC and these two printfs produce the same output: #include <stdio.h> #include "upc.h" shared int x; int main( int argc, char* argv[] ) { shared int* p = &x; printf( "%zu\n", upc_addrfield( p ) ); printf( "%tu\n", (shared [] char*)p - (shared [] char*)NULL ); return 0; } If someone has an implementation for which these printfs would produce different output, I'd be interested in hearing why.
Reported by
johnson.troy.a
on 2013-02-28 06:36:19 -
Account Deleted > printf( "%tu\n", (shared [] char*)p - (shared [] char*)NULL ); > > If someone has an implementation for which these printfs would produce different > output, I'd be interested in hearing why. In addition to violating C99's rule about pointer subtraction between pointers to separate objects, this also runs afoul of the UPC PTS subtraction semantics: the result is undefined unless there exists an integer x, representable as a ptrdiff t, such that (pts1 + x) == pts2 AND upc phaseof(pts1 + x) == upc phaseof(pts2). In this case (pts2 - pts1) evaluates to x. When *p has affinity to a non-zero thread, there may not be a value of x that you can add to NULL to get p. An implementation would be well within its rights to detect this as a fatal error. Barring that, the result of the subtraction would depend on the implementation-specific details of PTS NULL and the pointer representation.
Reported by
danbonachea
on 2013-02-28 08:47:06 -
Account Deleted Dan, everything you wrote in Comment #22 is true, but I'm asking if any implementation actually does treat this as a fatal error, not if they could in theory. Paul's looking for some way to write a hash function without upc_addrfield(). If we were to deprecate upc_addrfield() but define PTS - NULL for indefinite block size, that would leave a mechanism for writing his hash function. If any current implementation treats PTS - NULL as a fatal error, then it's a bit harder for us to make a change that defines PTS - NULL. I'm simply trying to gauge how such a change would affect current implementations. Defining addition pointer subtraction semantics is not without precedent -- C++03 defines a null pointer minus a null pointer to be 0 for example, whereas C leaves it undefined. UPC could similarly define PTS - NULL. (We could also define a null PTS minus a null PTS to be 0 too, if we had reason.)
Reported by
johnson.troy.a
on 2013-02-28 15:53:23 -
Account Deleted > I'm asking if any implementation actually does treat this as a fatal error, not if they could in theory. BUPC DOES detect such a subtraction as a fatal error at runtime in debug mode, when the pointer argument has non-zero affinity. There are two possible errors based on how exactly the test is written and constant-folded in the frontend: UPC Runtime error: Illegal: subtraction of indefinite pointers with affinity to different UPC threads (7 and 0) at: issue107e_obj.c:78 UPC Runtime error: Assertion failure at _upcr_pshared_to_local() at /usr/common/ftg/upc/2.16.0/bupc-2.16.0-icc-12.1.3/runtime/inst/dbg/include/upcr_sptr.h:689: upcr_isnull_pshared(sptr) || upcr_threadof_pshared(sptr) == upcr_mythread() at: issue107d_obj.c:76 Paul's usage case of PTS hashing has changed my mind on this issue - I think generating an appropriate hash for a PTS is sufficiently problematic and dependent on implementation-details that it should probably be encapsulated as a library function to allow portable hashing. Strengthening upc_addrfield() to fill that need (as suggested in comment #17) seems reasonable and in keeping with the "spirit" of the original (ie "an implementation-defined value reflecting the “local address” of the object"), and also compatible with the other known usage case of printing PTS values for debugging.
Reported by
danbonachea
on 2013-02-28 16:34:35 -
Account Deleted Thanks Dan, the BUPC example answers my question. A library function for generating a PTS hash may be problematic for two reasons. Such a function would need to accept a shared void*, so if one wanted to hash based on all three parts of a PTS, not just the thread and addrfield, it's not possible because the phase is thrown away. Second, and probably more importantly, hash functions are chosen carefully for performance reasons depending on what a specific application needs. Trying to provide a one-size-fits-all hash function for everyone via a upc_hash( shared void* ) function may be pointless because everyone will ignore it and write their own. Strengthening upc_addrfield() enough to allow people to write their own hash function sounds better to me.
Reported by
johnson.troy.a
on 2013-02-28 17:14:13 -
Account Deleted Ignore my first reason in Comment #25. Steve corrected me that shared void* preserves phase; I had forgotten that.
Reported by
johnson.troy.a
on 2013-02-28 17:31:10 -
Account Deleted > Trying to provide a one-size-fits-all hash function for everyone via a upc_hash( > shared void* ) function may be pointless because everyone will ignore it and write > their own. Apologies, my comment #24 was a bit unclear. What I meant was: "Generating an appropriate (value to be used in computing) a hash for a PTS is sufficiently problematic and dependent on implementation-details that it should probably be encapsulated as a library function to allow portable hashing." I think Paul was not suggesting to write the actual hashing function (ie the one that maps a data value to a bucket) but merely the utility that provides an appropriately unique integral value representing the object address, with the guarantees he suggests. There is currently no portable way to get such a value from a PTS, which implies there is no portable way to hash a PTS. The user is the one who hashes the return value to compute an actual bucket, and can choose to fold in additional information such as the result of upc_phaseof()/upc_threadof() as seems fit for his application. > Strengthening upc_addrfield() enough to allow people to write their own hash function sounds better to me. I'm pretty sure that's what Paul is proposing in comment #17.
Reported by
danbonachea
on 2013-02-28 17:40:25 -
Account Deleted >> Strengthening upc_addrfield() enough to allow people to write their >> own hash function sounds better to me. > > I'm pretty sure that's what Paul is proposing in comment #17. Exactly. I don't want a upc_hash_pts(), I just want upc_addrfield() to become well-defined enough that one can do something like the following: h = my_hash(upc_addrfield(p)) // good if p has known affinity h = my_hash(upc_addrfield(p) ^ upc_threadof(p)) // better when affinity may vary Additionally, upc_addrfield() would retain its usefulness for debugging (and writing of spec conformance tests).
Reported by
phhargrove@lbl.gov
on 2013-02-28 18:37:43 -
Account Deleted I propose the following change. I believe it strengthens upc_addrfield() sufficiently to satisfy all of Paul's concerns. Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -302,11 +302,27 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{If two pointers-to-shared compare equal, then they + shall produce the same value when passed to {\tt upc\_addrfield}. If two + pointers-to-shared do not compare equal, they may still produce the same + value when passed to {\tt upc\_addrfield}, but only if they point to shared + objects with affinity to different threads.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared {\tt S1} and {\tt S2} point + to distinct bytes of the same shared object which have affinity to the same + thread, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall evaluate on all threads to the same value that\\* + {\tt ((char *)S2 - (char *)S1)}\\* + evalutes to on the thread with which the pointed-to bytes have affinity.} + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-02-28 19:25:28 -
Account Deleted Comment on Paul's list in #17: > Properties I do NOT expect: > + Global uniqueness > Equality of upc_addrfield() does NOT imply anything when their threads differ Huh. I would have sworn it was required that, for a declaration like shared T A[THREADS]; that upc_addrfield(&A[i]) would yield the same value for all 0 <= i < THREADS. But no, it's not there. That makes upc_addrfield even less useful. It was certainly the original expectation.
Reported by
brian.wibecan
on 2013-02-28 19:39:50 -
Account Deleted Brian, While your stated/desired property in comment #30 is true for MANY implementations of statically declared variables it is STILL incorrect to infer that (upc_addrfield(p) == upc_addrfield(q)) implies that p and q point to the same array. For instance, if p and q were dynamically allocated by upc_alloc(). Thus even if your stated property was true, my statement about no "global uniqueness" would remain true. Currently, nothing constrains implementations to have a "symmetric heap" property for static allocations. This property WAS the case for the T3E, and is likely to still be true of most implementations because it is just simpler to deal with. George or Yili may correct me if I have this wrong, but I believe the BG/L implementation of UPC lacked a symmetric heap and communicated "on the wire" in terms of some sort of GUID (or handle) and (since that was not a RDMA network) the code at the target node would perform translation/table-lookup, performing lazy allocation if the handle had not been seen before. Note that an implementation could have a symmetric heap and still not satisfy your expectation if the heaps are offset across nodes and upc_addrfield() returns the full virtual address (though that could be "fixed" by returning the offset-within-heap). So, YES, upc_addrfield() is less useful than some may have thought.
Reported by
phhargrove@lbl.gov
on 2013-02-28 19:59:58 -
Account Deleted Brian, the requirement that upc_addrfield( &A[i] ) yield the same value for all threads for shared T A[THREADS] would be too strong. If you have UPC purely inside a shared memory environment, the entire array could be laid out in the same virtual memory space such that every A[i] is at a different address and the implementation could be using those locations for the result of upc_addrfield(). There are also non shared memory systems unfortunate enough to have had address layout randomization enabled such that true symmetric addressing is made quite painful.
Reported by
johnson.troy.a
on 2013-02-28 20:04:01 -
Account Deleted I endorse the intent and scope of Steve's proposed change in comment #29, but anticipate we'll want to refine the wording slightly. I think Dan is best qualified to take a shot at that. Since Steve mostly reworded my "prose", I am "too close" to the text to be an impartial editor. However, I'll make a few comments: + The constraint beginning "If two pointers-to-shared compare equal" should probably split in two, making one point at a time to make each one stronger. + Assuming the split suggested in my previous bullet, the "If two pointers-to-shared do not compare equal" half probably needs rewording - it just feels "clumsy" to me as written now. I would avoid phrases like "but only if" which can be hard to understand, when we have the formal "implies" at our disposal. The following is a "forward" version: ((p != q) && (upc_addrfield(p) == upc_addrfield(q))) -> (upc_threadof(p) != upc_threadof(q)) + "distinct bytes" doesn't seem necessary in the last constraint, as the expressions both evaluate to zero if the PTS reference the same byte. I am "neutral" on the question of 1.3 vs. 1.4 except the the extent that I think resolving issues 106 and 107 in the SAME spec seems likely to produce the best results.
Reported by
phhargrove@lbl.gov
on 2013-02-28 20:19:33 -
Account Deleted I just noticed the change to 6.4.2 made by the diff in comment #29. Such a change leaves constraint paragraphs 4 and 5 of 6.4.2 essentially pointless. They become all "setup" with no "punchline", because the remaining statements that taken pairwise (P1,S1) and (P2,S2) address the same objects is not of any consequence in defining address arithmetic. Steve, is it correct to assume you "yield" to Dan's change at the same location made by comment #61 of issue 106 (below)? -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). +\xadded[id=DB]{106}{ +\item The expression {\tt P1 + (S2 - S1) == P2} shall evaluate to 1.% +\truefootnote{This implies there is no padding inserted between blocks of shared array elements with affinity to a thread.} +}
Reported by
phhargrove@lbl.gov
on 2013-02-28 20:39:20 -
Account Deleted > Steve, is it correct to assume you "yield" to Dan's change at the same location made by comment #61 of issue 106 (below)? Yes. I actually like Dan's change--I just think that it is incomplete without also fixing upc_addrfield(). An updated proposal taking into account Paul's comments in comment 33. Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -302,11 +302,30 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{If two pointers-to-shared point to bytes with affinity + to the same thread, then the values resulting from passing them to + {\tt upc\_addrfield} shall compare equal if and only if the pointers + themselves compare equal.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared {\tt S1} and {\tt S2} point + to bytes with affinity to the same thread that are part of a single shared + object, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall evaluate on all threads to the same value that\\* + {\tt ((char *)S2 - (char *)S1)}\\* + evalutes to on the thread with which the pointed-to bytes have affinity.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared point to bytes with affinity + to different threads, then the result of comparing the values resulting + from passing them to {\tt upc\_addrfield} is undefined.} + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-02-28 20:44:28 -
Account Deleted +\np \xadded[id=SV]{107}{If two pointers-to-shared compare equal, then they + shall produce the same value when passed to {\tt upc\_addrfield}. If two + pointers-to-shared do not compare equal, they may still produce the same + value when passed to {\tt upc\_addrfield}, but only if they point to shared + objects with affinity to different threads.} The last clause of this paragraph specifies the "debatable" property from Paul's comment #17. I find that constraint unacceptable for the same reasons Paul listed - specifically, C99 does not guarantee size_t is large enough to contain a local address value (nor is it ever used for that purpose in C99 - size_t is for object sizes, (u)intptr_t are the integral types for containing address values). The function return type of size_t is not sufficient to guarantee every pointer-to-shared can be uniquely represented on every system, even when only considering pointers to objects with affinity a single thread. Consequently such a library specification could undesirably constrain the available shared memory on some systems. As Paul mentioned, the hashing usage case does not require this property. However if we want the property for other usage cases, then I think we must change the return type of upc_addrfield to ensure the result type is "wide enough" to uniquely represent any local pointer. Note that ptrdiff_t is also not guaranteed to be wide enough for this purpose (in fact C99 allows it to be smaller than size_t!). The most natural choice for a "wide-enough" return type would be uintptr_t or intptr_t - unfortunately these are designated as an OPTIONAL type in C99 (and also C11). To my knowledge these types are available on all systems of interest, as the architectural hardware to support them is usually the same as that used to support pointer arithmetic. UPC already requires a great many additional types and features relative to C99, so making that optional type mandatory for a compliant UPC implementation does not seem overly burdensome to implementers. A different concern is the effect on existing clients of upc_addrfield of changing the return value - although if we truly believe that's currently a small set then perhaps that minor break in backwards compatibility is acceptable. Of course the other option to changing the return type is to weaken the constraint and allow "false duplicates" in the return value.
Reported by
danbonachea
on 2013-02-28 20:46:18 -
Account Deleted Our comments passed in the ether. My comment #36 applies equally to the new proposed paragraph: +\np \xadded[id=SV]{107}{If two pointers-to-shared point to bytes with affinity + to the same thread, then the values resulting from passing them to + {\tt upc\_addrfield} shall compare equal if and only if the pointers + themselves compare equal.}
Reported by
danbonachea
on 2013-02-28 20:49:59 -
Account Deleted I also object to this proposed text: +\np \xadded[id=SV]{107}{If two pointers-to-shared point to bytes with affinity + to different threads, then the result of comparing the values resulting + from passing them to {\tt upc\_addrfield} is undefined.} The return value of the function is already "implementation-defined" by default in paragraph 1. Subsequent paragraphs in the proposal further constrain this value. But the constraint above essentially implies that a comparison operator using two values returned by the function can have undefined behavior (ie crash the program, launch the nukes), which is way too strong. I believe it's sufficient to delete this paragraph and leave the "implementation-defined" default from paragraph 1 in force for that case. If we feel it's necessary to amplify this point, it should be reformulated as "unspecified behavior", and written in a generic way that is not specific to a particular arithmetic operator.
Reported by
danbonachea
on 2013-02-28 21:00:40 -
Account Deleted How about the following, borrowing from the C99 definition of [u]intptr_t: Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -302,11 +302,37 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{If two pointers-to-shared point to bytes with affinity + to the same thread, then the values resulting from passing them to + {\tt upc\_addrfield} shall compare equal if the pointers themselves compare + equal.} + +\np \xadded[id=SV]{107}{If {\tt size\_t} has the property that any valid + pointer to {\tt void} can be converted to it, then converted back and + compare equal to the original pointer, then the results of passing + two pointers-to-shared that point to bytes with affinity to the same thread + to {\tt upc\_addrfield} shall compare equal only if the pointers themselves + compare equal.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared {\tt S1} and {\tt S2} point + to bytes with affinity to the same thread that are part of a single shared + object, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall evaluate on all threads to the same value that\\* + {\tt ((char *)S2 - (char *)S1)}\\* + evalutes to on the thread with which the pointed-to bytes have affinity.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared point to bytes with affinity + to different threads, then the result of comparing the values resulting + from passing them to {\tt upc\_addrfield} is undefined.} + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-02-28 21:03:48 -
Account Deleted Re: Dan's argument against per-thread uniqueness. Since Steve's agreement to the current proposal for issue #106 is conditional on resolving this issue (#107), I'd like to propose a two-stage approach: 1) To publish 1.3 without a lengthy debate on the "per-thread uniqueness" property, I suggest that Steve adjust the proposed text to EXCLUDE this property. 2) That for 1.4 we engage in the debate that will include the possibility of widening the return type to allow this property to be satisfied (FWIW, I don't see how one could implement a UPC runtime without having a [u]intptr_t in the base C compiler). Unless I am missing something, adding this constraint in 1.4 cannot break anything EXCEPT issues related (directly or indirectly) to the return type. If that is incorrect, then we'll have to hash this out sooner rather than later.
Reported by
phhargrove@lbl.gov
on 2013-02-28 21:04:47 -
Account Deleted > The return value of the function is already "implementation-defined" by default in paragraph 1. Subsequent paragraphs in the proposal further constrain this value. But the constraint above essentially implies that a comparison operator using two values returned by the function can have undefined behavior (ie crash the program, launch the nukes), which is way too strong. I believe it's sufficient to delete this paragraph and leave the "implementation-defined" default from paragraph 1 in force for that case. If we feel it's necessary to amplify this point, it should be reformulated as "unspecified behavior", and written in a generic way that is not specific to a particular arithmetic operator. This is true. I'll remove it in the next diff.
Reported by
sdvormwa@cray.com
on 2013-02-28 21:07:19 -
Account Deleted Removing the paragraph about undefined results: Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -302,11 +302,33 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{If two pointers-to-shared point to bytes with affinity + to the same thread, then the values resulting from passing them to + {\tt upc\_addrfield} shall compare equal if the pointers themselves compare + equal.} + +\np \xadded[id=SV]{107}{If {\tt size\_t} has the property that any valid + pointer to {\tt void} can be converted to it, then converted back and + compare equal to the original pointer, then the results of passing + two pointers-to-shared that point to bytes with affinity to the same thread + to {\tt upc\_addrfield} shall compare equal only if the pointers themselves + compare equal.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared {\tt S1} and {\tt S2} point + to bytes with affinity to the same thread that are part of a single shared + object, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall evaluate on all threads to the same value that\\* + {\tt ((char *)S2 - (char *)S1)}\\* + evalutes to on the thread with which the pointed-to bytes have affinity.} + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-02-28 21:08:53 -
Account Deleted Regarding the following from comment #39 > If two pointers-to-shared point to bytes with affinity > to the same thread, then the values resulting from passing them to > {\tt upc\_addrfield} shall compare equal if the pointers themselves > compare equal. PTS equality IMPLIES affinity to the same thread, making the first clause redundant. How about: If two pointers-to-shared compare equal, then the values resulting from passing them to {\tt upc\_addrfield} shall compare equal.
Reported by
phhargrove@lbl.gov
on 2013-02-28 21:10:47 -
Account Deleted > Our comments passed in the ether. My comment #36 applies equally to the new proposed paragraph > ... Does the new language address these concerns? I think it still provides the uniqueness guarantee for all platforms where size_t is "wide enough", while permitting implementations on platforms where that is not true.
Reported by
sdvormwa@cray.com
on 2013-02-28 21:13:35 -
Account Deleted Updated with Paul's comment from comment 43. Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -302,11 +302,32 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{If two pointers-to-shared compare equal, then + the values resulting from passing them to {\tt upc\_addrfield} shall + compare equal.} + +\np \xadded[id=SV]{107}{If {\tt size\_t} has the property that any valid + pointer to {\tt void} can be converted to it, then converted back and + compare equal to the original pointer, then the results of passing + two pointers-to-shared that point to bytes with affinity to the same thread + to {\tt upc\_addrfield} shall compare equal only if the pointers themselves + compare equal.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared {\tt S1} and {\tt S2} point + to bytes with affinity to the same thread that are part of a single shared + object, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall evaluate on all threads to the same value that\\* + {\tt ((char *)S2 - (char *)S1)}\\* + evalutes to on the thread with which the pointed-to bytes have affinity.} + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-02-28 21:15:09 -
Account Deleted Reported by
sdvormwa@cray.com
on 2013-02-28 21:18:15 - Labels added: Consensus-Medium, Milestone-Spec-1.3 - Labels removed: Consensus-Low, Milestone-Spec-1.4 -
Account Deleted More crossing-in-the-ether. My comment #40 requests deferral of the debate over per-thread uniqueness. HOWEVER, I see that Steve's comment #39 makes the property conditional on the width of size_t. I find that the be an ideal response to the objection that Dan raised (that I had already alluded to in comment #17).
Reported by
phhargrove@lbl.gov
on 2013-02-28 21:23:24 -
Account Deleted Another issue not yet mentioned that we need to be careful with here is what we guarantee about PTS arguments to upc_addrfield that reference objects with a limited lifetime (dynamically allocated shared data), and what we guarantee about the return value. I think there are two parts to this: 1) Add some requirement that the argument to upc_addrfield points to a (valid) shared object during the call. Paragraph 1 weakly implies this, but if we're strengthening constraints then I believe it should be directly stated. Something like "If ptr does not point to a shared object, behavior is undefined." 2) Be careful not to imply that upc_addrfield values "outlive" the object used to generate them. The current proposal text makes extensive use of the term "byte", but note that a "byte" may correspond to unallocated space containing no valid objects. For example if you did something like: shared void *p1 = upc_alloc(4096); size_t r1 = upc_addrfield(p1); upc_free(p1); shared void *p2 = upc_alloc(4096); size_t r2 = upc_addrfield(p2); It may happen to be the case that after this code (p1 == p2), but as they reference different objects I'm don't think its a good idea to require (r1 == r2). Conversely, if it happens to be the case that (p1 != p2), it seems like a bad idea to prohibit (r1 != r2).
Reported by
danbonachea
on 2013-02-28 21:26:23 -
Account Deleted /s/prohibit (r1 != r2)/require (r1 != r2)/
Reported by
danbonachea
on 2013-02-28 21:27:21 -
Account Deleted Getting down to the nitty-gritty now: Re: "If two pointers-to-shared {\tt S1} and {\tt S2} point to bytes with affinity to the same thread that are part of a single shared object" I don't like "point to bytes", because in mind S1 and S2 don't "point to bytes" unless their type is a pointer to char or array of char (where I mean "char" in all its signed and unsigned variations including typedefs like uint8_t). Alas, I don't have a good alternative suggestion off the top of my head. I'll look at C99 to see if I can find an analogous passage for guidance. Perhaps I'll find that "bytes" is the right term after all.
Reported by
phhargrove@lbl.gov
on 2013-02-28 21:31:02 -
Account Deleted in comment 50: s/in mind/in MY mind/ We are thinking faster than we can type today.
Reported by
phhargrove@lbl.gov
on 2013-02-28 21:33:09 -
Account Deleted > 2) Be careful not to imply that upc_addrfield values "outlive" the object used to generate them. The current proposal text makes extensive use of the term "byte", but note that a "byte" may correspond to unallocated space containing no valid objects. For example if you did something like: I think a better solution to this is to state (either in the allocation routines or the free routines) that any use of pointers to shared objects beyond that object's lifetime results in undefined behavior. Then we don't need to put anything special here (or other places where that causes problems, such as pointer-to-shared arithmetic ;) ).
Reported by
sdvormwa@cray.com
on 2013-02-28 21:33:38 -
Account Deleted > 1) Add some requirement that the argument to upc_addrfield points to a (valid) shared object during the call. Paragraph 1 weakly implies this, but if we're strengthening constraints then I believe it should be directly stated. Something like "If ptr does not point to a shared object, behavior is undefined." Note that none of the other PTS utility routines address this. We should probably update all of them (and probably some library routines as well) if we address this for upc_addrfield().
Reported by
sdvormwa@cray.com
on 2013-02-28 21:39:47 -
Account Deleted I am sure the following is imperfect, but it is a starting point. For instance I am doubtful about several of my word choices. + The argument to upc_addrfield() shall be a pointer to a statically allocated shared object, or to a dynamically allocated shared object that has not yet been freed (by upc_free or upc_all_free). + The result of applying upc_addrfield() to the address of a dynamically allocated shared object shall be valid only for the lifetime of the object, terminating when the object is freed. No constraint in this section applies to the result of upc_addrfield applied to a pointer to dynamic storage which has since been freed.
Reported by
phhargrove@lbl.gov
on 2013-02-28 21:43:22 -
Account Deleted > I don't like "point to bytes", because in mind S1 and S2 don't "point to bytes" unless their type is a pointer to char or array of char (where I mean "char" in all its signed and unsigned variations including typedefs like uint8_t). I'm not particularly attached to "bytes", but I can't use object because two pointers that point to different objects may compare equal (e.g. a pointer to a structure, and a pointer to the first element in it, assuming no padding). If you find something better, let me know.
Reported by
sdvormwa@cray.com
on 2013-02-28 21:43:59 -
Account Deleted +\np \xadded[id=SV]{107}{If {\tt size\_t} has the property that any valid + pointer to {\tt void} can be converted to it, then converted back and + compare equal to the original pointer, then the results of passing + two pointers-to-shared that point to bytes with affinity to the same thread + to {\tt upc\_addrfield} shall compare equal only if the pointers themselves + compare equal.} I don't like this proposal because it feels like "passing the buck" on portability to the client, who is left with a non-portable property. If they don't need that property then it's not a problem, but if they do need it and want portable code, then they are left with essentially no solution. The systems in question generally DO have an integer type wide enough to hold any PTL, it's just not size_t. Nor is there any expectation or even recommendation in C99 that size_t should be wide enough for this purpose - it just "happens" to be true on many common systems. The user can already write non-portable code to scan the bits in a PTS and munge them any way they want. If our primary goal is to provide a *portable* way to convert PTS to integer values with certain properties, then we should ensure those properties are provided portably. If the property is important enough to provide, then we should arrange types to make sure it can be implemented everywhere. If we believe we can live without the property, then remove it.
Reported by
danbonachea
on 2013-02-28 21:45:53 -
Account Deleted More crossing in the ether... In time-distorted-response to Steve's comment #53, I withdraw the "The argument to..." constraint I propose in comment #54. He is right that we have not made such explicit constraints on the arguments to other functions taking a PTS, and doing so once sets a precedent that sets us up for a lot more work. I think my second ("The result of...") constraint is still worth discussion to see if it addresses Dan's concern.
Reported by
phhargrove@lbl.gov
on 2013-02-28 21:49:57 -
Account Deleted Actually, C99 already has language to prohibit this (ISO/IEC 9899 6.2.4 2): The lifetime of an object is the portion of program execution during which storage is guaranteed to be reserved for it. An object exists, has a constant address,25) and retains its last-stored value throughout its lifetime.26) If an object is referred to outside of its lifetime, the behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifetime.
Reported by
sdvormwa@cray.com
on 2013-02-28 21:49:57 -
Account Deleted > I don't like this proposal because it feels like "passing the buck" on portability to the client, who is left with a non-portable property. If they don't need that property then it's not a problem, but if they do need it and want portable code, then they are left with essentially no solution. The systems in question generally DO have an integer type wide enough to hold any PTL, it's just not size_t. Nor is there any expectation or even recommendation in C99 that size_t should be wide enough for this purpose - it just "happens" to be true on many common systems. Would you rather we simply require uintptr_t and make that the return type? Remove the uniqueness guarantee altogether? Something else? I don't have a strong opinion either way on this particular aspect of upc_addrfield().
Reported by
sdvormwa@cray.com
on 2013-02-28 21:54:37 -
Account Deleted In comment #55 Steve responds to my comment #50: > I'm not particularly attached to "bytes", but I can't use object because two > pointers that point to different objects may compare equal (e.g. a pointer to > a structure, and a pointer to the first element in it, assuming no padding). > If you find something better, let me know. What I found (so far) is a portion of C99 section 6.3.2.3: Pointers. > When a pointer to an object is converted to a pointer to a character type, > the result points to the lowest addressed byte of the object. Successive > increments of the result, up to the size of the object, yield pointers > to the remaining bytes of the object. This seems to be fully consistent with Steve's proposed text which is treating an object as a sequence of bytes. I am still looking for a "better" term, but don't expect to find one.
Reported by
phhargrove@lbl.gov
on 2013-02-28 21:59:35 -
Account Deleted > The value of a pointer becomes indeterminate when the object it points to reaches > the end of its lifetime. Yes, the problem is the value returned by upc_addrfield() while the object was valid does not automatically become indeterminate when the object is later deallocated, unless we state that restriction. > Would you rather we simply require uintptr_t and make that the return type? Remove the uniqueness guarantee altogether? I don't have a strong opinion on the importance of the actual property, so it depends on whether we see an important usage case that needs it. Although in the spirit of paragraph 1, if we want the return value of this function to represent "a local address", then it should probably be a type large enough to hold one. I don't really see a problem with requiring uintptr_t for UPC compliance, and given the massive changes we're about to make in the semantics, changing the return type seems like small potatoes. If someone is really worried about backwards compatibility, we could possibly deprecate upc_addrfield() and introduce our new semantics under a new function name.
Reported by
danbonachea
on 2013-02-28 22:06:37 -
Account Deleted As an addendum to comment #48, we should probably also explicitly state whether upc_addrfield(NULL) is permitted or prohibited, and if permitted what the behavior should be.
Reported by
danbonachea
on 2013-02-28 22:15:31 -
Account Deleted > Yes, the problem is the value returned by upc_addrfield() while the object was valid does not automatically become indeterminate when the object is later deallocated, unless we state that restriction. Yes, but you cannot compare the corresponding pointers without running into undefined behavior. Since the constraint is defined in terms of the result of comparing the corresponding pointers, I don't think we need any further clarification.
Reported by
sdvormwa@cray.com
on 2013-02-28 22:16:34 -
Account Deleted > I don't like "point to bytes", because in mind S1 and S2 don't "point to bytes" > unless their type is a pointer to char or array of char (where I mean "char" in all > its signed and unsigned variations including typedefs like uint8_t). I agree with Paul here - it is a type error to say that an (int *) "points to a byte". It points to an object of type int that is comprised of a sequence of bytes. However I think the second occurrence of "byte" in the comment #45 proposal can safely be replaced with "object" (as in the original wording from 6.4.2).
Reported by
danbonachea
on 2013-02-28 22:21:09 -
Account Deleted In comment #56 Dan is unhappy w/ Steve's proposed compromise on the per-thread uniqueness property, and in comment #59 Steve indicates his flexibility in resolving this. In comment #61 Dan states that doesn't have a strong opinion on the importance of this property. Since I am the one who drew up the original list of requirements, I guess I should "weigh in". Though I already said I was happy w/ the conditional-on-the-width-of-size_t approach that Dan is unhappy with, now that I have read Dan's comment, I see his point-of-view and am somewhat sympathetic to it. I therefore suggest (again) that we continue to debate this particular constraint for possible inclusion in 1.4, and that we plan to omit if from 1.3. FWIW: The direct consequences of omitting this constraint is that we don't require (nor prohibit) that upc_addrfield() be a bijective (one-to-one) function. In the context of hashing that is of no concern, since the hash generation will almost always be a many-to-one mapping. There do exist hashing functions which are intended to preserve all the bits and only produce "randomness", and they would not be able to use upc_addrfield() without this property. Additionally, debugging checks and spec test cases would need to be constructed never to assume equality of upc_addrfield() applied to different PTSs implied any relationship between the objects the reference.
Reported by
phhargrove@lbl.gov
on 2013-02-28 22:22:22 -
Account Deleted In comment #61 Dan wrote, in part: > Although in the spirit of paragraph 1, if we want the return value of this > function to represent "a local address", then it should probably be a type > large enough to hold one. To me the key word in para 1 is "represent". Does that allow a "truncated representation" or not? I am of the opinion that for 1.3 we should (continue to) allow the possibility that the representation is truncated.
Reported by
phhargrove@lbl.gov
on 2013-02-28 22:27:13 -
Account Deleted In comment #62 Dan asks about the legality and result of upc_addrfield(NULL). My vote is that this is illegal. However, if we do make it legal then it returns "an implementation-defined value" just as for any other PTS.
Reported by
phhargrove@lbl.gov
on 2013-02-28 22:30:24 -
Account Deleted >> The lifetime of an object is the portion of program execution during which storage is >> guaranteed to be reserved for it. An object exists, has a constant address,25) and retains its >> last-stored value throughout its lifetime.26) If an object is referred to outside of its lifetime, the >> behavior is undefined. The value of a pointer becomes indeterminate when the object it points to reaches >> the end of its lifetime. >Yes, but you cannot compare the corresponding pointers without running into undefined > behavior. Since the constraint is defined in terms of the result of comparing the > corresponding pointers, I don't think we need any further clarification. C99 3.17.2: indeterminate value either an unspecified value or a trap representation Use of an unspecified value does not automatically imply undefined behavior. I believe it was Troy that brought up this exact point with respect to the non-blocking memcpy library. The fact that p1 has indeterminate value after the free does not mean the pointer comparison is prohibited, just that it's not guaranteed to have a specified result. We should not guarantee properties if the comparison happens to return true (or conversely false). Note that most library functions accepting pointers are relying on the part above that says "If an object is referred to outside of its lifetime, the behavior is undefined". However this function (and also upc_threadof/upc_phaseof) do not refer to an object. They operate soley upon the pointer value. Their argument does not even have an complete referenced type.
Reported by
danbonachea
on 2013-02-28 22:32:41 -
Account Deleted > I therefore suggest (again) that we continue to debate this particular constraint > for possible inclusion in 1.4, and that we plan to omit if from 1.3. Firstly, I'm not convinced any of these semantic changes to upc_addrfield should go into 1.3. We have direct experience with our two implementations, but no input so far on how this might impact other implementations. We also have a very limited set of usage scenarios under consideration, and have not gathered widespread input on other potential clients. There may be other "useful" properties we should include that we just haven't thought of yet. Secondly, if we're going to make "big changes" to a library function semantics, that is also the logical time to change the return value (if we're ever going to take that action). Polishing up the semantics in a way to encourage widespread usage, and then breaking backward compatibility by changing the return type in the next revision seems like asking for trouble. We should decide whether the property matters and either include it with all the other changes, or discard it permanently. If we don't feel qualified to make that decision right now, that sounds like even more reason to delay the semantic changes until the matter has received further study and we're sure they cover everything we will ever want.
Reported by
danbonachea
on 2013-02-28 22:44:02 -
Account Deleted > However I think the second occurrence of "byte" in the comment #45 proposal can safely be replaced with "object" (as in the original wording from 6.4.2). No, because I'm trying to capture the following: shared int a; shared [] char *cp = (&a) + 1; (upc_addrfield( cp ) - upc_addrfield( &a )) == 1) The wording from 6.4.2 doesn't work here as far as I can tell, because, as you pointed out, the objects are different.
Reported by
sdvormwa@cray.com
on 2013-02-28 22:56:18 -
Account Deleted I think we may want/need to make the following explicit: upc_addrfield((T1*)p) == upc_addrfield((T2*)p) This is NOT the same as "If two pointers-to-shared compare equal, then the values resulting from passing them to upc_addrfield shall compare equal", since T1 and T2 don't need to be compatible types and thus their pointer comparison might not be legal. Note that this thought was initially motivated by the case where either T1 or T2 is "shared void" as will be the likely case for the formal parameter in a shared hash table implementation. For that case, the existing "equal PTS -> equal addrfield" constraint *is* sufficient since the spec allows comparison between (shared void *) and any other PTS type. However, this issue becomes important if one uses (shared uint8_t *) to treat objects as arrays of bytes in one portion of ones code (a third party library for instance) and by their "actual" type in another and need to "communicate" using the upc_addrfield value (as through a hash key generated from them).
Reported by
phhargrove@lbl.gov
on 2013-02-28 23:00:38 -
Account Deleted Returning to the "point to bytes" text, let us take a step back to see if we can agree what scenarios we wish to cover. I can think of two "base" cases, and their "convolution": 1) We have P1 and P2 which are PTS referencing elements of a shared array object, and those elements have affinity to the same thread. 2) We have P3 and P4 which are PTS (probably of different type, BTW) that reference either a shared struct and a field in that struct, or two fields in the same shared struct. 1@2) The "convolution" of (1) and (2) in which P5 and P6 are references to structs or members of structs where those structs themselves are elements of a shared array, but still with the same-affinity constraint. I propose that we can "For the purposes of this section..." reduce all these cases to the case of an indefinite shared array of bytes (and reliance on the constraint I suggest in my previous comment). I just don't have the language to express that well.
Reported by
phhargrove@lbl.gov
on 2013-02-28 23:05:09 -
Account Deleted > This is NOT the same as "If two pointers-to-shared compare equal, then the values resulting from passing them to upc_addrfield shall compare equal", since T1 and T2 don't need to be compatible types and thus their pointer comparison might not be legal. Actually, as Dan has had to point out to me so many times, because the argument to upc_addrfield() is a generic pointer-to-shared, all type information is lost, and the pointer comparison IS legal.
Reported by
sdvormwa@cray.com
on 2013-02-28 23:05:54 -
Account Deleted A new proposal, taking into account a lot of the conversation (perhaps not all though) since the last one. In particular, I've worked around the "points to bytes" issue, and added text to prevent the constraints from applying beyond an objects lifetime. Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -222,6 +222,15 @@ \subsubsection{Pointer-to-shared manipulation functions} +\np \xadded[id=SV]{107}{The UPC pointer-to-shared manipulation functions that + accept a pointer-to-shared argument have undefined behavior when passed a + pointer-to-shared that points to a shared object outside of the lifetime + of that object.} + +\np \xadded[id=SV]{107}{The results of UPC pointer-to-shared manipulation + functions that accept a pointer-to-shared argument become unspecified if + the pointer becomes unspecified.} + \paragraph{The {\tt upc\_threadof} function} \label{upc_threadof} @@ -302,11 +311,28 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{If two pointers-to-shared compare equal\footnote{This + comparison occurs after any implict conversions to the generic + pointer-to-shared type.}, then the values resulting from passing them to + {\tt upc\_addrfield} shall compare equal.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared {\tt S1} and {\tt S2} point + shared objects with affinity to the same thread, and the expression\\* + {\tt ((char *)S2 - (char *)S1)}\\* + has a defined value when evaluated on that thread, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall produce the same value when evaluated on any thread.} + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + the value 0 when passed the {\em null pointer-to-shared}}. + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-02-28 23:11:55 -
Account Deleted A slight change to the proposal in comment 74, removing the redundant "on any thread" from the second to last constraint. Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -222,6 +222,15 @@ \subsubsection{Pointer-to-shared manipulation functions} +\np \xadded[id=SV]{107}{The UPC pointer-to-shared manipulation functions that + accept a pointer-to-shared argument have undefined behavior when passed a + pointer-to-shared that points to a shared object outside of the lifetime + of that object.} + +\np \xadded[id=SV]{107}{The results of UPC pointer-to-shared manipulation + functions that accept a pointer-to-shared argument become unspecified if + the pointer becomes unspecified.} + \paragraph{The {\tt upc\_threadof} function} \label{upc_threadof} @@ -302,11 +311,28 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{If two pointers-to-shared compare equal\footnote{This + comparison occurs after any implict conversions to the generic + pointer-to-shared type.}, then the values resulting from passing them to + {\tt upc\_addrfield} shall compare equal.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared {\tt S1} and {\tt S2} point + shared objects with affinity to the same thread, and the expression\\* + {\tt ((char *)S2 - (char *)S1)}\\* + has a defined value when evaluated on that thread, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall evaluate to the same value.} + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + the value 0 when passed the {\em null pointer-to-shared}}. + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-02-28 23:21:08 -
Account Deleted As an aside, why is upc_affinitysize() in the "Pointer-to-shared manipulation functions" sections? It seems out of place. That's the reason for the oddly phrased "UPC pointer-to-shared manipulation functions that accept a pointer-to-shared argument" rather than simply "UPC pointer-to-shared manipulation functions".
Reported by
sdvormwa@cray.com
on 2013-02-28 23:24:22 -
Account Deleted +\np \xadded[id=SV]{107}{The UPC pointer-to-shared manipulation functions that + accept a pointer-to-shared argument have undefined behavior when passed a + pointer-to-shared that points to a shared object outside of the lifetime + of that object.} This doesn't really "work", because a pointer to an object outside the lifetime of that object technically no longer points to that object. It also doesn't capture PTS containing other unspecified values (eg. an uninitialized pointer which is a stack variable). It should probably instead say something like "if the argument does not point to a shared object and is not a null pointer-to-shared, then the function has undefined behavior.". Also regarding upc_addrfield(NULL), note that upc_threadof(NULL) and upc_phaseof(NULL) ARE explicitly permitted and defined to return zero.
Reported by
danbonachea
on 2013-02-28 23:28:56 -
Account Deleted +\np \xadded[id=SV]{107}{If two pointers-to-shared compare equal\footnote{This + comparison occurs after any implict conversions to the generic + pointer-to-shared type.}, then the values resulting from passing them to + {\tt upc\_addrfield} shall compare equal.} I understand your approach here, but the phrasing isn't quite right. There is no implict(sic) conversion for comparing two actual arguments, implicit conversion occurs upon function call (so you'd have to talk about comparison of the formal arguments, across different function calls, yuk). Perhaps it should be spelled out via formula, eg: For any two pointers-to-shared S1 and S2, if (shared void *)S1 == (shared void *)S2, then upc_addrfield(S1) shall return the same value as upc_addrfield(S2).
Reported by
danbonachea
on 2013-02-28 23:38:08 -
Account Deleted I an alternative solution to the "point to bytes" by restating the byte-oriented-within-an-object constraint along the following lines: Given two pointer-to-shared P1 and P2: IF P1 and P2 referencing objects with affinity to the same thread AND (P1-P2) is has a defined value AND P1 is not an incomplete type THEN (upc_addrfield(P1) - upc_addrfield(P2)) == (P1-P2)*sizeof(*P1) Note that "(P1-P2) has a defined value" here is an indirect way of requiring that they are pointers to some portion of the same object (and additionally requires compatible types). It appears that Steve had this same idea in drafting what appears in comment #74 (and 75), which didn't exist when I began typing this. Well, guess what? We already have text that is nearly equivalent and it has been right under our nose (in Dan's comment #61 in issue 106): +\np Given the following declarations: + +\begin{verbatim} + T *P1, *P2; + shared T *S1, *S2; + + P1 = (T*) S1; /* allowed if upc_threadof(S1) == MYTHREAD */ + P2 = (T*) S2; /* allowed if upc_threadof(S2) == MYTHREAD */ +\end{verbatim} + + For all S1 and S2 that point to two distinct elements of + the same shared array object which have affinity to the same + thread, the expression:\\ + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t)upc\_addrfield(S1))} \\ + shall evaluate to the same value as: {\tt ((P2 - P1) * sizeof(T))}. I think keeping this minor revision of the time-honored text being removed from 6.4.2 also helps reassure the reader that the we've NOT fundamentally *altered* upc_addrfield() in the 1.3 spec - just added constraints required to make it useful. Then, as I mention in comment #72 a reduction of the other cases to array-of-bytes provides all of the byte-oriented guarantees I was seeking. Such a reduction to array-of-bytes does NOT need to appear in the spec, just as long as we include the type-independent constraint I suggested in comment #71, which allows the reader to perform such reduction if/when they need to prove some theorem. Added "in proof": since comment #74 appeared after I began drafting this comment, the text beginning "just as long as..." in the previous paragraph is probably irrelevant now.
Reported by
phhargrove@lbl.gov
on 2013-02-28 23:43:55 -
Account Deleted I agree w/ Dan's comment #78 that the approach taken is good but the phrasing is poor. Dan's suggested replacement is as unambiguous as we are likely to get.
Reported by
phhargrove@lbl.gov
on 2013-02-28 23:46:44 -
Account Deleted In comment #77 Dan states: > note that upc_threadof(NULL) and upc_phaseof(NULL) ARE explicitly > permitted and defined to return zero. I guess I should have checked those before writing comment #67. I change my "vote" to: Add a constraint reading "upc_addrfield(NULL) shall return zero" The constraint that now begins "The upc_addrfield function returns an implementation-defined value ..." could then be prefixed with the following text: "When passed a non-NULL argument, "
Reported by
phhargrove@lbl.gov
on 2013-02-28 23:54:45 -
Account Deleted I think (please correct me gently if I am wrong) that while some wording issues may remain, the only non-trivial point of contention remaining are: + The per-thread uniqueness constraint (include/conditional-on-size_t/exclude) + Dan is still not convinced (as of comment #69) that these changes belong in 1.3. I think we (Paul, Dan and Steve) should try to bring things to a point where we can send an email to the wg list (one NOT generated from the issue tracker) asking for outside opinions on a STATIC draft. As this issue has 60 comments today (counting this one), I doubt anyone is likely to jump in to state their views until we let the dust settle (otherwise it is like boarding a moving train). I am fairly confident that I have no more comments to offer on the current draft. However, I am hoping to see a new one that takes Dan's text in my comment #79 to replace the current "objects with affinity to the same thread" constraint, or a good "counter offer".
Reported by
phhargrove@lbl.gov
on 2013-03-01 00:21:19 -
Account Deleted I already defined that upc_addrfield( NULL ) == 0. Take a closer look at the last constraint. ;) > This doesn't really "work", because a pointer to an object outside the lifetime of that object technically no longer points to that object. It also doesn't capture PTS containing other unspecified values (eg. an uninitialized pointer which is a stack variable). Unfortunately, there ARE cases in C (and thus UPC) where we can generate meaningful pointers that don't point to an actual object. In particular, pointers that point just past the end of an array object are valid and have defined semantics, but do not point at any object. I believe the pointer-to-shared manipulation functions should generate the "expected" results when applied to such pointers. What about the following? Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -222,6 +222,17 @@ \subsubsection{Pointer-to-shared manipulation functions} +\np \xadded[id=SV]{107}{The UPC pointer-to-shared manipulation functions + that accept a pointer-to-shared argument have undefined behavior when + passed a pointer-to-shared that is the result of an expression with + undefined or unspecified value.} + +\np \xadded[id=SV]{107}{The results of UPC pointer-to-shared manipulation + functions that accept a pointer-to-shared argument are unspecified when + passed a pointer-to-shared that is the result of an expression with an + implementation defined value, and become unspecified when the pointer + becomes unspecified.} + \paragraph{The {\tt upc\_threadof} function} \label{upc_threadof} @@ -302,11 +313,27 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{If two pointers-to-shared compare equal after being + converted to generic pointers-to-shared, then the values resulting from + passing them to {\tt upc\_addrfield} shall compare equal.} + +\np \xadded[id=SV]{107}{If two pointers-to-shared {\tt S1} and {\tt S2} point + shared objects with affinity to the same thread, and the expression\\* + {\tt ((char *)S2 - (char *)S1)}\\* + has a defined value when evaluated on that thread, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall evaluate to the same value.} + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + the value 0 when passed the {\em null pointer-to-shared}}. + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-03-01 00:37:25 -
Account Deleted Split up the two paragraphs about pointer-to-shared manipulation functions into three for clarity. Reword the constraints on upc_addrfield() for clarity and to better match that in the other pointer-to-shared manipulation functions. Index: lang/upc-lib-core.tex =================================================================== --- lang/upc-lib-core.tex (revision 205) +++ lang/upc-lib-core.tex (working copy) @@ -222,6 +222,20 @@ \subsubsection{Pointer-to-shared manipulation functions} +\np \xadded[id=SV]{107}{The UPC pointer-to-shared manipulation functions + that accept a pointer-to-shared argument have undefined behavior when + passed a pointer-to-shared that is the result of an expression with + undefined or unspecified value.} + +\np \xadded[id=SV]{107}{The results of UPC pointer-to-shared manipulation + functions that accept a pointer-to-shared argument are implementation + defined when passed a pointer-to-shared that is the result of an + expression with an implementation defined value.} + +\np \xadded[id=SV]{107}{The results of UPC pointer-to-shared manipulation + functions that accept a pointer-to-shared argument become unspecified + when the pointer becomes unspecified.} + \paragraph{The {\tt upc\_threadof} function} \label{upc_threadof} @@ -302,11 +316,29 @@ \np The {\tt upc\_addrfield} function returns an implementation-defined value reflecting the ``local address'' of the - object pointed to by the pointer-to-shared argument.\footnote{% - This function is used in defining the semantics of pointer-to-shared - arithmetic in Section \ref{pointer-arithmetic}} + object pointed to by the pointer-to-shared argument. + +\np \xadded[id=SV]{107}{The return value of {\tt upc\_addrfield} shall be + independent of the calling thread.} - +\np \xadded[id=SV]{107}{Given two pointers-to-shared {\tt S1} and {\tt S2}, + if the expression\\* + {\tt ((shared void *)S1 == (shared void *)S2)}\\* + evaluates to true, then so shall the expression\\* + {\tt (upc\_addrfield(S1) == upc\_addrfield(S2))}} + +\np \xadded[id=SV]{107}{Given two pointers-to-shared {\tt S1} and {\tt S2}, + if the expression\\* + {\tt ((char *)S2 - (char *)S1)}\\* + has a defined value on any thread, then the expression\\* + {\tt ((ptrdiff\_t) upc\_addrfield(S2) - (ptrdiff\_t) upc\_addrfield(S1))}\\* + shall evaluate to the same value.} + +\np \xadded[id=SV]{107}{If {\tt ptr} is a null pointer-to-shared, the function + returns 0.} + +\xchangenote[id=SV]{107}{Constraints moved from 6.4.2 and cross-reference footnote removed.} + \paragraph{The {\tt upc\_affinitysize} function} {\bf Synopsis} Index: lang/upc-language.tex =================================================================== --- lang/upc-language.tex (revision 205) +++ lang/upc-language.tex (working copy) @@ -303,9 +303,8 @@ \begin{itemize} \item S1 and P1 shall point to the same object. \item S2 and P2 shall point to the same object. -\item The expression (({\tt (ptrdiff\_t) upc\_addrfield} (S2) - {\tt (ptrdiff\_t) upc\_addrfield(S1))} shall - evaluate to the same value as ((P2 - P1) * sizeof(T)). \end{itemize} +\xchangenote[id=SV]{107}{Constraint on {\tt upc\_addrfield} strengthened and moved to Section~\ref{upc_addrfield}.} \np Two compatible pointers-to-shared which point to the same object (i.e. having the same address and thread components) shall
Reported by
sdvormwa@cray.com
on 2013-03-01 00:57:34 -
Account Deleted > I already defined that upc_addrfield( NULL ) == 0. > Take a closer look at the last constraint. ;) Yup, I saw that after posting my comment. While I think this is the right thing to define, I have realized that there is a minor conflict with the "debatable" per-thread uniqueness property which I will now explain: A "likely" implementation of upc_addrfield() (used in BUPC in some cases) is an offset relative to the lowest address in the shared heap. That means that the "first" object allocated from the shared heap on thread 0 may have a upc_addrfield() of zero. Howerver, upc_threadof(NULL) == upc_adrfield(NULL) == 0. That would seem to imply that the first shared object allocated on thread 0 and the NULL PTS would "collide" in such a way as to violate the per-thread uniqueness property. I think the resolution is straight-forward: the per-thread uniqueness constraint (if any) must be worded to apply to non-NULL PTS only. FWIW: this "collision" can not occur in practice in BUPC because the runtime use memory at the "bottom" of the heap for internal purposes and thus no user-visible object can have that address. It is possible, but by no means certain, that other implementations that use an offset-from-bottom-of-heap have something similar in order to avoid having the internal representation of NULL and a valid object coincide.
Reported by
phhargrove@lbl.gov
on 2013-03-01 01:03:28 -
Account Deleted A usage-motivated question occurred to me while writing my previous comment, and lead to a significant change-of-heart for me: Do we want to say anything about the "alignment" of the upc_addrfield() values? For instance, lets take a pointer to a type (lets say shared double) that requires N-byte alignment on a given platform (N=8 on many ABIs). Do we guarantee that the upc_addrfield() is divisible by N? As with the issue of "width" (size_t vs uintptr_t) this comes down to what one wants to read into the phrase "reflecting the “local address”". How many properties of the local address are important enough that we are willing to constrain implementations of upc_addrfield() to "reflect" them? In response to my own question, I think that the more properties we can "reflect" the better, but that is based on a bias inherent in having an implementation where in the PTS representation contains either the full virtual address or the offset relative to a page-aligned base-of-shared-heap. So, for me the "trivial" implementation of upc_addrfield() preserves LOTS of properties, including "cache aligned" and "page aligned" properties that I have no intention of requiring in the spec. Having now (and not for the first time today) a new insight while composing one of these comments, I think I now have a good motivation to NOT require the per-thread uniqueness. This goes back to my almost throw-away mention in comment #31 of IBM's UPC implementation for BG/L, which used a GUID/handle in the PTS instead of anything directly connected to a virtual address. I began (while typing the previous paragraph) to wonder how they implemented upc_addrfield() in such a case. It occurred to that if your runtime addresses remote memory using a (GUID,thread,offset) tuple, then your upc_addrfield() is probably the offset FROM THE START OF THE CONTAINING OBJECT. Such an implementation *is* conforming to 6.4.2 as it stands in the 1.2 spec. I hope that is still permitted by all of the constraints we have discussed, with the exceptions of the per-thread uniqueness property. A upc_addrfield() implemented using (perhaps not alone) the offset from the base of the containing object is the only likely implementation for a system such as the BG/L where Put/Get are based on a target-side translation of a "handle". In such a case the offset of scalars is always zero, and for arrays is bounded by 0 and upc_affinitysize(). That, if taken alone, makes for a horrible input to a shared hash table implementation! That could, I suppose be remedied by "mixing" bits from the handle itself into the value returned from upc_addrfield(), but that is not something we can describe/mandate in the spec. Even if nobody runs on BG/L anymore, and even if I am wrong about some key implementation detail on BG/L, the fact remains that this offset-from-start-of-object implementation is currently allowed, would still be allowed by the current proposal, and yet doesn't address the usage case I presented to keep upc_addrfield() from being deprecated. On the whole I now feel OPPOSED to pushing additional constraints on upc_addrfield() into the 1.3 spec. I feel that we (though rightly I should only speak for myself) have taken too much for granted in our rush to resolve this issue. While I am very appreciative of Steve's LaTaX work today, I propose that for 1.3 we just use what Dan proposed in comment #61 of issue #106. That relocates the only pre-existing constraint to the proper document (thereby removing a cross-document forward reference) and it does NOT add any new constraints. We then carefully (less rush) work on additional constraints for the 1.4 spec.
Reported by
phhargrove@lbl.gov
on 2013-03-01 01:48:52 -
Account Deleted Paul said: > On the whole I now feel OPPOSED to pushing additional constraints on upc_addrfield() into the 1.3 spec. I feel > that we (though rightly I should only speak for myself) have taken too much for granted in our rush to resolve this issue. Echoing Paul's sentiment and stepping back for a moment, we're expending significant effort to ensure we can support portable hashing of PTS values via upc_addrfield(). This seems like a laudable goal from a programming language perspective, but I'm unclear on the true importance of this capability to real-world application code - this feature was motivated internally from a language design perspective, not by user request. Hashing a pointer value is a common practice in systems software and low-level libraries, because it allows you to transparently store hidden metadata about an object, without visibly affecting the pointer or object. However the utility of this technique is greatly reduced at the application level, where the programmer presumably has full control of the data structures and can usually store any necessary metadata directly in the relevant object - thus obviating the need for any hash structure and resulting in simpler code and slightly more efficient access. Applications usually hash on *data* values that are part of the problem domain, or possibly array indices, not pointer values that are implementation-defined, change from run to run, and have an arbitrary relation between separate objects. Am I missing something here? Do we have any examples of production codes that want to hash pointer values? It's difficult to decide the importance of various semantic details for a utility with no real-world client in mind. The only contrived example I can think of is to implement something like remote value caching at the application level, but at the app level that's traditionally done more simply and efficiently using replication. > While I am very appreciative of Steve's LaTaX work today, I propose that for 1.3 we just use what Dan proposed in > comment #61 of issue #106. That relocates the only pre-existing constraint to the proper document (thereby > removing a cross-document forward reference) and it does NOT add any new constraints. We then carefully (less > rush) work on additional constraints for the 1.4 spec. I agree on all counts. I think the length of this thread and the proliferation of semantic issues that just us three have uncovered in 24 hours related to redesigning this function is indication that further study is required before taking normative action. There are good reasons why we have a very long-duration process in place for introduction of new library functionality, generally starting with inclusion as an optional feature. Even if we end up keeping the same name for upc_addrfield(), the radical changes to the semantic guarantees that we're considering effectively make it a new library call. Any robust UPC code relying on the new guarantees would need to check the value of __UPC_VERSION__ before calling this new upc_addrfield, to avoid silently relying upon the underspecified "1.2 behavior" of the function that might result in undefined behavior. From that perspective, it might be preferable to introduce these semantics under a new library name (eg upc_pointertoint() or something else clever) with a dedicated feature macro to indicate its presence, and allow initial implementations to provide it as an optional feature while we evaluate the utility of the function and the design of its semantics in real application usage.
Reported by
danbonachea
on 2013-03-01 13:38:10 - Labels added: Milestone-Spec-1.4 - Labels removed: Milestone-Spec-1.3 -
Account Deleted > Am I missing something here? Kind of. I originally brought this issue up because upc_addrfield() is so ill-defined as to be useless. Its single constraint had the single purpose of attempting to provide the guarantee that issue 106 addresses. Because we're now addressing that guarantee in another way, I felt that it would be cleaner to simply remove it to avoid giving UPC (1.3) developers the impression that upc_addrfield() actually works the way they probably think it does. Then Paul chipped in with a potential use for a "stronger" upc_addrfield() that could not (easily) be done with any existing language features, and we spent the last 24 hours or so trying to make that work. In summary, the main focus of the bulk of the thread was indeed trying to find a way to support portable hashing of pointer-to-shared values, but the issue itself is not concerned with that per se. > There are good reasons why we have a very long-duration process in place for introduction of new library functionality That applies equally to the introduction of ANY language changes (*cough* issue 106 *cough*). The reason I pushed so hard to address this in 1.3 is because others pushed so hard to fix issue 106 in 1.3. These issues are EXTREMELY closely related, so fixing one without the other just makes things worse in my mind. > Even if we end up keeping the same name for upc_addrfield(), the radical changes to the semantic guarantees that we're considering effectively make it a new library call. Any robust UPC code relying on the new guarantees would need to check the value of __UPC_VERSION__ before calling this new upc_addrfield, to avoid silently relying upon the underspecified "1.2 behavior" of the function that might result in undefined behavior. It wouldn't be the first time. A similar thing happened when going from 1.1.1 to 1.2, so any "robust" UPC code must already check the value of __UPC_VERSION__.
Reported by
sdvormwa@cray.com
on 2013-03-01 16:40:13 -
Account Deleted > The reason I pushed so hard to address this in 1.3 is because others pushed so hard > to fix issue 106 in 1.3...fixing one without the other just makes things worse in my mind. Issue 106 is a CLARIFICATION to 6.4.2 that expresses the widespread implicit understanding of all implementers, tutorial materials and users about how blocked shared arrays must be laid out. Someone finally noticed that wasn't explicitly written down, and now it is. Everything else under discussion in this issue is clearly a CHANGE to existing semantics in section 7, with potential impact to users and implementers. A clarification and a change are two very different beasts, even if they happen to touch nearby paragraphs (that really never belonged nearby in the first place). I've worked very hard to satisfy the concerns of Steve and Troy regarding issue 106, and the current proposal makes the necessary clarification with exactly zero semantic changes or side-effects to upc_addrfield(). I believe this resolves Troy's concern about "subtly changing" upc_addrfield expressed in http://code.google.com/p/upc-specification/issues/detail?id=106#c59 although I'll wait for his opinion on the latest proposal. Paul and I have expressed strong opinions in favor of accepting the current proposal for 106 and deferring 107 for 1.4. I know we don't work under a voting system, but of the expressed opinions I believe Steve is currently "out-voted" on this matter. If our disagreement is insoluable, then we'll need to schedule another teleconference this coming week to discuss the matter amongst the entire committee and reach a final decision.
Reported by
danbonachea
on 2013-03-01 16:56:21 -
Account Deleted > Issue 106 is a CLARIFICATION to 6.4.2 that expresses the widespread implicit understanding of all implementers, tutorial materials and users about how blocked shared arrays must be laid out. Someone finally noticed that wasn't explicitly written down, and now it is. > > Everything else under discussion in this issue is clearly a CHANGE to existing semantics in section 7 Historically, the primary reason for those semantics in the first place was to (attempt to) provide the guarantees that issue 106 is addressing. Additionally, due to the poor existing wording in 7.2.3.4, users are likely to assume more constraints than they are guaranteed by the spec. Maintaining them simply because they were in 1.2 doesn't make sense to me in light of this.
Reported by
sdvormwa@cray.com
on 2013-03-01 18:37:54 -
Account Deleted Reported by
danbonachea
on 2013-03-01 18:52:10 - Labels added: Type-Enhancement - Labels removed: Type-Defect -
Account Deleted Reported by
danbonachea
on 2013-03-01 18:52:25 - Labels added: Consensus-Low - Labels removed: Consensus-Medium -
Account Deleted I just came across another use for stronger guarantees on upc_addrfield(), in particular that the return value be the same on all threads, while looking at a customer code. The code in question uses a lot of shared locks, acquired in pairs. As written, the code spins trying to acquire both locks with upc_lock_attempt() until both locks are acquired to avoid deadlock. If upc_addrfield() were guaranteed to return the same value on all threads, the code could instead grab locks with upc_lock() in increasing order of upc_addrfield() (using upc_threadof() to manage ties), achieving the same goal with (in most implementations) much better lock performance.
Reported by
sdvormwa@cray.com
on 2013-03-04 16:38:50 -
Account Deleted In response to Steve's comment #93 which suggests that ordering of locks might be another application of upc_addrfield(), I must point out 7.4.2.1 3: Two pointers to upc_lock_t that reference the same lock object will compare as equal. The results of applying upc_phaseof(), upc_threadof(), and upc_addrfield() to such pointers are undefined. Thus a non-trivial change to locks would be required to allow the application scenario described.
Reported by
phhargrove@lbl.gov
on 2013-03-04 18:05:04 -
Account Deleted I was merely pointing out another possible use case for a stronger upc_addrfield()--one which programmers are probably more familiar with if they've ever used locks in other programming models given that this is one of the most common methods of deadlock avoidance in the presence of multiple locks. If we wanted to actually support this use case, we'd obviously have to go through and fix anything else (such as the restrictions in 7.4.2.1 3) that would prevent it.
Reported by
sdvormwa@cray.com
on 2013-03-04 18:16:30 -
Account Deleted This comment is mostly about history. I do have one fairly minor suggestion to make. Sorry, this is another bit of tangent, but I think it worthwhile enough to bring up here. People were wondering about the history, so I did some digging. I had mentioned that upc_addrfield, initially referred to as "vadr" portion of a pointer-to-shared, as originally conceived, did have cross-thread consistency. One place to see this is expectation is in the 1999 paper by Carlson, Draper, et al, "Introduction to UPC and Language Specification". Another place to see this is in some of the existing tests in the GUTS suite from GWU. It was suggested in a comment that this particular implementation of upc_addrfield would be difficult in an SMP environment. It does take work, but it has been done that way in some implementations, including HP UPC. The HP UPC pointer-to-shared structure actually maintains as its "address" component the field that is reported by upc_addrfield, and does the appropriate offset calculations when converting it to a "real" address. But, more to the point, it doesn't have to be that way, either; it could maintain the "real" address, for example, and do the offset calculations only when returning upc_addrfield calls. Address randomization is also handled by a number of implementations while maintaining this consistency in the upc_addrfield function return value. HP UPC is a very old implementation. It existed before the specification (as Digital UPC, later Compaq UPC). It honors some understandings, such as this one, that never made it into the specification. I am NOT advocating that we introduce this consistency. That ship sailed long ago. There are too many implementations that do not provide it. It is not in the specification; it has NEVER been in the specification. I was going to suggest that the note regarding upc_addrfield being "used in the definition of pointer arithmetic" be dropped, but I see that there is some amount of constraint on the values returned by upc_addrfield that are listed under the pointer arithmetic section. I will, however, suggest that an explicit mention be made that upc_addrfield values across threads do not have any relation to each other. If something like the GUTS suite makes this assumption, there are potentially other code bases that do.
Reported by
brian.wibecan
on 2013-03-05 17:09:13 -
Account Deleted > I will, however, suggest that an explicit mention be made that upc_addrfield values across threads do not have any relation to each other. If something like the GUTS suite makes this assumption, there are potentially other code bases that do. Do you mean that for the same pointer, when passed to upc_addrfield(), the return value on different threads do not have any relation to each other? Or that pointers to elements of the same shared array object with affinity to different threads, when passed to upc_addrfield(), produce values with no relation to each other? I think the latter is desirable (for exactly the reasons you brought up in your post), but the former makes upc_addrfield() completely useless. Unfortunately, both are true for UPC 1.2 and the current UPC 1.3 draft, and I'm sure there are UPC codes that make invalid assumptions about one or both.
Reported by
sdvormwa@cray.com
on 2013-03-05 17:24:27 -
Account Deleted Sorry for not being clear. This one is what I meant: > pointers to elements of the same shared array object with affinity to > different threads, when passed to upc_addrfield(), produce values with > no relation to each other For example, given shared T Arr[THREADS]; upc_addrfield(&Arr[idx]) is not actually guaranteed to return the same result for all values of idx. I think this should be called out, rather than defined this way solely by omission. > the former makes upc_addrfield() completely useless I agree. I am surprised that even this basic expectation, the same result returned for the same argument when called by all threads, is not specified.
Reported by
brian.wibecan
on 2013-03-05 17:37:33 -
Account Deleted In the 3/15/13 telecon, we discussed this issue and decided that it should be deferred to 1.4, to allow further time for study into the desired semantics, usage cases and best way to deploy the new functionality.
Reported by
danbonachea
on 2013-03-16 01:16:36 - Log in to comment
Reported by
danbonachea
on 2013-02-27 11:32:11 - Labels added: Milestone-Spec-1.4