Re: [PATCH] Vector to scalar casting and widening

Andrew Burgess
2012-11-28T22:08:22Z

On 26/11/2012 2:18 PM, Ulrich Weigand wrote:
> Andrew Burgess wrote:
>> On 19/11/2012 9:16 PM, Tom Tromey wrote:
>>>>>>>> "Andrew" == Andrew Burgess <aburgess@broadcom.com> writes:
>>>
>>> Andrew> I'd like to change the way gdb handles scalar to vector
>>> Andrew> widening. I believe that the changes I propose will bring gdb
>>> Andrew> expression evaluation into line with how gcc handles these
>>> Andrew> things; this seems a good thing to me, but I'd be interested to
>>> Andrew> hear why anyone things we should stick with the current scheme.
>
> This difference between OpenCL semantics and GCC is a bit annoying;
> for the most part, GCC vector extensions were chosen to match (or
> at least extend) OpenCL semantics. But in the particular case of
> explicit conversion between integer and vector types it seems
> the behaviour of the GCC extension dates way back to 2000 or so,
> long before we even had OpenCL ...
>
> Given that, I agree that we ought to match GCC's behaviour when
> targeting non-OpenCL languages.
>
> However, I'm not sure I like the new language_defn flag. Generally, the
> OpenCL code has kept all expression evaluation that differs from the
> default behaviour local in evaluate_subexp_opencl; see e.g. the treatment
> of relational operations.
>
> Thus, I'd prefer a patch that:
> - changes the default behaviour of value_cast etc. to the GCC behaviour, and
> - adds handling of UNOP_CAST etc. to evaluate_subexp_opencl to implement the
> OpenCL semantics.

The latest patch below is is closer to the initial patch I posted, the
behaviour of value_cast now follows GCC, and the UNOP_CAST case is handled
in evaluate_subexp_opencl. I wasn't sure what the "etc" in the above point
referred to, I believe UNOP_CAST is all I need to catch, let me know if I've
missed anything.

>
> Unfortunately the only environment I know of to test the GDB OpenCL support
> is the IBM OpenCL SDK for PowerPC. I'll be happy to run the test for you ...

I've built and tested on x86-64 GNU/Linux, but I've not run any OpenCL tests. If
you were able to give this patch a go and let me know if I've broken any of the
OpenCL tests I'd be very grateful.

Other than passing the OpenCL tests, is there any other feedback on this patch? As
this version of the patch is so similar to the first version I posted, and Tom
seemed happy with that (I've made the suggested changes), the only part that really
needs review is the change to opencl-lang.c.

Thanks,

Andrew

gdb/ChangeLog

2012-11-27 Andrew Burgess <aburgess@broadcom.com>

Add language specific flag to control casting from scalar to
vector types. Allow scalar to vector widening during binary
operations on a scalar and a vector.
* valarith.c (value_vector_widen): New function for replicating a
scalar into a vector.
(value_binop): Use new function to convert scalar to vector rather
than value_cast.
* value.h (value_vector_widen): Declare new function.
* valops.c (value_casst): Update logic for casting between vector
types, and for casting from scalar to vector.
* opencl-lang.c (evaluate_subexp_opencl): Handle UNOP_CAST to deal
with OpenCL scalar to vector casting.

diff --git a/gdb/opencl-lang.c b/gdb/opencl-lang.c
index b8fd9b7..0a529fc 100644
--- a/gdb/opencl-lang.c
+++ b/gdb/opencl-lang.c
@@ -769,6 +769,56 @@ evaluate_subexp_opencl (struct type *expect_type, struct expression *exp,

return opencl_logical_not (exp, arg1);

+ 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);
+ if (type1 != value_type (arg1))
+ {
+ /* 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 (type1);
+
+ code1 = TYPE_CODE (to_type);
+ code2 = TYPE_CODE (check_typedef (value_type (arg2)));
+
+ if (code2 == TYPE_CODE_REF)
+ code2 = TYPE_CODE (check_typedef (value_type (coerce_ref (arg2))));
+
+ 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));
+ arg2 = value_cast (eltype, arg2);
+
+ return value_vector_widen (arg2, type1);
+ }
+ else
+ /* Standard cast handler. */
+ arg1 = value_cast (type1, arg1);
+ }
+ return arg1;
+
/* Handle the logical operator and(&&) and or(||). */
case BINOP_LOGICAL_AND:
case BINOP_LOGICAL_OR:
diff --git a/gdb/valarith.c b/gdb/valarith.c
index c457f4a..afb45e2 100644
--- a/gdb/valarith.c
+++ b/gdb/valarith.c
@@ -1347,6 +1347,49 @@ scalar_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
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. */

@@ -1426,7 +1469,9 @@ value_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
&& !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);
}

diff --git a/gdb/valops.c b/gdb/valops.c
index 502fb0d..8f7f9ad 100644
--- a/gdb/valops.c
+++ b/gdb/valops.c
@@ -546,29 +546,13 @@ value_cast (struct type *type, struct value *arg2)
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 (TYPE_LENGTH (type) == TYPE_LENGTH (type2))
{
if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR)
diff --git a/gdb/value.h b/gdb/value.h
index 3685935..f8438dc 100644
--- a/gdb/value.h
+++ b/gdb/value.h
@@ -482,6 +482,12 @@ extern void read_value_memory (struct value *val, int embedded_offset,
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"

gdb/testsuite/ChangeLog

2012-11-27 Andrew Burgess <aburgess@broadcom.com>

* gdb.base/gnu_vector.c: New variable for use in tests.
* gdb.base/gnu_vector.exp: Update and extend tests to reflect
changes in scalar to vector casting and widening.
* gdb.python/py-type.c: New variables for use in tests.
* gdb.python/py-type.exp: Update vector related tests to reflect
changes in scalar to vector casting and widening.

diff --git a/gdb/testsuite/gdb.base/gnu_vector.c b/gdb/testsuite/gdb.base/gnu_vector.c
index a2a218f..07bc529 100644
--- a/gdb/testsuite/gdb.base/gnu_vector.c
+++ b/gdb/testsuite/gdb.base/gnu_vector.c
@@ -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};
diff --git a/gdb/testsuite/gdb.base/gnu_vector.exp b/gdb/testsuite/gdb.base/gnu_vector.exp
index baba119..b6a1afb 100644
--- a/gdb/testsuite/gdb.base/gnu_vector.exp
+++ b/gdb/testsuite/gdb.base/gnu_vector.exp
@@ -82,32 +82,52 @@ gdb_test "print f4a / f4b" "\\\$$decimal = \\{2, 2, 1, 4\\}"
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 perform operation on vectors with different ty
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\\)\\)\\)"
diff --git a/gdb/testsuite/gdb.python/py-type.c b/gdb/testsuite/gdb.python/py-type.c
index bf39443..3412078 100644
--- a/gdb/testsuite/gdb.python/py-type.c
+++ b/gdb/testsuite/gdb.python/py-type.c
@@ -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 ()
{
diff --git a/gdb/testsuite/gdb.python/py-type.exp b/gdb/testsuite/gdb.python/py-type.exp
index 5009135..f9cc90a 100644
--- a/gdb/testsuite/gdb.python/py-type.exp
+++ b/gdb/testsuite/gdb.python/py-type.exp
@@ -126,15 +126,18 @@ 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_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 = ar\[0\].cast(ar\[0\].type.vector(0, 1))" "set vec2" 1
+ 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 = ar\[1\].cast(ar\[1\].type.vector(1))" "set vec3" 1
+ 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"
}
}