This means that floating-point constants can now have the range and precision of the extended type (aka long double), and floating-point constant expressions evaluated within the compiler also have that same range and precision (matching expressions evaluated at run time). This new behavior is intended to match the behavior specified in the C99 and later standards for FLT_EVAL_METHOD 2.
This fixes the previous problem where long double constants and constant expressions of type long double were not represented and evaluated with the full range and precision that they should be. It also gives extra range and precision to constants and constant expressions of type double or float. This may have pluses and minuses, but at any rate it is consistent with the existing behavior for expressions evaluated at run time, and with one of the possible models of floating point evaluation specified in the C standards.
This gives the name of the current function, as if the following definition appeared at the beginning of the function body:
static const char __func__[] = "function-name";
Note that we currently defer evaluation of such expressions to run time if the long long value cannot be represented exactly in a double, because statically-evaluated floating point expressions use the double format rather than the extended (long double) format used at run time.
The changes to constant expressions were not allowing the unsupported constant expressions to be evaluated at run time when they appear in regular code.
This converts them to 32-bit values before doing computations, which is (more than) sufficient for address calculations on the 65816. Trying to compute an address outside the legal range is undefined behavior, and does not necessarily "wrap around" in a predictable way.
Currently, the actual values they can have are still constrained to the 32-bit range. Also, there are some bits of functionality (e.g. for initializers) that are not implemented yet.
These would generally not work correctly on bit-fields, or on floating-point values that were in a structure or were accessed via a pointer.
The below program is an example that would demonstrate problems:
#include <stdio.h>
int main(void) {
struct {
signed int i:7;
unsigned long int j:6;
_Bool b:1;
double d;
} s = {-10, -20, 0, 5.0};
double d = 70.0, *dp = &d;
printf("%i\n", (int)s.i++);
printf("%i\n", (int)s.i--);
printf("%i\n", (int)++s.i);
printf("%i\n", (int)--s.i);
printf("%i\n", (int)s.i);
printf("%i\n", (int)s.j++);
printf("%i\n", (int)s.j--);
printf("%i\n", (int)++s.j);
printf("%i\n", (int)--s.j);
printf("%i\n", (int)s.j);
printf("%i\n", s.b++);
printf("%i\n", s.b--);
printf("%i\n", ++s.b);
printf("%i\n", --s.b);
printf("%i\n", s.b);
printf("%f\n", s.d++);
printf("%f\n", s.d--);
printf("%f\n", ++s.d);
printf("%f\n", --s.d);
printf("%f\n", s.d);
printf("%f\n", (*dp)++);
printf("%f\n", (*dp)--);
printf("%f\n", ++*dp);
printf("%f\n", --*dp);
printf("%f\n", *dp);
}
This was not happening for declared identifiers (variables and functions) or for enum constants, as demonstrated in the following example:
enum {a,b,c};
#if b
#error "bad b"
#endif
int x = 0;
#if x
#error "bad x"
#endif
This currently checks for:
*Calls to undefined functions (same as bit 0)
*Parameters not declared in K&R-style function definitions
*Declarations or type names with no type specifiers (includes but is broader than the condition checked by bit 1)
_Bool, _Complex, _Imaginary, _Atomic, restrict, and _Alignas are now recognized in types, but all except restrict and _Alignas will give an error saying they are not supported.
This also introduces uniform definitions of the syntactic classes of tokens that can be used in declaration specifiers and related constructs (currently used in some places but not yet in others).
As of C11, type names are now used as part of the declaration syntax (in _Alignas and _Atomic specifiers), in addition to their uses in expressions. Moving the TypeName method will allow it to be called when processing declarations.
This can occur in cases such as trying to assign to a non-l-value.
This patch ensures consistent handling of errors and prevents null pointer dereferences.
Previously, the logic for this was incorrect and would lead to a null pointer dereference in the compiler. In most cases the generated code would not actually change the pointer.
The following program demonstrates the issue:
#include <stdio.h>
#pragma memorymodel 1
typedef char bigarray[0x20000];
bigarray big[5];
int main(void) {
bigarray *p = big;
p++;
printf("%p %p\n", (void*)big, (void*)p);
}
This could cause spurious errors, or in some cases bad code generation.
The following example illustrates the problem:
#include <stdio.h>
enum {A,B,C};
/* arr was treated as having a size of 1, rather than 3 */
char arr[(int)C+1] = {1,2,3}; /* incorrectly gave an error for initializer */
int main(void) {
static int i = (int)C+1; /* incorrectly gave an error */
printf("%zu\n", sizeof(arr));
printf("%i\n", (int)C+1); /* OK */
printf("%i\n", i);
}
Per the C standards, the % operator should give a remainder after division, such that (a/b)*b + a%b equals a (provided that a/b is representable). As such, the operation of % is defined for cases where either or both of the operands are negative. Since division truncates toward 0, a%b should give a negative result (or 0) in cases where a is negative.
Previously, the % operator was essentially behaving like the "mod" operator in Pascal, which is equivalent for positive operands but not if either operand is negative. It would generally give incorrect results in those cases, or in some cases give compile-time or run-time errors.
This patch addresses both 16-bit and 32-bit signed computations at run time, and operations in constant expressions. The approach at run time is to call existing division routines, which return the correct remainder, except always as a positive number. The generated code checks the sign of the first operand, and if it is negative negates the remainder.
The code generated is somewhat large (especially for the 32-bit case), so it might be sensible to put it in a library function and call that, but for now it's just generated in-line. This avoids introducing a dependency on a new library function, so the generated code remains compatible with older versions of ORCALib (e.g. the GNO one).
Fixes#10.