Implement complex arithmetic

This adds support for complex arithmetic to gdb.  Now something like
"print 23 + 7i" will work.

Addition, subtraction, multiplication, division, and equality testing
are supported binary operations.

Unary +, negation, and complement are supported.  Following GCC, the ~
operator computes the complex conjugate.

gdb/ChangeLog
2020-04-01  Tom Tromey  <tom@tromey.com>

	PR exp/25299:
	* valarith.c (promotion_type, complex_binop): New functions.
	(scalar_binop): Handle complex numbers.  Use promotion_type.
	(value_pos, value_neg, value_complement): Handle complex numbers.

gdb/testsuite/ChangeLog
2020-04-01  Tom Tromey  <tom@tromey.com>

	* gdb.base/complex-parts.exp: Add arithmetic tests.
This commit is contained in:
Tom Tromey 2020-04-01 14:09:52 -06:00 committed by Tom Tromey
parent fa649bb7d3
commit c34e871466
4 changed files with 215 additions and 21 deletions

View file

@ -1,3 +1,10 @@
2020-04-01 Tom Tromey <tom@tromey.com>
PR exp/25299:
* valarith.c (promotion_type, complex_binop): New functions.
(scalar_binop): Handle complex numbers. Use promotion_type.
(value_pos, value_neg, value_complement): Handle complex numbers.
2020-04-01 Tom Tromey <tom@tromey.com> 2020-04-01 Tom Tromey <tom@tromey.com>
* c-exp.y (COMPLEX_INT, COMPLEX_FLOAT): New tokens. * c-exp.y (COMPLEX_INT, COMPLEX_FLOAT): New tokens.

View file

@ -1,3 +1,7 @@
2020-04-01 Tom Tromey <tom@tromey.com>
* gdb.base/complex-parts.exp: Add arithmetic tests.
2020-04-01 Tom Tromey <tom@tromey.com> 2020-04-01 Tom Tromey <tom@tromey.com>
* gdb.compile/compile.exp: Update. * gdb.compile/compile.exp: Update.

View file

@ -60,3 +60,29 @@ gdb_test "p \$_cimag (i1)" "expected a complex number"
gdb_test "p \$_creal (d1)" "expected a complex number" gdb_test "p \$_creal (d1)" "expected a complex number"
gdb_test "p \$_creal (f1)" "expected a complex number" gdb_test "p \$_creal (f1)" "expected a complex number"
gdb_test "p \$_creal (i1)" "expected a complex number" gdb_test "p \$_creal (i1)" "expected a complex number"
#
# General complex number tests.
#
gdb_test "print 23 + 7i" " = 23 \\+ 7i"
gdb_test "print 23.125f + 7i" " = 23.125 \\+ 7i"
gdb_test "print 23 + 7.25fi" " = 23 \\+ 7.25i"
gdb_test "print (23 + 7i) + (17 + 10i)" " = 40 \\+ 17i"
gdb_test "print 23 + -7i" " = 23 \\+ -7i"
gdb_test "print 23 - 7i" " = 23 \\+ -7i"
gdb_test "print -(23 + 7i)" " = -23 \\+ -7i"
gdb_test "print +(23 + 7i)" " = 23 \\+ 7i"
gdb_test "print ~(23 + 7i)" " = 23 \\+ -7i"
gdb_test "print (5 + 5i) * (2 + 2i)" " = 0 \\+ 20i"
gdb_test "print (5 + 7i) == (5 + 7i)" " = 1"
gdb_test "print (5 + 7i) == (8 + 7i)" " = 0"
gdb_test "print (5 + 7i) == (5 + 92i)" " = 0"
gdb_test "print (5 + 7i) != (5 + 7i)" " = 0"
gdb_test "print (5 + 7i) != (8 + 7i)" " = 1"
gdb_test "print (5 + 7i) != (5 + 92i)" " = 1"
gdb_test "print (20 - 4i) / (3 + 2i)" " = 4 \\+ -4i"

View file

