Re: [PATCH] Vector to scalar casting and widening

Ulrich Weigand
2013-01-08T19:05:40Z

Andrew Burgess wrote:

> I have a new patch which I believe should be closer to what we want,
> I'd be grateful if you could test the opencl parts of this again and
> let me know how it gets on. Also any other feedback is of course
> welcome too.

Thanks, this does look like what I had been thinking of.

Unfortunately, handling of binary operations would need to be quite a
bit more complex than this: in particular, there is no handling of the
usual arithmetic promotions, pointer arithmetic, and support for the
+= etc. variants of operators. Basically, all the special handling
done in eval.c:evaluate_subexp ...

However, when I looked at the OpenCL specs to verify what the precise
rules ought to be, I noticed that I had in fact misremembered the rule:
in fact, even in OpenCL, there is (now -- this seems to have been added
with the OpenCL 1.1 specs revision) a section on scalar to vector
promotion as part of arithmetic promotion, and this also prohibits
down-casting. The specific rules seem to be slightly different than
the C rules, but for the most part, the effect would be very similar.
[ So existing GDB behaviour in fact is just wrong. ]

Thus, I'd be fine with removing the OpenCL special cases and just let
it use the (modified) default case. It doesn't seem worthwhile to
exactly model the (minor) differences. (In particular, the existing
OpenCL GDB test cases all pass as-is.)

Sorry for the extra confusion; I really should have looked up the
precise rules earlier ...

Apart from that, I've had to make another set of minor changes:

- we need to handle UNOP_CAST_TYPE in addition to UNOP_CAST

- we also need to handle BINOP_ASSIGN similarly to UNOP_CAST,
since OpenCL also allows implicit scalar-to-vector widening
during assignment

With those changes, as in the modified version of the patch
below, all OpenCL test cases now pass. As far as OpenCL
is concerned, this would be OK to commit.

In fact *all* of the patch looks good to me now, but I'd ask
you wait for another couple of days in case one of the other
maintainers wants to comment ...

Thanks,
Ulrich

Index: gdb/opencl-lang.c
===================================================================
RCS file: /cvs/src/src/gdb/opencl-lang.c,v
retrieving revision 1.21
diff -u -p -r1.21 opencl-lang.c
--- gdb/opencl-lang.c 1 Jan 2013 06:32:47 -0000 1.21
+++ gdb/opencl-lang.c 8 Jan 2013 18:24:54 -0000
@@ -683,6 +683,58 @@ vector_relop (struct expression *exp, st
return ret;
}

+/* Perform a cast of ARG into TYPE. There's sadly a lot of duplication in
+ here from valops.c:value_cast, opencl is different only in the
+ behaviour of scalar to vector casting. As far as possibly we're going
+ to try and delegate back to the standard value_cast function. */
+
+static struct value *
+opencl_value_cast (struct type *type, struct value *arg)
+{
+ if (type != value_type (arg))
+ {
+ /* Casting scalar to vector is a special case for OpenCL, scalar
+ is cast to element type of vector then replicated into each
+ element of the vector. First though, we need to work out if
+ this is a scalar to vector cast; code lifted from
+ valops.c:value_cast. */
+ enum type_code code1, code2;
+ struct type *to_type;
+ int scalar;
+
+ to_type = check_typedef (type);
+
+ code1 = TYPE_CODE (to_type);
+ code2 = TYPE_CODE (check_typedef (value_type (arg)));
+
+ if (code2 == TYPE_CODE_REF)
+ code2 = TYPE_CODE (check_typedef (value_type (coerce_ref (arg))));
+
+ scalar = (code2 == TYPE_CODE_INT || code2 == TYPE_CODE_BOOL
+ || code2 == TYPE_CODE_CHAR || code2 == TYPE_CODE_FLT
+ || code2 == TYPE_CODE_DECFLOAT || code2 == TYPE_CODE_ENUM
+ || code2 == TYPE_CODE_RANGE);
+
+ if (code1 == TYPE_CODE_ARRAY && TYPE_VECTOR (to_type) && scalar)
+ {
+ struct type *eltype;
+
+ /* Cast to the element type of the vector here as
+ value_vector_widen will error if the scalar value is
+ truncated by the cast. To avoid the error, cast (and
+ possibly truncate) here. */
+ eltype = check_typedef (TYPE_TARGET_TYPE (to_type));
+ arg = value_cast (eltype, arg);
+
+ return value_vector_widen (arg, type);
+ }
+ else
+ /* Standard cast handler. */
+ arg = value_cast (type, arg);
+ }
+ return arg;
+}
+
/* Perform a relational operation on two operands. */

