diff --git a/Makefile b/Makefile index 2045978..d2ec3d0 100644 --- a/Makefile +++ b/Makefile @@ -265,7 +265,7 @@ lib/%/libtcc1.a : FORCE $(PROGS_CROSS) FORCE: # install -TCC_INCLUDES = stdarg.h stddef.h stdbool.h float.h varargs.h +TCC_INCLUDES = stdarg.h stddef.h stdbool.h float.h varargs.h complex.h INSTALL=install ifdef STRIP_BINARIES INSTALLBIN=$(INSTALL) -s diff --git a/TODO b/TODO index eb846d9..cbdac46 100644 --- a/TODO +++ b/TODO @@ -64,6 +64,17 @@ Missing features: - atexit (Nigel Horne) - packed attribute - C99: add complex types (gcc 3.2 testsuite issue) + (Complex types and complex constants, with constant propagation, + are already implemented, but nothing else so far. A complex + number could be held in two registers rather like how a 64-bit + integer is held in two registers on a 32-bit target. It is probably + possible to implement complex arithmetic without modifying + the back-ends: * and / turn into function calls, and + and - turn + into a pair of ordinary floating-point operations. It may be possible + to implement argument passing without adding specific code to the + back-ends, at least if all TCC architectures pass a complex number + in the same way as they would pass a struct containing two ordinary + floating point members.) - postfix compound literals (see 20010124-1.c) Optimizations: diff --git a/include/complex.h b/include/complex.h new file mode 100644 index 0000000..5b331cb --- /dev/null +++ b/include/complex.h @@ -0,0 +1,8 @@ +#ifndef _COMPLEX_H +#define _COMPLEX_H 1 + +#define complex _Complex +#define _Complex_I __tcc_Complex_I +#define I _Complex_I + +#endif diff --git a/tcc.h b/tcc.h index c284880..5b75e4d 100644 --- a/tcc.h +++ b/tcc.h @@ -396,6 +396,7 @@ typedef struct CType { /* constant value */ typedef union CValue { + struct { long double real, imag; } c; long double ld; double d; float f; @@ -839,6 +840,7 @@ struct TCCState { #define VT_UNSIGNED 0x0010 /* unsigned type */ #define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */ #define VT_BITFIELD 0x0040 /* bitfield modifier */ +#define VT_COMPLEX 0x80000 /* _Complex */ #define VT_CONSTANT 0x0800 /* const modifier */ #define VT_VOLATILE 0x1000 /* volatile modifier */ #define VT_DEFSIGN 0x2000 /* signed type */ @@ -853,14 +855,14 @@ struct TCCState { #define VT_EXPORT 0x00008000 /* win32: data exported from dll */ #define VT_WEAK 0x00010000 /* weak symbol */ #define VT_TLS 0x00040000 /* thread-local storage */ -#define VT_VIS_SHIFT 19 /* shift for symbol visibility, overlapping +#define VT_VIS_SHIFT 20 /* shift for symbol visibility, overlapping bitfield values, because bitfields never have linkage and hence never have visibility. */ #define VT_VIS_SIZE 2 /* We have four visibilities. */ #define VT_VIS_MASK (((1 << VT_VIS_SIZE)-1) << VT_VIS_SHIFT) -#define VT_STRUCT_SHIFT 19 /* shift for bitfield shift values (max: 32 - 2*6) */ +#define VT_STRUCT_SHIFT 20 /* shift for bitfield shift values (max: 32 - 2*6) */ /* type mask (except storage) */ diff --git a/tccgen.c b/tccgen.c index 72e892c..ad64acf 100644 --- a/tccgen.c +++ b/tccgen.c @@ -1612,6 +1612,52 @@ static void gen_opif(int op) } } +static void gen_opx(int op) +{ + tcc_error("complex arithmetic unimplemented"); +} + +/* generate a complex point operation with constant propagation */ +static void gen_opix(int op) +{ + SValue *v1 = vtop - 1; + SValue *v2 = vtop; + int c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + int c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + if (c1 && c2) { + /* XXX: ignoring NaNs and infinities */ + long double r1 = v1->c.c.real; + long double i1 = v1->c.c.imag; + long double r2 = v2->c.c.real; + long double i2 = v2->c.c.imag; + switch (op) { + case '+': + v1->c.c.real = r1 + r2; + v1->c.c.imag = i1 + i2; + break; + case '-': + v1->c.c.real = r1 - r2; + v1->c.c.imag = i1 - i2; + break; + case '*': + v1->c.c.real = r1 * r2 - i1 * i2; + v1->c.c.imag = r1 * i2 + i1 * r2; + break; + case '/': { + long double d = r2 * r2 + i2 * i2; + v1->c.c.real = (r1 * r2 + i1 * i2) / d; + v1->c.c.imag = (i1 * r2 - r1 * i2) / d; + break; + } + } + --vtop; + } + else if (!nocode_wanted) + gen_opx(op); + else + --vtop; +} + static int pointed_size(CType *type) { int align; @@ -1698,8 +1744,22 @@ ST_FUNC void gen_op(int op) t2 = vtop[0].type.t; bt1 = t1 & VT_BTYPE; bt2 = t2 & VT_BTYPE; - - if (bt1 == VT_PTR || bt2 == VT_PTR) { + + if ((t1 | t2) & VT_COMPLEX) { + if (op != '+' && op != '-' && op != '*' && op != '/' && + op != TOK_EQ && op != TOK_NE) + tcc_error("invalid operands for binary operation"); + t = (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE ? VT_LDOUBLE : + bt1 == VT_DOUBLE || bt2 == VT_DOUBLE ? VT_DOUBLE : + VT_FLOAT) | VT_COMPLEX; + type1.t = t; + vswap(); + gen_cast(&type1); + vswap(); + gen_cast(&type1); + gen_opix(op); + } + else if (bt1 == VT_PTR || bt2 == VT_PTR) { /* at least one operand is a pointer */ /* relationnal op: must be both pointers */ if (op >= TOK_ULT && op <= TOK_LOR) { @@ -1987,6 +2047,26 @@ static void gen_cast(CType *type) if (c) { /* constant case: we can do it now */ /* XXX: in ISOC, cannot do it if error in convert */ + + if ((type->t | vtop->type.t) & VT_COMPLEX) { + if (!(vtop->type.t & VT_COMPLEX)) { + CType type1 = { 0 }; + type1.t = VT_LDOUBLE; + gen_cast(&type1); + vtop->c.c.real = vtop->c.ld; + vtop->c.c.imag = 0; + vtop->type = *type; + } + else if (!(type->t & VT_COMPLEX)) { + vtop->c.ld = vtop->c.c.real; + vtop->type.t = VT_LDOUBLE; + gen_cast(type); + } + else + vtop->type = *type; + return; + } + if (sbt == VT_FLOAT) vtop->c.ld = vtop->c.f; else if (sbt == VT_DOUBLE) @@ -2050,6 +2130,10 @@ static void gen_cast(CType *type) vtop->c.i = 1; } else if (!nocode_wanted) { /* non constant case: generate code */ + + if ((type->t | vtop->type.t) & VT_COMPLEX) + tcc_error("complex cast unimplemented"); + if (sf && df) { /* convert from fp to fp */ gen_cvt_ftof(dbt); @@ -2182,7 +2266,7 @@ ST_FUNC int type_size(CType *type, int *a) } } else if (bt == VT_LDOUBLE) { *a = LDOUBLE_ALIGN; - return LDOUBLE_SIZE; + return type->t & VT_COMPLEX ? LDOUBLE_SIZE * 2 : LDOUBLE_SIZE; } else if (bt == VT_DOUBLE || bt == VT_LLONG) { #ifdef TCC_TARGET_I386 #ifdef TCC_TARGET_PE @@ -2199,10 +2283,10 @@ ST_FUNC int type_size(CType *type, int *a) #else *a = 8; #endif - return 8; + return type->t & VT_COMPLEX ? 16 : 8; } else if (bt == VT_INT || bt == VT_ENUM || bt == VT_FLOAT) { *a = 4; - return 4; + return type->t & VT_COMPLEX ? 8 : 4; } else if (bt == VT_SHORT) { *a = 2; return 2; @@ -2656,6 +2740,8 @@ ST_FUNC void vstore(void) #endif rc = RC_INT; if (is_float(ft)) { + if (ft & VT_COMPLEX) + tcc_error("complex vstore unimplemented"); rc = RC_FLOAT; #ifdef TCC_TARGET_X86_64 if ((ft & VT_BTYPE) == VT_LDOUBLE) { @@ -3201,6 +3287,12 @@ static int parse_btype(CType *type, AttributeDef *ad) case TOK_BOOL: u = VT_BOOL; goto basic_type; + case TOK_COMPLEX: + if (t & VT_COMPLEX) + tcc_error("duplicate _Complex"); + t |= VT_COMPLEX; + next(); + break; case TOK_FLOAT: u = VT_FLOAT; goto basic_type; @@ -3349,6 +3441,11 @@ the_end: #else t = (t & ~VT_BTYPE) | VT_LLONG; #endif + + if ((t & VT_COMPLEX) && (t & VT_BTYPE) != VT_FLOAT && + (t & VT_BTYPE) != VT_DOUBLE && (t & VT_BTYPE) != VT_LDOUBLE) + tcc_error("complex type must be floating-point"); + type->t = t; return type_found; } @@ -3764,6 +3861,12 @@ ST_FUNC void unary(void) vpush_tokc(VT_LDOUBLE); next(); break; + case TOK_COMPLEX_I: + vpush_tokc(VT_COMPLEX | VT_FLOAT); + vtop->c.c.real = 0; + vtop->c.c.imag = 1; + next(); + break; case TOK___FUNCTION__: if (!gnu_ext) goto tok_identifier; @@ -4076,7 +4179,10 @@ ST_FUNC void unary(void) /* In IEEE negate(x) isn't subtract(0,x), but rather subtract(-0, x). */ vpush(&vtop->type); - if (t == VT_FLOAT) + if (vtop->type.t & VT_COMPLEX) { + vtop->c.c.real = 0; + vtop->c.c.imag = 0; + } else if (t == VT_FLOAT) vtop->c.f = -0.0f; else if (t == VT_DOUBLE) vtop->c.d = -0.0; diff --git a/tcctok.h b/tcctok.h index 31b6dae..6f59bb6 100644 --- a/tcctok.h +++ b/tcctok.h @@ -40,6 +40,8 @@ DEF(TOK_FLOAT, "float") DEF(TOK_DOUBLE, "double") DEF(TOK_BOOL, "_Bool") + DEF(TOK_COMPLEX, "_Complex") + DEF(TOK_COMPLEX_I, "__tcc_Complex_I") DEF(TOK_SHORT, "short") DEF(TOK_STRUCT, "struct") DEF(TOK_UNION, "union") diff --git a/tests/tests2/80_complex.c b/tests/tests2/80_complex.c new file mode 100644 index 0000000..62087b8 --- /dev/null +++ b/tests/tests2/80_complex.c @@ -0,0 +1,24 @@ +#include +#include + +int main() +{ + float f; + double d; + long double l; + float _Complex fc; + double _Complex dc; + long double _Complex lc; + + printf("%d %d %d %d\n", + sizeof(fc) == 2 * sizeof(f), + sizeof(dc) == 2 * sizeof(d), + sizeof(lc) == 2 * sizeof(l), + sizeof(I) == 2 * sizeof(f)); + printf("%d\n", (int)((2 + 3 * I) * -(5 + 7 * I))); + printf("%d\n", (int)((2 + 3 * I) * (5 + 7 * I) * -I)); + + /* Currently that's all that's implemented for complex types. */ + + return 0; +} diff --git a/tests/tests2/80_complex.expect b/tests/tests2/80_complex.expect new file mode 100644 index 0000000..5d81a47 --- /dev/null +++ b/tests/tests2/80_complex.expect @@ -0,0 +1,3 @@ +1 1 1 1 +11 +29