@ -911,6 +911,157 @@ value_args_as_target_float (struct value *arg1, struct value *arg2,
TYPE_NAME (type2)); TYPE_NAME (type2));
} }
/* A helper function that finds the type to use for a binary operation
involving TYPE1 and TYPE2. */
static struct type *
promotion_type (struct type *type1, struct type *type2)
{
struct type *result_type;
if (is_floating_type (type1) || is_floating_type (type2))
{
/* If only one type is floating-point, use its type.
Otherwise use the bigger type. */
if (!is_floating_type (type1))
result_type = type2;
else if (!is_floating_type (type2))
result_type = type1;
else if (TYPE_LENGTH (type2) > TYPE_LENGTH (type1))
result_type = type2;
else
result_type = type1;
}
else
{
/* Integer types. */
if (TYPE_LENGTH (type1) > TYPE_LENGTH (type2))
result_type = type1;
else if (TYPE_LENGTH (type2) > TYPE_LENGTH (type1))
result_type = type2;
else if (TYPE_UNSIGNED (type1))
result_type = type1;
else if (TYPE_UNSIGNED (type2))
result_type = type2;
else
result_type = type1;
}
return result_type;
}
static struct value *scalar_binop (struct value *arg1, struct value *arg2,
enum exp_opcode op);
/* Perform a binary operation on complex operands. */
static struct value *
complex_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
{
struct type *arg1_type = check_typedef (value_type (arg1));
struct type *arg2_type = check_typedef (value_type (arg2));
struct value *arg1_real, *arg1_imag, *arg2_real, *arg2_imag;
if (TYPE_CODE (arg1_type) == TYPE_CODE_COMPLEX)
{
arg1_real = value_real_part (arg1);
arg1_imag = value_imaginary_part (arg1);
}
else
{
arg1_real = arg1;
arg1_imag = value_zero (arg1_type, not_lval);
}
if (TYPE_CODE (arg2_type) == TYPE_CODE_COMPLEX)
{
arg2_real = value_real_part (arg2);
arg2_imag = value_imaginary_part (arg2);
}
else
{
arg2_real = arg2;
arg2_imag = value_zero (arg2_type, not_lval);
}
struct type *comp_type = promotion_type (value_type (arg1_real),
value_type (arg2_real));
arg1_real = value_cast (comp_type, arg1_real);
arg1_imag = value_cast (comp_type, arg1_imag);
arg2_real = value_cast (comp_type, arg2_real);
arg2_imag = value_cast (comp_type, arg2_imag);
struct type *result_type = init_complex_type (nullptr, comp_type);
struct value *result_real, *result_imag;
switch (op)
{
case BINOP_ADD:
case BINOP_SUB:
result_real = scalar_binop (arg1_real, arg2_real, op);
result_imag = scalar_binop (arg1_imag, arg2_imag, op);
break;
case BINOP_MUL:
{
struct value *x1 = scalar_binop (arg1_real, arg2_real, op);
struct value *x2 = scalar_binop (arg1_imag, arg2_imag, op);
result_real = scalar_binop (x1, x2, BINOP_SUB);
x1 = scalar_binop (arg1_real, arg2_imag, op);
x2 = scalar_binop (arg1_imag, arg2_real, op);
result_imag = scalar_binop (x1, x2, BINOP_ADD);
}
break;
case BINOP_DIV:
{
if (TYPE_CODE (arg2_type) == TYPE_CODE_COMPLEX)
{
struct value *conjugate = value_complement (arg2);
/* We have to reconstruct ARG1, in case the type was
promoted. */
arg1 = value_literal_complex (arg1_real, arg1_imag, result_type);
struct value *numerator = scalar_binop (arg1, conjugate,
BINOP_MUL);
arg1_real = value_real_part (numerator);
arg1_imag = value_imaginary_part (numerator);
struct value *x1 = scalar_binop (arg2_real, arg2_real, BINOP_MUL);
struct value *x2 = scalar_binop (arg2_imag, arg2_imag, BINOP_MUL);
arg2_real = scalar_binop (x1, x2, BINOP_ADD);
}
result_real = scalar_binop (arg1_real, arg2_real, op);
result_imag = scalar_binop (arg1_imag, arg2_real, op);
}
break;
case BINOP_EQUAL:
case BINOP_NOTEQUAL:
{
struct value *x1 = scalar_binop (arg1_real, arg2_real, op);
struct value *x2 = scalar_binop (arg1_imag, arg2_imag, op);
LONGEST v1 = value_as_long (x1);
LONGEST v2 = value_as_long (x2);
if (op == BINOP_EQUAL)
v1 = v1 && v2;
else
v1 = v1 || v2;
return value_from_longest (value_type (x1), v1);
}
break;
default:
error (_("Invalid binary operation on numbers."));
}
return value_literal_complex (result_real, result_imag, result_type);
}
/* Perform a binary operation on two operands which have reasonable /* Perform a binary operation on two operands which have reasonable
representations as integers or floats. This includes booleans, representations as integers or floats. This includes booleans,
characters, integers, or floats. characters, integers, or floats.
@ -929,23 +1080,17 @@ scalar_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
type1 = check_typedef (value_type (arg1)); type1 = check_typedef (value_type (arg1));
type2 = check_typedef (value_type (arg2)); type2 = check_typedef (value_type (arg2));
if (TYPE_CODE (type1) == TYPE_CODE_COMPLEX
|| TYPE_CODE (type2) == TYPE_CODE_COMPLEX)
return complex_binop (arg1, arg2, op);
if ((!is_floating_value (arg1) && !is_integral_type (type1)) if ((!is_floating_value (arg1) && !is_integral_type (type1))
|| (!is_floating_value (arg2) && !is_integral_type (type2))) || (!is_floating_value (arg2) && !is_integral_type (type2)))
error (_("Argument to arithmetic operation not a number or boolean.")); error (_("Argument to arithmetic operation not a number or boolean."));
if (is_floating_type (type1) || is_floating_type (type2)) if (is_floating_type (type1) || is_floating_type (type2))
{ {
/* If only one type is floating-point, use its type. result_type = promotion_type (type1, type2);
Otherwise use the bigger type. */
if (!is_floating_type (type1))
result_type = type2;
else if (!is_floating_type (type2))
result_type = type1;
else if (TYPE_LENGTH (type2) > TYPE_LENGTH (type1))
result_type = type2;
else
result_type = type1;
val = allocate_value (result_type); val = allocate_value (result_type);
struct type *eff_type_v1, *eff_type_v2; struct type *eff_type_v1, *eff_type_v2;
@ -1013,16 +1158,8 @@ scalar_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
if one of the operands is unsigned. */ if one of the operands is unsigned. */
if (op == BINOP_RSH || op == BINOP_LSH || op == BINOP_EXP) if (op == BINOP_RSH || op == BINOP_LSH || op == BINOP_EXP)
result_type = type1; result_type = type1;
else if (TYPE_LENGTH (type1) > TYPE_LENGTH (type2))
result_type = type1;
else if (TYPE_LENGTH (type2) > TYPE_LENGTH (type1))
result_type = type2;
else if (TYPE_UNSIGNED (type1))
result_type = type1;
else if (TYPE_UNSIGNED (type2))
result_type = type2;
else else
result_type = type1; result_type = promotion_type (type1, type2);
if (TYPE_UNSIGNED (result_type)) if (TYPE_UNSIGNED (result_type))
{ {
@ -1629,7 +1766,8 @@ value_pos (struct value *arg1)
type = check_typedef (value_type (arg1)); type = check_typedef (value_type (arg1));
if (is_integral_type (type) || is_floating_value (arg1) if (is_integral_type (type) || is_floating_value (arg1)
|| (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type))) || (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type))
|| TYPE_CODE (type) == TYPE_CODE_COMPLEX)
return value_from_contents (type, value_contents (arg1)); return value_from_contents (type, value_contents (arg1));
else else
error (_("Argument to positive operation not a number.")); error (_("Argument to positive operation not a number."));
@ -1663,6 +1801,15 @@ value_neg (struct value *arg1)
} }
return val; return val;
} }
else if (TYPE_CODE (type) == TYPE_CODE_COMPLEX)
{
struct value *real = value_real_part (arg1);
struct value *imag = value_imaginary_part (arg1);
real = value_neg (real);
imag = value_neg (imag);
return value_literal_complex (real, imag, type);
}
else else
error (_("Argument to negate operation not a number.")); error (_("Argument to negate operation not a number."));
} }
@ -1696,6 +1843,16 @@ value_complement (struct value *arg1)
value_contents_all (tmp), TYPE_LENGTH (eltype)); value_contents_all (tmp), TYPE_LENGTH (eltype));
} }
} }
else if (TYPE_CODE (type) == TYPE_CODE_COMPLEX)
{
/* GCC has an extension that treats ~complex as the complex
conjugate. */
struct value *real = value_real_part (arg1);
struct value *imag = value_imaginary_part (arg1);
imag = value_neg (imag);
return value_literal_complex (real, imag, type);
}
else else
error (_("Argument to complement operation not an integer, boolean.")); error (_("Argument to complement operation not an integer, boolean."));