summaryrefslogtreecommitdiff
path: root/gcc/cppexp.c
diff options
context:
space:
mode:
authorneil <neil@138bc75d-0d04-0410-961f-82ee72b054a4>2002-05-26 18:42:21 +0000
committerneil <neil@138bc75d-0d04-0410-961f-82ee72b054a4>2002-05-26 18:42:21 +0000
commitb444d47a3eb7c70492702b498da24bdcdee18148 (patch)
treee0b28595be44974817fef680c5e031a4b195ef68 /gcc/cppexp.c
parent235d7594b104d4c89bd12bdfa44efbe50d7a5ff2 (diff)
downloadgcc-b444d47a3eb7c70492702b498da24bdcdee18148.tar.gz
* cppexp.c (possible_sum_sign, integer_overflow, left_shift,
right_shift): Remove. (cpp_num, cpp_num_part, PART_PRECISION, HALF_MASK, LOW_PART, HIGH_PART): New. (struct op): Use cpp_num. (num_zerop, num_eq, num_positive, num_greater_freq, num_trim, num_part_mul, num_unary_op, num_binary_op, num_negate, num_bitwise_op, num_inequality_op, num_equality_op, num_mul, num_div_op, num_lshift, num_rshift, append_digit): New. (interpret_number, parse_defined, eval_token, reduce): Update for two-integer arithmetic. (binary_handler): New typedef. (optab): Update. (COMPARE, EQUALITY, BITWISE, MINMAX, UNARY, SHIFT): Delete. (_cpp_parse_expr, reduce): Update to handle two-integers. * cpplib.c (_cpp_test_assertion): Back up on CPP_EOF. testsuite: * gcc.dg/cpp/arith-1.c: New semantic tests. * gcc.dg/cpp/if-1.c: Update. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@53900 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/cppexp.c')
-rw-r--r--gcc/cppexp.c1124
1 files changed, 805 insertions, 319 deletions
diff --git a/gcc/cppexp.c b/gcc/cppexp.c
index 2d2ce489c03..7aa350d49bd 100644
--- a/gcc/cppexp.c
+++ b/gcc/cppexp.c
@@ -23,29 +23,61 @@ Boston, MA 02111-1307, USA. */
#include "cpplib.h"
#include "cpphash.h"
-/* Yield nonzero if adding two numbers with A's and B's signs can yield a
- number with SUM's sign, where A, B, and SUM are all C integers. */
-#define possible_sum_sign(a, b, sum) ((((a) ^ (b)) | ~ ((a) ^ (sum))) < 0)
-
-static void integer_overflow PARAMS ((cpp_reader *));
-static HOST_WIDEST_INT left_shift PARAMS ((cpp_reader *, HOST_WIDEST_INT,
- unsigned int,
- unsigned HOST_WIDEST_INT));
-static HOST_WIDEST_INT right_shift PARAMS ((cpp_reader *, HOST_WIDEST_INT,
- unsigned int,
- unsigned HOST_WIDEST_INT));
-static struct op parse_number PARAMS ((cpp_reader *, const cpp_token *));
-static struct op parse_defined PARAMS ((cpp_reader *));
-static struct op eval_token PARAMS ((cpp_reader *, const cpp_token *));
-static struct op *reduce PARAMS ((cpp_reader *, struct op *, enum cpp_ttype));
+typedef unsigned long cpp_num_part;
+typedef struct cpp_num cpp_num;
+
+#define PART_PRECISION (sizeof (cpp_num_part) * CHAR_BIT)
+#define HALF_MASK (~(cpp_num_part) 0 >> (PART_PRECISION / 2))
+#define LOW_PART(num_part) (num_part & HALF_MASK)
+#define HIGH_PART(num_part) (num_part >> (PART_PRECISION / 2))
+
+/* A preprocessing number. Code assumes that any unused high bits of
+ the double integer are set to zero. */
+struct cpp_num
+{
+ cpp_num_part high;
+ cpp_num_part low;
+ bool unsignedp; /* True if value should be treated as unsigned. */
+ bool overflow; /* True if the most recent calculation overflowed. */
+};
struct op
{
+ cpp_num value; /* The value logically "right" of op. */
enum cpp_ttype op;
- uchar unsignedp; /* True if value should be treated as unsigned. */
- HOST_WIDEST_INT value; /* The value logically "right" of op. */
};
+/* Some simple utility routines on double integers. */
+#define num_zerop(num) ((num.low | num.high) == 0)
+#define num_eq(num1, num2) (num1.low == num2.low && num1.high == num2.high)
+static bool num_positive PARAMS ((cpp_num, size_t));
+static bool num_greater_eq PARAMS ((cpp_num, cpp_num, size_t));
+static cpp_num num_trim PARAMS ((cpp_num, size_t));
+static cpp_num num_part_mul PARAMS ((cpp_num_part, cpp_num_part));
+
+static cpp_num num_unary_op PARAMS ((cpp_reader *, cpp_num, enum cpp_ttype));
+static cpp_num num_binary_op PARAMS ((cpp_reader *, cpp_num, cpp_num,
+ enum cpp_ttype));
+static cpp_num num_negate PARAMS ((cpp_num, size_t));
+static cpp_num num_bitwise_op PARAMS ((cpp_reader *, cpp_num, cpp_num,
+ enum cpp_ttype));
+static cpp_num num_inequality_op PARAMS ((cpp_reader *, cpp_num, cpp_num,
+ enum cpp_ttype));
+static cpp_num num_equality_op PARAMS ((cpp_reader *, cpp_num, cpp_num,
+ enum cpp_ttype));
+static cpp_num num_mul PARAMS ((cpp_reader *, cpp_num, cpp_num,
+ enum cpp_ttype));
+static cpp_num num_div_op PARAMS ((cpp_reader *, cpp_num, cpp_num,
+ enum cpp_ttype));
+static cpp_num num_lshift PARAMS ((cpp_num, size_t, size_t));
+static cpp_num num_rshift PARAMS ((cpp_num, size_t, size_t));
+
+static cpp_num append_digit PARAMS ((cpp_num, int, int, size_t));
+static cpp_num interpret_number PARAMS ((cpp_reader *, const cpp_token *));
+static cpp_num parse_defined PARAMS ((cpp_reader *));
+static cpp_num eval_token PARAMS ((cpp_reader *, const cpp_token *));
+static struct op *reduce PARAMS ((cpp_reader *, struct op *, enum cpp_ttype));
+
/* Token type abuse. There is no "error" token, but we can't get
comments in #if, so we can abuse that token type. Similarly,
create unary plus and minus operators. */
@@ -83,26 +115,79 @@ static const struct suffix vsuf_3[] = {
{ "llu", 1, 2 }, { "LLU", 1, 2 }, { "LLu", 1, 2 }, { "llU", 1, 2 }
};
+/* Append DIGIT to NUM, a number of PRECISION bits being read in base
+ BASE. */
+static cpp_num
+append_digit (num, digit, base, precision)
+ cpp_num num;
+ int digit, base;
+ size_t precision;
+{
+ cpp_num result;
+ unsigned int shift = 3 + (base == 16);
+ bool overflow;
+ cpp_num_part add_high, add_low;
+
+ /* Multiply by 8 or 16. Catching this overflow here means we don't
+ need to worry about add_high overflowing. */
+ overflow = num.high >> (PART_PRECISION - shift);
+ result.high = num.high << shift;
+ result.low = num.low << shift;
+ result.high |= num.low >> (PART_PRECISION - shift);
+
+ if (base == 10)
+ {
+ add_low = num.low << 1;
+ add_high = (num.high << 1) + (num.low >> (PART_PRECISION - 1));
+ }
+ else
+ add_high = add_low = 0;
+
+ if (add_low + digit < add_low)
+ add_high++;
+ add_low += digit;
+
+ if (result.low + add_low < result.low)
+ add_high++;
+ if (result.high + add_high < result.high)
+ overflow = true;
+
+ result.low += add_low;
+ result.high += add_high;
+
+ /* The above code catches overflow of a cpp_num type. This catches
+ overflow of the (possibly shorter) target precision. */
+ num.low = result.low;
+ num.high = result.high;
+ result = num_trim (result, precision);
+ if (!num_eq (result, num))
+ overflow = true;
+
+ result.unsignedp = num.unsignedp;
+ result.overflow = overflow;
+ return result;
+}
+
/* Parse and convert what is presumably an integer in TOK. Accepts
decimal, hex, or octal with or without size suffixes. Returned op
is CPP_ERROR on error, otherwise it is a CPP_NUMBER. */
-static struct op
-parse_number (pfile, tok)
+static cpp_num
+interpret_number (pfile, tok)
cpp_reader *pfile;
const cpp_token *tok;
{
- struct op op;
+ cpp_num result;
const uchar *start = tok->val.str.text;
const uchar *end = start + tok->val.str.len;
const uchar *p = start;
- int c = 0, i, nsuff;
- unsigned HOST_WIDEST_INT n = 0, nd, MAX_over_base;
- int base = 10;
- int overflow = 0;
- int digit, largest_digit = 0;
const struct suffix *sufftab;
+ size_t precision = CPP_OPTION (pfile, precision);
+ unsigned int i, nsuff, base = 10, c = 0, largest_digit = 0;
+ bool overflow = false;
- op.unsignedp = 0;
+ result.low = result.high = 0;
+ result.unsignedp = 0;
+ result.overflow = 0;
if (p[0] == '0')
{
@@ -118,25 +203,19 @@ parse_number (pfile, tok)
}
}
- /* Some buggy compilers (e.g. MPW C) seem to need both casts. */
- MAX_over_base = (((unsigned HOST_WIDEST_INT) -1)
- / ((unsigned HOST_WIDEST_INT) base));
-
for(; p < end; p++)
{
c = *p;
- if (ISDIGIT (c)
- || (base == 16 && ISXDIGIT (c)))
- digit = hex_value (c);
+ if (ISDIGIT (c) || (base == 16 && ISXDIGIT (c)))
+ c = hex_value (c);
else
break;
- if (largest_digit < digit)
- largest_digit = digit;
- nd = n * base + digit;
- overflow |= MAX_over_base < n || nd < n;
- n = nd;
+ result = append_digit (result, c, base, precision);
+ overflow |= result.overflow;
+ if (largest_digit < c)
+ largest_digit = c;
}
if (p < end)
@@ -166,7 +245,7 @@ parse_number (pfile, tok)
break;
if (i == nsuff)
goto invalid_suffix;
- op.unsignedp = sufftab[i].u;
+ result.unsignedp = sufftab[i].u;
if (CPP_WTRADITIONAL (pfile)
&& sufftab[i].u
@@ -183,38 +262,34 @@ parse_number (pfile, tok)
"integer constant contains digits beyond the radix");
if (overflow)
- cpp_error (pfile, DL_PEDWARN, "integer constant out of range");
-
+ cpp_error (pfile, DL_PEDWARN, "integer constant too large for its type");
/* If too big to be signed, consider it unsigned. */
- else if ((HOST_WIDEST_INT) n < 0 && ! op.unsignedp)
+ else if (!result.unsignedp && !num_positive (result, precision))
{
if (base == 10)
cpp_error (pfile, DL_WARNING,
"integer constant is so large that it is unsigned");
- op.unsignedp = 1;
+ result.unsignedp = 1;
}
- op.value = n;
- op.op = CPP_NUMBER;
- return op;
+ return result;
invalid_suffix:
cpp_error (pfile, DL_ERROR, "invalid suffix '%.*s' on integer constant",
(int) (end - p), p);
syntax_error:
- op.op = CPP_ERROR;
- return op;
+ return result;
}
/* Handle meeting "defined" in a preprocessor expression. */
-static struct op
+static cpp_num
parse_defined (pfile)
cpp_reader *pfile;
{
+ cpp_num result;
int paren = 0;
cpp_hashnode *node = 0;
const cpp_token *token;
- struct op op;
cpp_context *initial_context = pfile->context;
/* Don't expand macros. */
@@ -253,57 +328,60 @@ parse_defined (pfile)
}
}
- if (!node)
- op.op = CPP_ERROR;
- else
+ if (node)
{
if (pfile->context != initial_context)
cpp_error (pfile, DL_WARNING,
"this use of \"defined\" may not be portable");
- op.value = node->type == NT_MACRO;
- op.unsignedp = 0;
- op.op = CPP_NUMBER;
-
/* A possible controlling macro of the form #if !defined ().
_cpp_parse_expr checks there was no other junk on the line. */
pfile->mi_ind_cmacro = node;
}
pfile->state.prevent_expansion--;
- return op;
+
+ result.unsignedp = 0;
+ result.high = 0;
+ result.overflow = 0;
+ result.low = node && node->type == NT_MACRO;
+ return result;
}
/* Convert a token into a CPP_NUMBER (an interpreted preprocessing
number or character constant, or the result of the "defined" or "#"
operators), or CPP_ERROR on error. */
-static struct op
+static cpp_num
eval_token (pfile, token)
cpp_reader *pfile;
const cpp_token *token;
{
+ cpp_num result;
unsigned int temp;
int unsignedp = 0;
- struct op op;
-
- op.op = CPP_NUMBER;
switch (token->type)
{
case CPP_NUMBER:
- return parse_number (pfile, token);
+ return interpret_number (pfile, token);
case CPP_WCHAR:
case CPP_CHAR:
{
- cppchar_t result = cpp_interpret_charconst (pfile, token,
- &temp, &unsignedp);
- op.value = result;
+ cppchar_t cc = cpp_interpret_charconst (pfile, token,
+ &temp, &unsignedp);
+
+ result.high = 0;
+ result.low = cc;
/* Sign-extend the result if necessary. */
- if (!unsignedp && (cppchar_signed_t) result < 0
- && sizeof (HOST_WIDEST_INT) > sizeof (cppchar_t))
- op.value |= ~(((unsigned HOST_WIDEST_INT) 1 << BITS_PER_CPPCHAR_T)
- - 1);
+ if (!unsignedp && (cppchar_signed_t) cc < 0)
+ {
+ if (PART_PRECISION > BITS_PER_CPPCHAR_T)
+ result.low |= ~(~(cpp_num_part) 0
+ >> (PART_PRECISION - BITS_PER_CPPCHAR_T));
+ result.high = ~(cpp_num_part) 0;
+ result = num_trim (result, CPP_OPTION (pfile, precision));
+ }
}
break;
@@ -314,7 +392,8 @@ eval_token (pfile, token)
&& (token->val.node == pfile->spec_nodes.n_true
|| token->val.node == pfile->spec_nodes.n_false))
{
- op.value = (token->val.node == pfile->spec_nodes.n_true);
+ result.high = 0;
+ result.low = (token->val.node == pfile->spec_nodes.n_true);
/* Warn about use of true or false in #if when pedantic
and stdbool.h has not been included. */
@@ -326,7 +405,8 @@ eval_token (pfile, token)
}
else
{
- op.value = 0;
+ result.high = 0;
+ result.low = 0;
if (CPP_OPTION (pfile, warn_undef) && !pfile->state.skip_eval)
cpp_error (pfile, DL_WARNING, "\"%s\" is not defined",
NODE_NAME (token->val.node));
@@ -334,66 +414,14 @@ eval_token (pfile, token)
break;
default: /* CPP_HASH */
- if (_cpp_test_assertion (pfile, &temp))
- op.op = CPP_ERROR;
- op.value = temp;
+ _cpp_test_assertion (pfile, &temp);
+ result.high = 0;
+ result.low = temp;
}
- op.unsignedp = unsignedp;
- return op;
-}
-
-/* Warn if appropriate on overflow. */
-static void
-integer_overflow (pfile)
- cpp_reader *pfile;
-{
- if (CPP_PEDANTIC (pfile))
- cpp_error (pfile, DL_PEDWARN,
- "integer overflow in preprocessor expression");
-}
-
-/* Handle shifting A left by B bits. UNSIGNEDP is non-zero if A is
- unsigned. */
-static HOST_WIDEST_INT
-left_shift (pfile, a, unsignedp, b)
- cpp_reader *pfile;
- HOST_WIDEST_INT a;
- unsigned int unsignedp;
- unsigned HOST_WIDEST_INT b;
-{
- if (b >= HOST_BITS_PER_WIDEST_INT)
- {
- if (! unsignedp && a != 0)
- integer_overflow (pfile);
- return 0;
- }
- else if (unsignedp)
- return (unsigned HOST_WIDEST_INT) a << b;
- else
- {
- HOST_WIDEST_INT l = a << b;
- if (l >> b != a)
- integer_overflow (pfile);
- return l;
- }
-}
-
-/* Handle shifting A right by B bits. UNSIGNEDP is non-zero if A is
- unsigned. */
-static HOST_WIDEST_INT
-right_shift (pfile, a, unsignedp, b)
- cpp_reader *pfile ATTRIBUTE_UNUSED;
- HOST_WIDEST_INT a;
- unsigned int unsignedp;
- unsigned HOST_WIDEST_INT b;
-{
- if (b >= HOST_BITS_PER_WIDEST_INT)
- return unsignedp ? 0 : a >> (HOST_BITS_PER_WIDEST_INT - 1);
- else if (unsignedp)
- return (unsigned HOST_WIDEST_INT) a >> b;
- else
- return a >> b;
+ result.unsignedp = unsignedp;
+ result.overflow = 0;
+ return result;
}
/* Operator precedence and flags table.
@@ -422,74 +450,58 @@ extra semantics need to be handled with operator-specific code. */
#define NO_L_OPERAND (1 << 0)
#define LEFT_ASSOC (1 << 1)
+/* Arity. */
+#define UNARY (1 << 0)
+#define BINARY (1 << 1)
+#define OTHER (1 << 2)
+
+typedef cpp_num (*binary_handler) PARAMS ((cpp_reader *, cpp_num, cpp_num,
+ enum cpp_ttype));
/* Operator to priority map. Must be in the same order as the first
N entries of enum cpp_ttype. */
static const struct operator
{
uchar prio;
uchar flags;
+ uchar arity;
+ binary_handler handler;
} optab[] =
{
- /* EQ */ {0, 0}, /* Shouldn't happen. */
- /* NOT */ {16, NO_L_OPERAND},
- /* GREATER */ {12, LEFT_ASSOC},
- /* LESS */ {12, LEFT_ASSOC},
- /* PLUS */ {14, LEFT_ASSOC},
- /* MINUS */ {14, LEFT_ASSOC},
- /* MULT */ {15, LEFT_ASSOC},
- /* DIV */ {15, LEFT_ASSOC},
- /* MOD */ {15, LEFT_ASSOC},
- /* AND */ {9, LEFT_ASSOC},
- /* OR */ {7, LEFT_ASSOC},
- /* XOR */ {8, LEFT_ASSOC},
- /* RSHIFT */ {13, LEFT_ASSOC},
- /* LSHIFT */ {13, LEFT_ASSOC},
- /* MIN */ {10, LEFT_ASSOC}, /* C++ specific */
- /* MAX */ {10, LEFT_ASSOC}, /* extensions */
-
- /* COMPL */ {16, NO_L_OPERAND},
- /* AND_AND */ {6, LEFT_ASSOC},
- /* OR_OR */ {5, LEFT_ASSOC},
- /* QUERY */ {3, 0},
- /* COLON */ {4, LEFT_ASSOC},
- /* COMMA */ {2, LEFT_ASSOC},
- /* OPEN_PAREN */ {1, NO_L_OPERAND},
- /* CLOSE_PAREN */ {0, 0},
- /* EOF */ {0, 0},
- /* EQ_EQ */ {11, LEFT_ASSOC},
- /* NOT_EQ */ {11, LEFT_ASSOC},
- /* GREATER_EQ */ {12, LEFT_ASSOC},
- /* LESS_EQ */ {12, LEFT_ASSOC},
- /* UPLUS */ {16, NO_L_OPERAND},
- /* UMINUS */ {16, NO_L_OPERAND}
+ /* EQ */ {0, 0, OTHER, NULL}, /* Shouldn't happen. */
+ /* NOT */ {16, NO_L_OPERAND, UNARY, NULL},
+ /* GREATER */ {12, LEFT_ASSOC, BINARY, num_inequality_op},
+ /* LESS */ {12, LEFT_ASSOC, BINARY, num_inequality_op},
+ /* PLUS */ {14, LEFT_ASSOC, BINARY, num_binary_op},
+ /* MINUS */ {14, LEFT_ASSOC, BINARY, num_binary_op},
+ /* MULT */ {15, LEFT_ASSOC, BINARY, num_mul},
+ /* DIV */ {15, LEFT_ASSOC, BINARY, num_div_op},
+ /* MOD */ {15, LEFT_ASSOC, BINARY, num_div_op},
+ /* AND */ {9, LEFT_ASSOC, BINARY, num_bitwise_op},
+ /* OR */ {7, LEFT_ASSOC, BINARY, num_bitwise_op},
+ /* XOR */ {8, LEFT_ASSOC, BINARY, num_bitwise_op},
+ /* RSHIFT */ {13, LEFT_ASSOC, BINARY, num_binary_op},
+ /* LSHIFT */ {13, LEFT_ASSOC, BINARY, num_binary_op},
+
+ /* MIN */ {10, LEFT_ASSOC, BINARY, num_binary_op},
+ /* MAX */ {10, LEFT_ASSOC, BINARY, num_binary_op},
+
+ /* COMPL */ {16, NO_L_OPERAND, UNARY, NULL},
+ /* AND_AND */ {6, LEFT_ASSOC, OTHER, NULL},
+ /* OR_OR */ {5, LEFT_ASSOC, OTHER, NULL},
+ /* QUERY */ {3, 0, OTHER, NULL},
+ /* COLON */ {4, LEFT_ASSOC, OTHER, NULL},
+ /* COMMA */ {2, LEFT_ASSOC, BINARY, num_binary_op},
+ /* OPEN_PAREN */ {1, NO_L_OPERAND, OTHER, NULL},
+ /* CLOSE_PAREN */ {0, 0, OTHER, NULL},
+ /* EOF */ {0, 0, OTHER, NULL},
+ /* EQ_EQ */ {11, LEFT_ASSOC, BINARY, num_equality_op},
+ /* NOT_EQ */ {11, LEFT_ASSOC, BINARY, num_equality_op},
+ /* GREATER_EQ */ {12, LEFT_ASSOC, BINARY, num_inequality_op},
+ /* LESS_EQ */ {12, LEFT_ASSOC, BINARY, num_inequality_op},
+ /* UPLUS */ {16, NO_L_OPERAND, UNARY, NULL},
+ /* UMINUS */ {16, NO_L_OPERAND, UNARY, NULL}
};
-#define COMPARE(OP) \
- top->unsignedp = 0; \
- top->value = (unsigned1 | unsigned2) \
- ? (unsigned HOST_WIDEST_INT) v1 OP (unsigned HOST_WIDEST_INT) v2 \
- : (v1 OP v2)
-#define EQUALITY(OP) \
- top->value = v1 OP v2; \
- top->unsignedp = 0;
-#define BITWISE(OP) \
- top->value = v1 OP v2; \
- top->unsignedp = unsigned1 | unsigned2;
-#define MINMAX(OP) \
- top->value = (v1 OP v2) ? v1 : v2; \
- top->unsignedp = unsigned1 | unsigned2;
-#define UNARY(OP) \
- top->value = OP v2; \
- top->unsignedp = unsigned2;
-#define SHIFT(PSH, MSH) \
- if (pfile->state.skip_eval) \
- break; \
- top->unsignedp = unsigned1; \
- if (v2 < 0 && ! unsigned2) \
- top->value = MSH (pfile, v1, unsigned1, -v2); \
- else \
- top->value = PSH (pfile, v1, unsigned1, v2);
-
/* Parse and evaluate a C expression, reading from PFILE.
Returns the truth value of the expression.
@@ -541,11 +553,7 @@ _cpp_parse_expr (pfile)
SYNTAX_ERROR2 ("missing binary operator before token \"%s\"",
cpp_token_as_text (pfile, token));
want_value = false;
- op = eval_token (pfile, token);
- if (op.op == CPP_ERROR)
- goto syntax_error;
- top->value = op.value;
- top->unsignedp = op.unsignedp;
+ top->value = eval_token (pfile, token);
continue;
case CPP_NOT:
@@ -607,18 +615,18 @@ _cpp_parse_expr (pfile)
case CPP_CLOSE_PAREN:
continue;
case CPP_OR_OR:
- if (top->value)
+ if (!num_zerop (top->value))
pfile->state.skip_eval++;
break;
case CPP_AND_AND:
case CPP_QUERY:
- if (!top->value)
+ if (num_zerop (top->value))
pfile->state.skip_eval++;
break;
case CPP_COLON:
if (top->op != CPP_QUERY)
SYNTAX_ERROR (" ':' without preceding '?'");
- if (top[-1].value) /* Was '?' condition true? */
+ if (!num_zerop (top[-1].value)) /* Was '?' condition true? */
pfile->state.skip_eval++;
else
pfile->state.skip_eval--;
@@ -648,7 +656,7 @@ _cpp_parse_expr (pfile)
return false; /* Return false on syntax error. */
}
- return top->value != 0;
+ return !num_zerop (top->value);
}
/* Reduce the operator / value stack if possible, in preparation for
@@ -662,6 +670,13 @@ reduce (pfile, top, op)
{
unsigned int prio;
+ if (top->op <= CPP_EQ || top->op > CPP_LAST_CPP_OP + 2)
+ {
+ bad_op:
+ cpp_error (pfile, DL_ICE, "impossible operator '%u'", top->op);
+ return 0;
+ }
+
if (op == CPP_OPEN_PAREN)
return top;
@@ -670,144 +685,73 @@ reduce (pfile, top, op)
prio = optab[op].prio - ((optab[op].flags & LEFT_ASSOC) != 0);
while (prio < optab[top->op].prio)
{
- HOST_WIDEST_INT v1, v2;
- unsigned int unsigned1, unsigned2;
-
- unsigned2 = top->unsignedp, v2 = top->value;
- top--;
- unsigned1 = top->unsignedp, v1 = top->value;
-
- /* Now set top->value = (top[1].op)(v1, v2); */
- switch (top[1].op)
+ if (optab[top->op].arity == UNARY)
{
- default:
- cpp_error (pfile, DL_ICE, "impossible operator '%u'", top[1].op);
- return 0;
-
- case CPP_NOT: UNARY(!); break;
- case CPP_COMPL: UNARY(~); break;
- case CPP_LESS: COMPARE(<); break;
- case CPP_GREATER: COMPARE(>); break;
- case CPP_LESS_EQ: COMPARE(<=); break;
- case CPP_GREATER_EQ: COMPARE(>=); break;
- case CPP_EQ_EQ: EQUALITY(==); break;
- case CPP_NOT_EQ: EQUALITY(!=); break;
- case CPP_AND: BITWISE(&); break;
- case CPP_XOR: BITWISE(^); break;
- case CPP_OR: BITWISE(|); break;
- case CPP_LSHIFT: SHIFT(left_shift, right_shift); break;
- case CPP_RSHIFT: SHIFT(right_shift, left_shift); break;
- case CPP_MIN: MINMAX(<); break;
- case CPP_MAX: MINMAX(>); break;
-
- case CPP_UPLUS:
- /* Can't use UNARY(+) because K+R C did not have unary
- plus. Can't use UNARY() because some compilers object
- to the empty argument. */
- top->value = v2;
- top->unsignedp = unsigned2;
- if (CPP_WTRADITIONAL (pfile))
- cpp_error (pfile, DL_WARNING,
- "traditional C rejects the unary plus operator");
- break;
- case CPP_UMINUS:
- UNARY(-);
- if (!pfile->state.skip_eval && (top->value & v2) < 0 && !unsigned2)
- integer_overflow (pfile);
+ if (!pfile->state.skip_eval)
+ top[-1].value = num_unary_op (pfile, top->value, top->op);
+ top--;
+ }
+ else if (optab[top->op].arity == BINARY)
+ {
+ if (!pfile->state.skip_eval)
+ top[-1].value = (* (binary_handler) optab[top->op].handler)
+ (pfile, top[-1].value, top->value, top->op);
+ top--;
+ }
+ /* Anything changing skip_eval has to be handled here. */
+ else switch (top--->op)
+ {
+ case CPP_OR_OR:
+ if (!num_zerop (top->value))
+ pfile->state.skip_eval--;
+ top->value.low = !num_zerop (top->value) || !num_zerop (top[1].value);
+ top->value.high = 0;
+ top->value.unsignedp = false;
+ top->value.overflow = false;
break;
- case CPP_PLUS:
- top->value = v1 + v2;
- top->unsignedp = unsigned1 | unsigned2;
- if (! top->unsignedp && ! pfile->state.skip_eval
- && ! possible_sum_sign (v1, v2, top->value))
- integer_overflow (pfile);
- break;
- case CPP_MINUS:
- top->value = v1 - v2;
- top->unsignedp = unsigned1 | unsigned2;
- if (! top->unsignedp && ! pfile->state.skip_eval
- && ! possible_sum_sign (top->value, v2, v1))
- integer_overflow (pfile);
- break;
- case CPP_MULT:
- top->unsignedp = unsigned1 | unsigned2;
- if (top->unsignedp)
- top->value = (unsigned HOST_WIDEST_INT) v1 * v2;
- else if (!pfile->state.skip_eval)
- {
- top->value = v1 * v2;
- if (v1 && (top->value / v1 != v2
- || (top->value & v1 & v2) < 0))
- integer_overflow (pfile);
- }
+ case CPP_AND_AND:
+ if (num_zerop (top->value))
+ pfile->state.skip_eval--;
+ top->value.low = !num_zerop (top->value) && !num_zerop (top[1].value);
+ top->value.high = 0;
+ top->value.unsignedp = false;
+ top->value.overflow = false;
break;
- case CPP_DIV:
- case CPP_MOD:
- if (pfile->state.skip_eval)
- break;
- if (v2 == 0)
+
+ case CPP_OPEN_PAREN:
+ if (op != CPP_CLOSE_PAREN)
{
- cpp_error (pfile, DL_ERROR, "division by zero in #if");
+ cpp_error (pfile, DL_ERROR, "missing ')' in expression");
return 0;
}
- top->unsignedp = unsigned1 | unsigned2;
- if (top[1].op == CPP_DIV)
+ top->value = top[1].value;
+ return top;
+
+ case CPP_COLON:
+ top--;
+ if (!num_zerop (top->value))
{
- if (top->unsignedp)
- top->value = (unsigned HOST_WIDEST_INT) v1 / v2;
- else
- {
- top->value = v1 / v2;
- if ((top->value & v1 & v2) < 0)
- integer_overflow (pfile);
- }
+ pfile->state.skip_eval--;
+ top->value = top[1].value;
}
else
- {
- if (top->unsignedp)
- top->value = (unsigned HOST_WIDEST_INT) v1 % v2;
- else
- top->value = v1 % v2;
- }
+ top->value = top[2].value;
+ top->value.unsignedp = (top[1].value.unsignedp
+ || top[2].value.unsignedp);
break;
- case CPP_OR_OR:
- top->value = v1 || v2;
- top->unsignedp = 0;
- if (v1) pfile->state.skip_eval--;
- break;
- case CPP_AND_AND:
- top->value = v1 && v2;
- top->unsignedp = 0;
- if (!v1) pfile->state.skip_eval--;
- break;
- case CPP_COMMA:
- if (CPP_PEDANTIC (pfile))
- cpp_error (pfile, DL_PEDWARN,
- "comma operator in operand of #if");
- top->value = v2;
- top->unsignedp = unsigned2;
- break;
case CPP_QUERY:
cpp_error (pfile, DL_ERROR, "'?' without following ':'");
return 0;
- case CPP_COLON:
- top--;
- if (top->value) pfile->state.skip_eval--;
- top->value = top->value ? v1 : v2;
- top->unsignedp = unsigned1 | unsigned2;
- break;
- case CPP_OPEN_PAREN:
- if (op != CPP_CLOSE_PAREN)
- {
- cpp_error (pfile, DL_ERROR, "missing ')' in expression");
- return 0;
- }
- top->value = v2;
- top->unsignedp = unsigned2;
- return top;
+
+ default:
+ goto bad_op;
}
+
+ if (top->value.overflow && !pfile->state.skip_eval)
+ cpp_error (pfile, DL_PEDWARN,
+ "integer overflow in preprocessor expression");
}
if (op == CPP_CLOSE_PAREN)
@@ -833,3 +777,545 @@ _cpp_expand_op_stack (pfile)
return pfile->op_stack + old_size;
}
+
+/* Clears the unused high order bits of the number pointed to by PNUM. */
+static cpp_num
+num_trim (num, precision)
+ cpp_num num;
+ size_t precision;
+{
+ if (precision > PART_PRECISION)
+ {
+ precision -= PART_PRECISION;
+ if (precision < PART_PRECISION)
+ num.high &= (1 << precision) - 1;
+ }
+ else
+ {
+ if (precision < PART_PRECISION)
+ num.low &= (1 << precision) - 1;
+ num.high = 0;
+ }
+
+ return num;
+}
+
+/* True iff A (presumed signed) >= 0. */
+static bool
+num_positive (num, precision)
+ cpp_num num;
+ size_t precision;
+{
+ if (precision > PART_PRECISION)
+ {
+ precision -= PART_PRECISION;
+ return (num.high & (1 << (precision - 1))) == 0;
+ }
+
+ return (num.low & (1 << (precision - 1))) == 0;
+}
+
+/* Returns the negative of NUM. */
+static cpp_num
+num_negate (num, precision)
+ cpp_num num;
+ size_t precision;
+{
+ cpp_num copy;
+
+ copy = num;
+ num.high = ~num.high;
+ num.low = ~num.low;
+ if (++num.low == 0)
+ num.high++;
+ num = num_trim (num, precision);
+ num.overflow = (!num.unsignedp && num_eq (num, copy) && !num_zerop (num));
+
+ return num;
+}
+
+/* Returns true if A >= B. */
+static bool
+num_greater_eq (pa, pb, precision)
+ cpp_num pa, pb;
+ size_t precision;
+{
+ bool unsignedp;
+
+ unsignedp = pa.unsignedp || pb.unsignedp;
+
+ if (!unsignedp)
+ {
+ /* Both numbers have signed type. If they are of different
+ sign, the answer is the sign of A. */
+ unsignedp = num_positive (pa, precision);
+
+ if (unsignedp != num_positive (pb, precision))
+ return unsignedp;
+
+ /* Otherwise we can do an unsigned comparison. */
+ }
+
+ return (pa.high > pb.high) || (pa.high == pb.high && pa.low >= pb.low);
+}
+
+/* Returns LHS OP RHS, where OP is a bit-wise operation. */
+static cpp_num
+num_bitwise_op (pfile, lhs, rhs, op)
+ cpp_reader *pfile ATTRIBUTE_UNUSED;
+ cpp_num lhs, rhs;
+ enum cpp_ttype op;
+{
+ lhs.overflow = false;
+ lhs.unsignedp = lhs.unsignedp || rhs.unsignedp;
+
+ /* As excess precision is zeroed, there is no need to num_trim () as
+ these operations cannot introduce a set bit there. */
+ if (op == CPP_AND)
+ {
+ lhs.low &= rhs.low;
+ lhs.high &= rhs.high;
+ }
+ else if (op == CPP_OR)
+ {
+ lhs.low |= rhs.low;
+ lhs.high |= rhs.high;
+ }
+ else
+ {
+ lhs.low ^= rhs.low;
+ lhs.high ^= rhs.high;
+ }
+
+ return lhs;
+}
+
+/* Returns LHS OP RHS, where OP is an inequality. */
+static cpp_num
+num_inequality_op (pfile, lhs, rhs, op)
+ cpp_reader *pfile;
+ cpp_num lhs, rhs;
+ enum cpp_ttype op;
+{
+ bool gte = num_greater_eq (lhs, rhs, CPP_OPTION (pfile, precision));
+
+ if (op == CPP_GREATER_EQ)
+ lhs.low = gte;
+ else if (op == CPP_LESS)
+ lhs.low = !gte;
+ else if (op == CPP_GREATER)
+ lhs.low = gte && !num_eq (lhs, rhs);
+ else /* CPP_LESS_EQ. */
+ lhs.low = !gte || num_eq (lhs, rhs);
+
+ lhs.high = 0;
+ lhs.overflow = false;
+ lhs.unsignedp = false;
+ return lhs;
+}
+
+/* Returns LHS OP RHS, where OP is == or !=. */
+static cpp_num
+num_equality_op (pfile, lhs, rhs, op)
+ cpp_reader *pfile ATTRIBUTE_UNUSED;
+ cpp_num lhs, rhs;
+ enum cpp_ttype op;
+{
+ lhs.low = num_eq (lhs, rhs);
+ if (op == CPP_NOT_EQ)
+ lhs.low = !lhs.low;
+ lhs.high = 0;
+ lhs.overflow = false;
+ lhs.unsignedp = false;
+ return lhs;
+}
+
+/* Shift NUM, of width PRECISION, right by N bits. */
+static cpp_num
+num_rshift (num, precision, n)
+ cpp_num num;
+ size_t precision, n;
+{
+ cpp_num_part sign_mask;
+
+ if (num.unsignedp || num_positive (num, precision))
+ sign_mask = 0;
+ else
+ sign_mask = ~(cpp_num_part) 0;
+
+ if (n >= precision)
+ num.high = num.low = sign_mask;
+ else
+ {
+ /* Sign-extend. */
+ if (precision < PART_PRECISION)
+ num.high = sign_mask, num.low |= sign_mask << precision;
+ else if (precision < 2 * PART_PRECISION)
+ num.high |= sign_mask << (precision - PART_PRECISION);
+
+ if (n >= PART_PRECISION)
+ {
+ n -= PART_PRECISION;
+ num.low = num.high;
+ num.high = sign_mask;
+ }
+
+ if (n)
+ {
+ num.low = (num.low >> n) | (num.high << (PART_PRECISION - n));
+ num.high = (num.high >> n) | (sign_mask << (PART_PRECISION - n));
+ }
+ }
+
+ num = num_trim (num, precision);
+ num.overflow = false;
+ return num;
+}
+
+/* Shift NUM, of width PRECISION, left by N bits. */
+static cpp_num
+num_lshift (num, precision, n)
+ cpp_num num;
+ size_t precision, n;
+{
+ if (n >= precision)
+ {
+ num.overflow = !num.unsignedp && !num_zerop (num);
+ num.high = num.low = 0;
+ }
+ else
+ {
+ cpp_num orig, maybe_orig;
+ size_t m = n;
+
+ orig = num;
+ if (m >= PART_PRECISION)
+ {
+ m -= PART_PRECISION;
+ num.high = num.low;
+ num.low = 0;
+ }
+ if (m)
+ {
+ num.high = (num.high << m) | (num.low >> (PART_PRECISION - m));
+ num.low <<= m;
+ }
+ num = num_trim (num, precision);
+
+ if (num.unsignedp)
+ num.overflow = false;
+ else
+ {
+ maybe_orig = num_rshift (num, precision, n);
+ num.overflow = !num_eq (orig, maybe_orig);
+ }
+ }
+
+ return num;
+}
+
+/* The four unary operators: +, -, ! and ~. */
+static cpp_num
+num_unary_op (pfile, num, op)
+ cpp_reader *pfile;
+ cpp_num num;
+ enum cpp_ttype op;
+{
+ switch (op)
+ {
+ case CPP_UPLUS:
+ if (CPP_WTRADITIONAL (pfile))
+ cpp_error (pfile, DL_WARNING,
+ "traditional C rejects the unary plus operator");
+ num.overflow = false;
+ break;
+
+ case CPP_UMINUS:
+ num = num_negate (num, CPP_OPTION (pfile, precision));
+ break;
+
+ case CPP_COMPL:
+ num.high = ~num.high;
+ num.low = ~num.low;
+ num = num_trim (num, CPP_OPTION (pfile, precision));
+ num.overflow = false;
+ break;
+
+ default: /* case CPP_NOT: */
+ num.low = num_zerop (num);
+ num.high = 0;
+ num.overflow = false;
+ num.unsignedp = false;
+ break;
+ }
+
+ return num;
+}
+
+/* The various binary operators. */
+static cpp_num
+num_binary_op (pfile, lhs, rhs, op)
+ cpp_reader *pfile;
+ cpp_num lhs, rhs;
+ enum cpp_ttype op;
+{
+ cpp_num result;
+ size_t precision = CPP_OPTION (pfile, precision);
+ bool gte;
+ size_t n;
+
+ switch (op)
+ {
+ /* Shifts. */
+ case CPP_LSHIFT:
+ case CPP_RSHIFT:
+ if (!rhs.unsignedp && !num_positive (rhs, precision))
+ {
+ /* A negative shift is a positive shift the other way. */
+ if (op == CPP_LSHIFT)
+ op = CPP_RSHIFT;
+ else
+ op = CPP_LSHIFT;
+ rhs = num_negate (rhs, precision);
+ }
+ if (rhs.high)
+ n = ~0; /* Maximal. */
+ else
+ n = rhs.low;
+ if (op == CPP_LSHIFT)
+ lhs = num_lshift (lhs, precision, n);
+ else
+ lhs = num_rshift (lhs, precision, n);
+ break;
+
+ /* Min / Max. */
+ case CPP_MIN:
+ case CPP_MAX:
+ {
+ bool unsignedp = lhs.unsignedp || rhs.unsignedp;
+
+ gte = num_greater_eq (lhs, rhs, precision);
+ if (op == CPP_MIN)
+ gte = !gte;
+ if (!gte)
+ lhs = rhs;
+ lhs.unsignedp = unsignedp;
+ }
+ break;
+
+ /* Arithmetic. */
+ case CPP_MINUS:
+ rhs = num_negate (rhs, precision);
+ case CPP_PLUS:
+ result.low = lhs.low + rhs.low;
+ result.high = lhs.high + rhs.high;
+ if (result.low < lhs.low)
+ result.high++;
+
+ result = num_trim (result, precision);
+ result.unsignedp = lhs.unsignedp || rhs.unsignedp;
+ if (result.unsignedp)
+ result.overflow = false;
+ else
+ {
+ bool lhsp = num_positive (lhs, precision);
+ result.overflow = (lhsp == num_positive (rhs, precision)
+ && lhsp != num_positive (result, precision));
+ }
+ return result;
+
+ /* Comma. */
+ default: /* case CPP_COMMA: */
+ if (CPP_PEDANTIC (pfile))
+ cpp_error (pfile, DL_PEDWARN,
+ "comma operator in operand of #if");
+ lhs = rhs;
+ break;
+ }
+
+ return lhs;
+}
+
+/* Multiplies two unsigned cpp_num_parts to give a cpp_num. This
+ cannot overflow. */
+static cpp_num
+num_part_mul (lhs, rhs)
+ cpp_num_part lhs, rhs;
+{
+ cpp_num result;
+ cpp_num_part middle[2], temp;
+
+ result.low = LOW_PART (lhs) * LOW_PART (rhs);
+ result.high = HIGH_PART (lhs) * HIGH_PART (rhs);
+
+ middle[0] = LOW_PART (lhs) * HIGH_PART (rhs);
+ middle[1] = HIGH_PART (lhs) * LOW_PART (rhs);
+
+ temp = result.low;
+ result.low += LOW_PART (middle[0]) << (PART_PRECISION / 2);
+ if (result.low < temp)
+ result.high++;
+
+ temp = result.low;
+ result.low += LOW_PART (middle[1]) << (PART_PRECISION / 2);
+ if (result.low < temp)
+ result.high++;
+
+ result.high += HIGH_PART (middle[0]);
+ result.high += HIGH_PART (middle[1]);
+
+ return result;
+}
+
+/* Multiply two preprocessing numbers. */
+static cpp_num
+num_mul (pfile, lhs, rhs, op)
+ cpp_reader *pfile;
+ cpp_num lhs, rhs;
+ enum cpp_ttype op ATTRIBUTE_UNUSED;
+{
+ cpp_num result, temp;
+ bool unsignedp = lhs.unsignedp || rhs.unsignedp;
+ bool overflow, negate = false;
+ size_t precision = CPP_OPTION (pfile, precision);
+
+ /* Prepare for unsigned multiplication. */
+ if (!unsignedp)
+ {
+ if (!num_positive (lhs, precision))
+ negate = !negate, lhs = num_negate (lhs, precision);
+ if (!num_positive (rhs, precision))
+ negate = !negate, rhs = num_negate (rhs, precision);
+ }
+
+ overflow = lhs.high && rhs.high;
+ result = num_part_mul (lhs.low, rhs.low);
+
+ temp = num_part_mul (lhs.high, rhs.low);
+ result.high += temp.low;
+ if (temp.high)
+ overflow = true;
+
+ temp = num_part_mul (lhs.low, rhs.high);
+ result.high += temp.low;
+ if (temp.high)
+ overflow = true;
+
+ temp.low = result.low, temp.high = result.high;
+ result = num_trim (result, precision);
+ if (!num_eq (result, temp))
+ overflow = true;
+
+ if (negate)
+ result = num_negate (result, precision);
+
+ if (unsignedp)
+ result.overflow = false;
+ else
+ result.overflow = overflow || (num_positive (result, precision) ^ !negate
+ && !num_zerop (result));
+ result.unsignedp = unsignedp;
+
+ return result;
+}
+
+/* Divide two preprocessing numbers, returning the answer or the
+ remainder depending upon OP. */
+static cpp_num
+num_div_op (pfile, lhs, rhs, op)
+ cpp_reader *pfile;
+ cpp_num lhs, rhs;
+ enum cpp_ttype op;
+{
+ cpp_num result, sub;
+ cpp_num_part mask;
+ bool unsignedp = lhs.unsignedp || rhs.unsignedp;
+ bool negate = false, lhs_neg = false;
+ size_t i, precision = CPP_OPTION (pfile, precision);
+
+ /* Prepare for unsigned division. */
+ if (!unsignedp)
+ {
+ if (!num_positive (lhs, precision))
+ negate = !negate, lhs_neg = true, lhs = num_negate (lhs, precision);
+ if (!num_positive (rhs, precision))
+ negate = !negate, rhs = num_negate (rhs, precision);
+ }
+
+ /* Find the high bit. */
+ if (rhs.high)
+ {
+ i = precision - 1;
+ mask = 1 << (i - PART_PRECISION);
+ for (; ; i--, mask >>= 1)
+ if (rhs.high & mask)
+ break;
+ }
+ else if (rhs.low)
+ {
+ if (precision > PART_PRECISION)
+ i = precision - PART_PRECISION - 1;
+ else
+ i = precision - 1;
+ mask = 1 << i;
+ for (; ; i--, mask >>= 1)
+ if (rhs.low & mask)
+ break;
+ }
+ else
+ {
+ cpp_error (pfile, DL_ERROR, "division by zero in #if");
+ return lhs;
+ }
+
+ /* First non-zero bit of RHS is bit I. Do naive division by
+ shifting the RHS fully left, and subtracting from LHS if LHS is
+ at least as big, and then repeating but with one less shift.
+ This is not very efficient, but is easy to understand. */
+
+ rhs.unsignedp = true;
+ lhs.unsignedp = true;
+ i = precision - i - 1;
+ sub = num_lshift (rhs, precision, i);
+
+ result.high = result.low = 0;
+ for (;;)
+ {
+ if (num_greater_eq (lhs, sub, precision))
+ {
+ lhs = num_binary_op (pfile, lhs, sub, CPP_MINUS);
+ if (i >= PART_PRECISION)
+ result.high |= 1 << (i - PART_PRECISION);
+ else
+ result.low |= 1 << i;
+ }
+ if (i-- == 0)
+ break;
+ sub.low = (sub.low >> 1) | (sub.high << (PART_PRECISION - 1));
+ sub.high >>= 1;
+ }
+
+ /* We divide so that the remainder has the sign of the LHS. */
+ if (op == CPP_DIV)
+ {
+ result.unsignedp = unsignedp;
+ if (unsignedp)
+ result.overflow = false;
+ else
+ {
+ if (negate)
+ result = num_negate (result, precision);
+ result.overflow = num_positive (result, precision) ^ !negate;
+ }
+
+ return result;
+ }
+
+ /* CPP_MOD. */
+ lhs.unsignedp = unsignedp;
+ lhs.overflow = false;
+ if (lhs_neg)
+ lhs = num_negate (lhs, precision);
+
+ return lhs;
+}