Open StuartDBrady opened 4 years ago
Hi Stuart,
I think the semantics above are reasonably well-specified in the latest OpenCL C specs. In particular, for the logical operators and vector types:
https://www.khronos.org/registry/OpenCL/specs/2.2/html/OpenCL_C.html#operators-logical
For scalar types, the logical operators shall return 0 if the result of the operation is false and 1 if the result is true. For vector types, the logical operators shall return 0 if the result of the operation is false and -1 (i.e. all bits set) if the result is true.
For casts from bool
to integer vector components:
https://www.khronos.org/registry/OpenCL/specs/2.2/html/OpenCL_C.html#built-in-scalar-data-types
bool
: A conditional data type which is either true or false. The value true expands to the integer constant 1 and the value false expands to the integer constant 0.
These descriptions sound consistent with the version of Clang you are using.
Do you have suggestions to improve these descriptions further? Thanks!
Hi Stuart,
I think the semantics above are reasonably well-specified in the latest OpenCL C specs. In particular, for the logical operators and vector types:
https://www.khronos.org/registry/OpenCL/specs/2.2/html/OpenCL_C.html#operators-logical
For scalar types, the logical operators shall return 0 if the result of the operation is false and 1 if the result is true. For vector types, the logical operators shall return 0 if the result of the operation is false and -1 (i.e. all bits set) if the result is true.
Hi Ben,
While it's true that the result for each component must be -1 or 0, the specification does not state how integer vector components are consumed. The conformance tests seem to expect the same semantics as for scalars (i.e. all non-zero values are considered true). If we agree, then I could submit something to clarify this. However, it seems possible to me that the conformance tests might be too strict. (I.e. would there be benefit to allowing only the most significant bit of each integer vector component to be tested, or would this add needless complication?)
For casts from
bool
to integer vector components:https://www.khronos.org/registry/OpenCL/specs/2.2/html/OpenCL_C.html#built-in-scalar-data-types
bool
: A conditional data type which is either true or false. The value true expands to the integer constant 1 and the value false expands to the integer constant 0.These descriptions sound consistent with the version of Clang you are using.
Agreed. However, in 6.2.2. Explicit Casts, we have:
When casting a
bool
to a vector integer data type, the vector components will be set to -1 (i.e. all bits set) if the bool value is true and 0 otherwise.
It seems reasonable to expect similar semantics when using bool
s within vector literals, i.e. one might expect (int2)((bool)true, (bool)true)
to be equivalent to (int2)(bool)true
, but this is inconsistent with what I see from Clang.
Note: the explicit cast to bool
in my examples above might be unnecessary. I have found that in Clang, (int2)true
and (int2)(bool)true
are equivalent. However, the specification only goes as far as stating that true
expands to 1 and false
expands to 0. It does not state whether the integer constants that true
and false
expand to are actually of type bool
(which I presume is the intent), and whether bool
is therefore an integer type (i.e. can I increment false
by 1 and expect to get true
)?
Do you have suggestions to improve these descriptions further? Thanks!
I would be happy to submit improvements, but first I need a clear view of the intended, desired and relied-upon semantics.
the specification does not state how integer vector components are consumed.
Ah, I think I get it now. If we have:
int2 a = ...;
int2 b = ...;
int2 c = a || b;
The question is less about what c
contains and more about how a
and b
are interpreted to compute c
. One specific question is whether a component of a
is considered true
if it is non-zero, true
if the MSB is set, or true
based on some other condition. Is that correct?
If so, I've always assumed that a component of a
is considered true
if it is non-zero, and I think this is somewhat implied by the statement that (emphasis mine) "For built-in vector types, both operands are evaluated and the operators are applied component-wise", but perhaps this could be clarified further.
Regarding casting bool
to a vector of integers, I agree that it is somewhat non-intuitive that (int2)((bool)true, (bool)true)
is not equivalent to (int2)(bool)true
, but I think this is well-specified.
I'm guessing the bool casting semantics are driven by the select()
built-in function, since otherwise the behavior of select(a, b, true)
would be VERY non-intuitive for vector values of a
and b
.
Ah, I think I get it now. If we have:
int2 a = ...; int2 b = ...; int2 c = a || b;
The question is less about what
c
contains and more about howa
andb
are interpreted to computec
. One specific question is whether a component ofa
is consideredtrue
if it is non-zero,true
if the MSB is set, ortrue
based on some other condition. Is that correct?
Yeah, exactly! This seems as though it ought to be straightforward to specify, even if the behavior is explicitly made undefined.
If so, I've always assumed that a component of
a
is consideredtrue
if it is non-zero, and I think this is somewhat implied by the statement that (emphasis mine) "For built-in vector types, both operands are evaluated and the operators are applied component-wise", but perhaps this could be clarified further.
Perhaps, but even if vectors components were considered true(*) based on some other criteria, the statement that "for built-in vector types, both operands are evaluated and the operators are applied component-wise" would be equally valid. The question here is whether "the operators" in this sentence refers to the exact same operators as are applied for scalar types.
Given that the ?
operator works differently for vectors and that casts from scalar bool
works differently, there doesn't seem to be an especially strong implication in the specification that the scalar and vector operators have the same semantics. I.e. the implication that is there seems weak enough to that there could be a risk of specification being interpreted alternatively. I would agree, though, that it does seem to suggest that the original intent was for the semantics to be the same.
If we change this to "the operators are applied component-wise with the same semantics as for scalar types" or some equivalent wording, that would be sufficient, in my view. However, I'd welcome alternative suggestions if there are any differing views on this.
(* Note: I'm avoiding formatting this as true
here as I'm referring to the internal notion of truth as it applies within the &&
and ||
operators, considered independently of the true
and false
literals.)
Regarding casting
bool
to a vector of integers, I agree that it is somewhat non-intuitive that(int2)((bool)true, (bool)true)
is not equivalent to(int2)(bool)true
, but I think this is well-specified.I'm guessing the bool casting semantics are driven by the
select()
built-in function, since otherwise the behavior ofselect(a, b, true)
would be VERY non-intuitive for vector values ofa
andb
.
I see: the statement that "a vector literal operates as an overloaded function" is sufficient to indicate that the usual implicit type conversion rules apply to the function parameters (i.e. the vector elements). I agree, then, that it is well-specified, albeit somewhat unfortunately so. Thank you for the correction. I've updated the title and description of this issue accordingly.
The OpenCL C specification does not define the semantics for the consumption of integer vector components as logic values for the operands of the binary logical operators (i.e.
&&
and||
). I.e. although it is not stated, it must be the case that((int2)(-1, -1) || (int2)(x, y)) == (int2)(-1, -1)
and that((int2)(0, 0) && (int2)(x, y)) == (int2)(0, 0)
, but it is not clear how the binary logical operators are defined for integer vector component values other than 0 and -1. (The version of Clang that I have tested constant-folds((int2)(1, 1) || (int2)(x, y))
into(int2)(-1, -1)
.)The specification also does not explicitly define the semantics of implicit casts frombool
for the integer vector components of vector literals. I.e. is(int2)((bool)true, (bool)true)
equivalent to(int2)(1, 1)
or(int2)(-1, -1)
? (The version of Clang that I have tested uses(int2)(1, 1)
.) [Note: this is incorrect: the semantics are well-defined.]It would be good to clarify these semantics, if possible.
Note: the semantics for the representation of logic values as integer vector components for results are clearly defined for all operators including casts from
bool
scalars to the integer vector types, and for the relational built-in functions. The semantics for the consumption of integer vector components as logic values are clearly defined for the unary logical (negation) operator (i.e.!
), theselect()
built-in function, the ternary selection operator (?
), and theany()
andall()
built-in functions.The OpenCL C conformance test suite has code in the
integer_ops
test to check that the binary and unary logical operators determine truth for integer vector components by comparing against 0. (Somewhat confusingly, the test for!
has been implemented as a binary operator test.)