static struct value *
@@ -718,7 +770,7 @@ opencl_relop (struct expression *exp, st
if (TYPE_CODE (t) != TYPE_CODE_FLT && !is_integral_type (t))
error (_("Argument to operation not a number or boolean."));

- *v = value_cast (t1_is_vec ? type1 : type2, *v);
+ *v = opencl_value_cast (t1_is_vec ? type1 : type2, *v);
val = vector_relop (exp, arg1, arg2, op);
}

@@ -740,6 +792,46 @@ evaluate_subexp_opencl (struct type *exp

switch (op)
{
+ /* Handle assignment and cast operators to support OpenCL-style
+ scalar-to-vector widening. */
+ case BINOP_ASSIGN:
+ (*pos)++;
+ arg1 = evaluate_subexp (NULL_TYPE, exp, pos, noside);
+ type1 = value_type (arg1);
+ arg2 = evaluate_subexp (type1, exp, pos, noside);
+
+ if (noside == EVAL_SKIP || noside == EVAL_AVOID_SIDE_EFFECTS)
+ return arg1;
+
+ if (deprecated_value_modifiable (arg1)
+ && VALUE_LVAL (arg1) != lval_internalvar)
+ arg2 = opencl_value_cast (type1, arg2);
+
+ return value_assign (arg1, arg2);
+
+ case UNOP_CAST:
+ type1 = exp->elts[*pos + 1].type;
+ (*pos) += 2;
+ arg1 = evaluate_subexp (type1, exp, pos, noside);
+
+ if (noside == EVAL_SKIP)
+ return value_from_longest (builtin_type (exp->gdbarch)->
+ builtin_int, 1);
+
+ return opencl_value_cast (type1, arg1);
+
+ case UNOP_CAST_TYPE:
+ (*pos)++;
+ arg1 = evaluate_subexp (NULL, exp, pos, EVAL_AVOID_SIDE_EFFECTS);
+ type1 = value_type (arg1);
+ arg1 = evaluate_subexp (type1, exp, pos, noside);
+
+ if (noside == EVAL_SKIP)
+ return value_from_longest (builtin_type (exp->gdbarch)->
+ builtin_int, 1);
+
+ return opencl_value_cast (type1, arg1);
+
/* Handle binary relational and equality operators that are either not
or differently defined for GNU vectors. */
case BINOP_EQUAL:
@@ -852,12 +944,12 @@ evaluate_subexp_opencl (struct type *exp
/* Widen the scalar operand to a vector if necessary. */
if (t2_is_vec || !t3_is_vec)
{
- arg3 = value_cast (type2, arg3);
+ arg3 = opencl_value_cast (type2, arg3);
type3 = value_type (arg3);
}
else if (!t2_is_vec || t3_is_vec)
{
- arg2 = value_cast (type3, arg2);
+ arg2 = opencl_value_cast (type3, arg2);
type2 = value_type (arg2);
}
else if (!t2_is_vec || !t3_is_vec)
Index: gdb/valarith.c
===================================================================
RCS file: /cvs/src/src/gdb/valarith.c,v
retrieving revision 1.107
diff -u -p -r1.107 valarith.c
--- gdb/valarith.c 1 Jan 2013 06:41:29 -0000 1.107
+++ gdb/valarith.c 8 Jan 2013 18:24:55 -0000
@@ -1346,6 +1346,49 @@ scalar_binop (struct value *arg1, struct
return val;
}

+/* Widen a scalar value SCALAR_VALUE to vector type VECTOR_TYPE by
+ replicating SCALAR_VALUE for each element of the vector. Only scalar
+ types that can be cast to the type of one element of the vector are
+ acceptable. The newly created vector value is returned upon success,
+ otherwise an error is thrown. */
+
+struct value *
+value_vector_widen (struct value *scalar_value, struct type *vector_type)
+{
+ /* Widen the scalar to a vector. */
+ struct type *eltype, *scalar_type;
+ struct value *val, *elval;
+ LONGEST low_bound, high_bound;
+ int i;
+
+ CHECK_TYPEDEF (vector_type);
+
+ gdb_assert (TYPE_CODE (vector_type) == TYPE_CODE_ARRAY
+ && TYPE_VECTOR (vector_type));
+
+ if (!get_array_bounds (vector_type, &low_bound, &high_bound))
+ error (_("Could not determine the vector bounds"));
+
+ eltype = check_typedef (TYPE_TARGET_TYPE (vector_type));
+ elval = value_cast (eltype, scalar_value);
+
+ scalar_type = check_typedef (value_type (scalar_value));
+
+ /* If we reduced the length of the scalar then check we didn't loose any
+ important bits. */
+ if (TYPE_LENGTH (eltype) < TYPE_LENGTH (scalar_type)
+ && !value_equal (elval, scalar_value))
+ error (_("conversion of scalar to vector involves truncation"));
+
+ val = allocate_value (vector_type);
+ for (i = 0; i < high_bound - low_bound + 1; i++)
+ /* Duplicate the contents of elval into the destination vector. */
+ memcpy (value_contents_writeable (val) + (i * TYPE_LENGTH (eltype)),
+ value_contents_all (elval), TYPE_LENGTH (eltype));
+
+ return val;
+}
+
/* Performs a binary operation on two vector operands by calling scalar_binop
for each pair of vector components. */

@@ -1425,7 +1468,9 @@ value_binop (struct value *arg1, struct
&& !is_integral_type (t))
error (_("Argument to operation not a number or boolean."));

- *v = value_cast (t1_is_vec ? type1 : type2, *v);
+ /* Replicate the scalar value to make a vector value. */
+ *v = value_vector_widen (*v, t1_is_vec ? type1 : type2);
+
val = vector_binop (arg1, arg2, op);
}

Index: gdb/valops.c
===================================================================
RCS file: /cvs/src/src/gdb/valops.c,v
retrieving revision 1.308
diff -u -p -r1.308 valops.c
--- gdb/valops.c 1 Jan 2013 06:41:29 -0000 1.308
+++ gdb/valops.c 8 Jan 2013 18:24:55 -0000
@@ -546,29 +546,13 @@ value_cast (struct type *type, struct va
minus one, instead of biasing the normal case. */
return value_from_longest (type, -1);
}
- else if (code1 == TYPE_CODE_ARRAY && TYPE_VECTOR (type) && scalar)
- {
- /* Widen the scalar to a vector. */
- struct type *eltype;
- struct value *val;
- LONGEST low_bound, high_bound;
- int i;
-
- if (!get_array_bounds (type, &low_bound, &high_bound))
- error (_("Could not determine the vector bounds"));
-
- eltype = check_typedef (TYPE_TARGET_TYPE (type));
- arg2 = value_cast (eltype, arg2);
- val = allocate_value (type);
-
- for (i = 0; i < high_bound - low_bound + 1; i++)
- {
- /* Duplicate the contents of arg2 into the destination vector. */
- memcpy (value_contents_writeable (val) + (i * TYPE_LENGTH (eltype)),
- value_contents_all (arg2), TYPE_LENGTH (eltype));
- }
- return val;
- }
+ else if (code1 == TYPE_CODE_ARRAY && TYPE_VECTOR (type)
+ && code2 == TYPE_CODE_ARRAY && TYPE_VECTOR (type2)
+ && TYPE_LENGTH (type) != TYPE_LENGTH (type2))
+ error (_("Cannot convert between vector values of different sizes"));
+ else if (code1 == TYPE_CODE_ARRAY && TYPE_VECTOR (type) && scalar
+ && TYPE_LENGTH (type) != TYPE_LENGTH (type2))
+ error (_("can only cast scalar to vector of same size"));
else if (code1 == TYPE_CODE_VOID)
{
return value_zero (type, not_lval);
Index: gdb/value.h
===================================================================
RCS file: /cvs/src/src/gdb/value.h,v
retrieving revision 1.213
diff -u -p -r1.213 value.h
--- gdb/value.h 1 Jan 2013 06:41:29 -0000 1.213
+++ gdb/value.h 8 Jan 2013 18:24:55 -0000
@@ -482,6 +482,12 @@ extern void read_value_memory (struct va
int stack, CORE_ADDR memaddr,
gdb_byte *buffer, size_t length);

+/* Cast SCALAR_VALUE to the element type of VECTOR_TYPE, then replicate
+ into each element of a new vector value with VECTOR_TYPE. */
+
+struct value *value_vector_widen (struct value *scalar_value,
+ struct type *vector_type);
+
\f

#include "symtab.h"
Index: gdb/testsuite/gdb.base/gnu_vector.c
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.base/gnu_vector.c,v
retrieving revision 1.7
diff -u -p -r1.7 gnu_vector.c
--- gdb/testsuite/gdb.base/gnu_vector.c 1 Jan 2013 06:33:25 -0000 1.7
+++ gdb/testsuite/gdb.base/gnu_vector.c 8 Jan 2013 18:24:56 -0000
@@ -31,6 +31,7 @@ int ia = 2;
int ib = 1;
float fa = 2;
float fb = 1;
+long long lla __attribute__ ((mode(DI))) = 0x0000000100000001ll;
char4 c4 = {1, 2, 3, 4};
int4 i4a = {2, 4, 8, 16};
int4 i4b = {1, 2, 8, 4};
Index: gdb/testsuite/gdb.base/gnu_vector.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.base/gnu_vector.exp,v
retrieving revision 1.11
diff -u -p -r1.11 gnu_vector.exp
--- gdb/testsuite/gdb.base/gnu_vector.exp 1 Jan 2013 06:33:25 -0000 1.11
+++ gdb/testsuite/gdb.base/gnu_vector.exp 8 Jan 2013 18:24:56 -0000
@@ -82,32 +82,52 @@ gdb_test "print f4a / f4b" "\\\$$decimal
gdb_test "print +f4a" "\\\$$decimal = \\{2, 4, 8, 16\\}"
gdb_test "print -f4a" "\\\$$decimal = \\{-2, -4, -8, -16\\}"

-# Test scalar to vector widening
-gdb_test "print (int2) 1" "\\\$$decimal = \\{1, 1\\}"
-gdb_test "print (longlong2) 2" "\\\$$decimal = \\{2, 2\\}"
-gdb_test "print (float2) 3" "\\\$$decimal = \\{3, 3\\}"
-gdb_test "print (double2) 4" "\\\$$decimal = \\{4, 4\\}"
-gdb_test "print (char4) 12" "\\\$$decimal = \\{12, 12, 12, 12\\}"
-gdb_test "print (uint4) ia" "\\\$$decimal = \\{2, 2, 2, 2\\}"
-gdb_test "print (int4) -3" "\\\$$decimal = \\{-3, -3, -3, -3\\}"
-gdb_test "print (float4) 4" "\\\$$decimal = \\{4, 4, 4, 4\\}"
+# When casting to vector the input type must have the same length as
+# the total length of the vector.
+gdb_test "print (char4) 0x01010101" "\\\$$decimal = \\{1, 1, 1, 1\\}"
+gdb_test "print (char4) ia" "\\\$$decimal = \\{2, 0, 0, 0\\}"
+gdb_test "print (int2) lla" "\\\$$decimal = \\{1, 1\\}"
+
+gdb_test "print (int2) 1" "can only cast scalar to vector of same size"
+gdb_test "print (longlong2) 2" "can only cast scalar to vector of same size"
+gdb_test "print (float2) 3" "can only cast scalar to vector of same size"
+gdb_test "print (double2) 4" "can only cast scalar to vector of same size"
+gdb_test "print (uint4) ia" "can only cast scalar to vector of same size"
+gdb_test "print (int4) -3" "can only cast scalar to vector of same size"
+gdb_test "print (float4) 4" "can only cast scalar to vector of same size"
+
+gdb_test "print i4b = ia" "can only cast scalar to vector of same size"
+gdb_test "print i4a = 3" "can only cast scalar to vector of same size"
+gdb_test "print f4a = fb" "can only cast scalar to vector of same size"
+gdb_test "print f4b = 2" "can only cast scalar to vector of same size"
+
+gdb_test "print c4 + lla" "conversion of scalar to vector involves truncation"
+gdb_test "print i4a + lla" "conversion of scalar to vector involves truncation"
+gdb_test "print lla + c4" "conversion of scalar to vector involves truncation"
+gdb_test "print lla + i4a" "conversion of scalar to vector involves truncation"

+gdb_test "print c4 + ib" "\\\$$decimal = \\{2, 3, 4, 5\\}"
gdb_test "print i4a + ib" "\\\$$decimal = \\{3, 5, 9, 17\\}"
+gdb_test "print i4a + 1" "\\\$$decimal = \\{3, 5, 9, 17\\}"
+gdb_test "print 1 + i4a" "\\\$$decimal = \\{3, 5, 9, 17\\}"
gdb_test "print fa - f4b" "\\\$$decimal = \\{1, 0, -6, -2\\}"
+gdb_test "print 2 - f4b" "\\\$$decimal = \\{1, 0, -6, -2\\}"
gdb_test "print f4a * fb" "\\\$$decimal = \\{2, 4, 8, 16\\}"
+gdb_test "print f4a * 1" "\\\$$decimal = \\{2, 4, 8, 16\\}"
gdb_test "print ia / i4b" "\\\$$decimal = \\{2, 1, 0, 0\\}"
+gdb_test "print 2 / i4b" "\\\$$decimal = \\{2, 1, 0, 0\\}"
gdb_test "print i4a % ib" "\\\$$decimal = \\{0, 0, 0, 0\\}"
-
+gdb_test "print i4a % 1" "\\\$$decimal = \\{0, 0, 0, 0\\}"
gdb_test "print ia & i4b" "\\\$$decimal = \\{0, 2, 0, 0\\}"
+gdb_test "print 2 & i4b" "\\\$$decimal = \\{0, 2, 0, 0\\}"
gdb_test "print i4a | ib" "\\\$$decimal = \\{3, 5, 9, 17\\}"
+gdb_test "print i4a | 1" "\\\$$decimal = \\{3, 5, 9, 17\\}"
gdb_test "print ia ^ i4b" "\\\$$decimal = \\{3, 0, 10, 6\\}"
+gdb_test "print 2 ^ i4b" "\\\$$decimal = \\{3, 0, 10, 6\\}"
gdb_test "print i4a << ib" "\\\$$decimal = \\{4, 8, 16, 32\\}"
+gdb_test "print i4a << 1" "\\\$$decimal = \\{4, 8, 16, 32\\}"
gdb_test "print i4a >> ib" "\\\$$decimal = \\{1, 2, 4, 8\\}"
-
-gdb_test "print i4b = ia" "\\\$$decimal = \\{2, 2, 2, 2\\}"
-gdb_test "print i4a = 3" "\\\$$decimal = \\{3, 3, 3, 3\\}"
-gdb_test "print f4a = fb" "\\\$$decimal = \\{1, 1, 1, 1\\}"
-gdb_test "print f4b = 2" "\\\$$decimal = \\{2, 2, 2, 2\\}"
+gdb_test "print i4a >> 1" "\\\$$decimal = \\{1, 2, 4, 8\\}"

gdb_test "print i4a = \{2, 4, 8, 16\}" "\\\$$decimal = \\{2, 4, 8, 16\\}"
gdb_test "print i4a <<= ib" "\\\$$decimal = \\{4, 8, 16, 32\\}"
@@ -130,6 +150,10 @@ gdb_test "print i2 + i4a" "Cannot perfor
gdb_test "print f4a + f2" "Cannot perform operation on vectors with different types"
gdb_test "print f2 + f4a" "Cannot perform operation on vectors with different types"

+gdb_test "print (double2) f2" "Cannot convert between vector values of different sizes"
+gdb_test "print (int4) c4" "Cannot convert between vector values of different sizes"
+gdb_test "print (char4) i4a" "Cannot convert between vector values of different sizes"
+
# Test ptype on vector types.
gdb_test "ptype c4" "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
gdb_test "ptype char4" "type = char __attribute__ \\(\\(vector_size\\(4\\)\\)\\)"
Index: gdb/testsuite/gdb.python/py-type.c
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.c,v
retrieving revision 1.8
diff -u -p -r1.8 py-type.c
--- gdb/testsuite/gdb.python/py-type.c 1 Jan 2013 06:41:26 -0000 1.8
+++ gdb/testsuite/gdb.python/py-type.c 8 Jan 2013 18:24:56 -0000
@@ -50,6 +50,9 @@ enum E
{ v1, v2, v3
};

+struct s vec_data_1 = {1, 1};
+struct s vec_data_2 = {1, 2};
+
int
main ()
{
Index: gdb/testsuite/gdb.python/py-type.exp
===================================================================
RCS file: /cvs/src/src/gdb/testsuite/gdb.python/py-type.exp,v
retrieving revision 1.28
diff -u -p -r1.28 py-type.exp
--- gdb/testsuite/gdb.python/py-type.exp 1 Jan 2013 06:41:26 -0000 1.28
+++ gdb/testsuite/gdb.python/py-type.exp 8 Jan 2013 18:24:56 -0000
@@ -118,7 +118,7 @@ proc test_fields {lang} {
# Test gdb.Type.array.
gdb_test "python print (ar\[0\].cast(ar\[0\].type.array(1)))" \
".1, 2." "cast to array with one argument"
- gdb_test "python print (ar\[0\].cast(ar\[0\].type.array(0, 1)))" \
+ gdb_test "python print ar\[0\].cast(ar\[0\].type.array(0, 1))" \
".1, 2." "cast to array with two arguments"

gdb_test "python print (ar\[0\].type == ar\[0\].type)" "True"
@@ -126,16 +126,19 @@ proc test_fields {lang} {
# Test gdb.Type.vector.
# Note: vectors cast differently than arrays. Here ar[0] is replicated
# for the size of the vector.
- gdb_py_test_silent_cmd \
- "python vec1 = ar\[0\].cast(ar\[0\].type.vector(1))" "set vec1" 1
- gdb_test "python print (vec1)" ".1, 1." "cast to vector with one argument"
- gdb_py_test_silent_cmd \
- "python vec2 = ar\[0\].cast(ar\[0\].type.vector(0, 1))" "set vec2" 1
- gdb_test "python print (vec2)" ".1, 1." "cast to vector with two arguments"
- gdb_test "python print (vec1 == vec2)" "True"
- gdb_py_test_silent_cmd \
- "python vec3 = ar\[1\].cast(ar\[1\].type.vector(1))" "set vec3" 1
- gdb_test "python print (vec1 == vec3)" "False"
+ gdb_py_test_silent_cmd "print vec_data_1" "print value (vec_data_1)" 1
+ gdb_py_test_silent_cmd "python vec_data_1 = gdb.history (0)" "get value (vec_data_1) from history" 1
+
+ gdb_py_test_silent_cmd "print vec_data_2" "print value (vec_data_2)" 1
+ gdb_py_test_silent_cmd "python vec_data_2 = gdb.history (0)" "get value (vec_data_2) from history" 1
+
+ gdb_py_test_silent_cmd "python vec1 = vec_data_1.cast(ar\[0\].type.vector(1))" "set vec1" 1
+ gdb_test "python print vec1" ".1, 1." "cast to vector with one argument"
+ gdb_py_test_silent_cmd "python vec2 = vec_data_1.cast(ar\[0\].type.vector(0, 1))" "set vec2" 1
+ gdb_test "python print vec2" ".1, 1." "cast to vector with two arguments"
+ gdb_test "python print vec1 == vec2" "True"
+ gdb_py_test_silent_cmd "python vec3 = vec_data_2.cast(ar\[0\].type.vector(1))" "set vec3" 1
+ gdb_test "python print vec1 == vec3" "False"
}
}

--
Dr. Ulrich Weigand
GNU Toolchain for Linux on System z and Cell BE
Ulrich.Weigand@de.ibm.com