#include "config.h"
#include "system.h"
#include "cpplib.h"
#include "internal.h"
#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))
struct op
{
const cpp_token *token;
cpp_num value;
enum cpp_ttype op;
};
#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 (cpp_num, size_t);
static bool num_greater_eq (cpp_num, cpp_num, size_t);
static cpp_num num_trim (cpp_num, size_t);
static cpp_num num_part_mul (cpp_num_part, cpp_num_part);
static cpp_num num_unary_op (cpp_reader *, cpp_num, enum cpp_ttype);
static cpp_num num_binary_op (cpp_reader *, cpp_num, cpp_num, enum cpp_ttype);
static cpp_num num_negate (cpp_num, size_t);
static cpp_num num_bitwise_op (cpp_reader *, cpp_num, cpp_num, enum cpp_ttype);
static cpp_num num_inequality_op (cpp_reader *, cpp_num, cpp_num,
enum cpp_ttype);
static cpp_num num_equality_op (cpp_reader *, cpp_num, cpp_num,
enum cpp_ttype);
static cpp_num num_mul (cpp_reader *, cpp_num, cpp_num);
static cpp_num num_div_op (cpp_reader *, cpp_num, cpp_num, enum cpp_ttype);
static cpp_num num_lshift (cpp_num, size_t, size_t);
static cpp_num num_rshift (cpp_num, size_t, size_t);
static cpp_num append_digit (cpp_num, int, int, size_t);
static cpp_num parse_defined (cpp_reader *);
static cpp_num eval_token (cpp_reader *, const cpp_token *);
static struct op *reduce (cpp_reader *, struct op *, enum cpp_ttype);
static unsigned int interpret_float_suffix (const uchar *, size_t);
static unsigned int interpret_int_suffix (const uchar *, size_t);
static void check_promotion (cpp_reader *, const struct op *);
#define CPP_UPLUS (CPP_LAST_CPP_OP + 1)
#define CPP_UMINUS (CPP_LAST_CPP_OP + 2)
#define SYNTAX_ERROR(msgid) \
do { cpp_error (pfile, CPP_DL_ERROR, msgid); goto syntax_error; } while(0)
#define SYNTAX_ERROR2(msgid, arg) \
do { cpp_error (pfile, CPP_DL_ERROR, msgid, arg); goto syntax_error; } \
while(0)
static unsigned int
interpret_float_suffix (const uchar *s, size_t len)
{
size_t f = 0, l = 0, i = 0;
while (len--)
switch (s[len])
{
case 'f': case 'F': f++; break;
case 'l': case 'L': l++; break;
case 'i': case 'I':
case 'j': case 'J': i++; break;
default:
return 0;
}
if (f + l > 1 || i > 1)
return 0;
return ((i ? CPP_N_IMAGINARY : 0)
| (f ? CPP_N_SMALL :
l ? CPP_N_LARGE : CPP_N_MEDIUM));
}
static unsigned int
interpret_int_suffix (const uchar *s, size_t len)
{
size_t u, l, i;
u = l = i = 0;
while (len--)
switch (s[len])
{
case 'u': case 'U': u++; break;
case 'i': case 'I':
case 'j': case 'J': i++; break;
case 'l': case 'L': l++;
if (l == 2 && s[len] != s[len + 1])
return 0;
break;
default:
return 0;
}
if (l > 2 || u > 1 || i > 1)
return 0;
return ((i ? CPP_N_IMAGINARY : 0)
| (u ? CPP_N_UNSIGNED : 0)
| ((l == 0) ? CPP_N_SMALL
: (l == 1) ? CPP_N_MEDIUM : CPP_N_LARGE));
}
unsigned int
cpp_classify_number (cpp_reader *pfile, const cpp_token *token)
{
const uchar *str = token->val.str.text;
const uchar *limit;
unsigned int max_digit, result, radix;
enum {NOT_FLOAT = 0, AFTER_POINT, AFTER_EXPON} float_flag;
if (token->val.str.len == 1)
return CPP_N_INTEGER | CPP_N_SMALL | CPP_N_DECIMAL;
limit = str + token->val.str.len;
float_flag = NOT_FLOAT;
max_digit = 0;
radix = 10;
if (*str == '0')
{
radix = 8;
str++;
if ((*str == 'x' || *str == 'X')
&& (str[1] == '.' || ISXDIGIT (str[1])))
{
radix = 16;
str++;
}
}
for (;;)
{
unsigned int c = *str++;
if (ISDIGIT (c) || (ISXDIGIT (c) && radix == 16))
{
c = hex_value (c);
if (c > max_digit)
max_digit = c;
}
else if (c == '.')
{
if (float_flag == NOT_FLOAT)
float_flag = AFTER_POINT;
else
SYNTAX_ERROR ("too many decimal points in number");
}
else if ((radix <= 10 && (c == 'e' || c == 'E'))
|| (radix == 16 && (c == 'p' || c == 'P')))
{
float_flag = AFTER_EXPON;
break;
}
else
{
str--;
break;
}
}
if (float_flag != NOT_FLOAT && radix == 8)
radix = 10;
if (max_digit >= radix)
SYNTAX_ERROR2 ("invalid digit \"%c\" in octal constant", '0' + max_digit);
if (float_flag != NOT_FLOAT)
{
if (radix == 16 && CPP_PEDANTIC (pfile) && !CPP_OPTION (pfile, c99))
cpp_error (pfile, CPP_DL_PEDWARN,
"use of C99 hexadecimal floating constant");
if (float_flag == AFTER_EXPON)
{
if (*str == '+' || *str == '-')
str++;
if (!ISDIGIT (*str))
SYNTAX_ERROR ("exponent has no digits");
do
str++;
while (ISDIGIT (*str));
}
else if (radix == 16)
SYNTAX_ERROR ("hexadecimal floating constants require an exponent");
result = interpret_float_suffix (str, limit - str);
if (result == 0)
{
cpp_error (pfile, CPP_DL_ERROR,
"invalid suffix \"%.*s\" on floating constant",
(int) (limit - str), str);
return CPP_N_INVALID;
}
if (limit != str
&& CPP_WTRADITIONAL (pfile)
&& ! cpp_sys_macro_p (pfile))
cpp_error (pfile, CPP_DL_WARNING,
"traditional C rejects the \"%.*s\" suffix",
(int) (limit - str), str);
result |= CPP_N_FLOATING;
}
else
{
result = interpret_int_suffix (str, limit - str);
if (result == 0)
{
cpp_error (pfile, CPP_DL_ERROR,
"invalid suffix \"%.*s\" on integer constant",
(int) (limit - str), str);
return CPP_N_INVALID;
}
if (CPP_WTRADITIONAL (pfile) && ! cpp_sys_macro_p (pfile))
{
int u_or_i = (result & (CPP_N_UNSIGNED|CPP_N_IMAGINARY));
int large = (result & CPP_N_WIDTH) == CPP_N_LARGE;
if (u_or_i || (large && CPP_OPTION (pfile, warn_long_long)))
cpp_error (pfile, CPP_DL_WARNING,
"traditional C rejects the \"%.*s\" suffix",
(int) (limit - str), str);
}
if ((result & CPP_N_WIDTH) == CPP_N_LARGE
&& ! CPP_OPTION (pfile, c99)
&& CPP_OPTION (pfile, warn_long_long))
cpp_error (pfile, CPP_DL_PEDWARN,
"use of C99 long long integer constant");
result |= CPP_N_INTEGER;
}
if ((result & CPP_N_IMAGINARY) && CPP_PEDANTIC (pfile))
cpp_error (pfile, CPP_DL_PEDWARN,
"imaginary constants are a GCC extension");
if (radix == 10)
result |= CPP_N_DECIMAL;
else if (radix == 16)
result |= CPP_N_HEX;
else
result |= CPP_N_OCTAL;
return result;
syntax_error:
return CPP_N_INVALID;
}
cpp_num
cpp_interpret_integer (cpp_reader *pfile, const cpp_token *token,
unsigned int type)
{
const uchar *p, *end;
cpp_num result;
result.low = 0;
result.high = 0;
result.unsignedp = !!(type & CPP_N_UNSIGNED);
result.overflow = false;
p = token->val.str.text;
end = p + token->val.str.len;
if (token->val.str.len == 1)
result.low = p[0] - '0';
else
{
cpp_num_part max;
size_t precision = CPP_OPTION (pfile, precision);
unsigned int base = 10, c = 0;
bool overflow = false;
if ((type & CPP_N_RADIX) == CPP_N_OCTAL)
{
base = 8;
p++;
}
else if ((type & CPP_N_RADIX) == CPP_N_HEX)
{
base = 16;
p += 2;
}
max = ~(cpp_num_part) 0;
if (precision < PART_PRECISION)
max >>= PART_PRECISION - precision;
max = (max - base + 1) / base + 1;
for (; p < end; p++)
{
c = *p;
if (ISDIGIT (c) || (base == 16 && ISXDIGIT (c)))
c = hex_value (c);
else
break;
if (result.low < max)
result.low = result.low * base + c;
else
{
result = append_digit (result, c, base, precision);
overflow |= result.overflow;
max = 0;
}
}
if (overflow)
cpp_error (pfile, CPP_DL_PEDWARN,
"integer constant is too large for its type");
else if (!result.unsignedp
&& !(CPP_OPTION (pfile, traditional)
&& pfile->state.in_directive)
&& !num_positive (result, precision))
{
if (base == 10)
cpp_error (pfile, CPP_DL_WARNING,
"integer constant is so large that it is unsigned");
result.unsignedp = true;
}
}
return result;
}
static cpp_num
append_digit (cpp_num num, int digit, int base, size_t precision)
{
cpp_num result;
unsigned int shift = 3 + (base == 16);
bool overflow;
cpp_num_part add_high, add_low;
overflow = !!(num.high >> (PART_PRECISION - shift));
result.high = num.high << shift;
result.low = num.low << shift;
result.high |= num.low >> (PART_PRECISION - shift);
result.unsignedp = num.unsignedp;
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;
result.overflow = overflow;
num.low = result.low;
num.high = result.high;
result = num_trim (result, precision);
if (!num_eq (result, num))
result.overflow = true;
return result;
}
static cpp_num
parse_defined (cpp_reader *pfile)
{
cpp_num result;
int paren = 0;
cpp_hashnode *node = 0;
const cpp_token *token;
cpp_context *initial_context = pfile->context;
pfile->state.prevent_expansion++;
token = cpp_get_token (pfile);
if (token->type == CPP_OPEN_PAREN)
{
paren = 1;
token = cpp_get_token (pfile);
}
if (token->type == CPP_NAME)
{
node = token->val.node;
if (paren && cpp_get_token (pfile)->type != CPP_CLOSE_PAREN)
{
cpp_error (pfile, CPP_DL_ERROR, "missing ')' after \"defined\"");
node = 0;
}
}
else
{
cpp_error (pfile, CPP_DL_ERROR,
"operator \"defined\" requires an identifier");
if (token->flags & NAMED_OP)
{
cpp_token op;
op.flags = 0;
op.type = token->type;
cpp_error (pfile, CPP_DL_ERROR,
"(\"%s\" is an alternative token for \"%s\" in C++)",
cpp_token_as_text (pfile, token),
cpp_token_as_text (pfile, &op));
}
}
if (node)
{
if (pfile->context != initial_context && CPP_PEDANTIC (pfile))
cpp_error (pfile, CPP_DL_WARNING,
"this use of \"defined\" may not be portable");
_cpp_mark_macro_used (node);
pfile->mi_ind_cmacro = node;
}
pfile->state.prevent_expansion--;
result.unsignedp = false;
result.high = 0;
result.overflow = false;
result.low = node && node->type == NT_MACRO;
return result;
}
static cpp_num
eval_token (cpp_reader *pfile, const cpp_token *token)
{
cpp_num result;
unsigned int temp;
int unsignedp = 0;
result.unsignedp = false;
result.overflow = false;
switch (token->type)
{
case CPP_NUMBER:
temp = cpp_classify_number (pfile, token);
switch (temp & CPP_N_CATEGORY)
{
case CPP_N_FLOATING:
cpp_error (pfile, CPP_DL_ERROR,
"floating constant in preprocessor expression");
break;
case CPP_N_INTEGER:
if (!(temp & CPP_N_IMAGINARY))
return cpp_interpret_integer (pfile, token, temp);
cpp_error (pfile, CPP_DL_ERROR,
"imaginary number in preprocessor expression");
break;
case CPP_N_INVALID:
break;
}
result.high = result.low = 0;
break;
case CPP_WCHAR:
case CPP_CHAR:
{
cppchar_t cc = cpp_interpret_charconst (pfile, token,
&temp, &unsignedp);
result.high = 0;
result.low = cc;
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;
case CPP_NAME:
if (token->val.node == pfile->spec_nodes.n_defined)
return parse_defined (pfile);
else if (CPP_OPTION (pfile, cplusplus)
&& (token->val.node == pfile->spec_nodes.n_true
|| token->val.node == pfile->spec_nodes.n_false))
{
result.high = 0;
result.low = (token->val.node == pfile->spec_nodes.n_true);
}
else
{
result.high = 0;
result.low = 0;
if (CPP_OPTION (pfile, warn_undef) && !pfile->state.skip_eval)
cpp_error (pfile, CPP_DL_WARNING, "\"%s\" is not defined",
NODE_NAME (token->val.node));
}
break;
default:
_cpp_test_assertion (pfile, &temp);
result.high = 0;
result.low = temp;
}
result.unsignedp = !!unsignedp;
return result;
}
#define NO_L_OPERAND (1 << 0)
#define LEFT_ASSOC (1 << 1)
#define CHECK_PROMOTION (1 << 2)
static const struct operator
{
uchar prio;
uchar flags;
} optab[] =
{
{0, 0},
{16, NO_L_OPERAND},
{12, LEFT_ASSOC | CHECK_PROMOTION},
{12, LEFT_ASSOC | CHECK_PROMOTION},
{14, LEFT_ASSOC | CHECK_PROMOTION},
{14, LEFT_ASSOC | CHECK_PROMOTION},
{15, LEFT_ASSOC | CHECK_PROMOTION},
{15, LEFT_ASSOC | CHECK_PROMOTION},
{15, LEFT_ASSOC | CHECK_PROMOTION},
{9, LEFT_ASSOC | CHECK_PROMOTION},
{7, LEFT_ASSOC | CHECK_PROMOTION},
{8, LEFT_ASSOC | CHECK_PROMOTION},
{13, LEFT_ASSOC},
{13, LEFT_ASSOC},
{10, LEFT_ASSOC | CHECK_PROMOTION},
{10, LEFT_ASSOC | CHECK_PROMOTION},
{16, NO_L_OPERAND},
{6, LEFT_ASSOC},
{5, LEFT_ASSOC},
{3, 0},
{4, LEFT_ASSOC | CHECK_PROMOTION},
{2, LEFT_ASSOC},
{1, NO_L_OPERAND},
{0, 0},
{0, 0},
{11, LEFT_ASSOC},
{11, LEFT_ASSOC},
{12, LEFT_ASSOC | CHECK_PROMOTION},
{12, LEFT_ASSOC | CHECK_PROMOTION},
{16, NO_L_OPERAND},
{16, NO_L_OPERAND}
};
bool
_cpp_parse_expr (cpp_reader *pfile)
{
struct op *top = pfile->op_stack;
unsigned int lex_count;
bool saw_leading_not, want_value = true;
pfile->state.skip_eval = 0;
pfile->mi_ind_cmacro = 0;
saw_leading_not = false;
lex_count = 0;
top->op = CPP_EOF;
for (;;)
{
struct op op;
lex_count++;
op.token = cpp_get_token (pfile);
op.op = op.token->type;
switch (op.op)
{
case CPP_NUMBER:
case CPP_CHAR:
case CPP_WCHAR:
case CPP_NAME:
case CPP_HASH:
if (!want_value)
SYNTAX_ERROR2 ("missing binary operator before token \"%s\"",
cpp_token_as_text (pfile, op.token));
want_value = false;
top->value = eval_token (pfile, op.token);
continue;
case CPP_NOT:
saw_leading_not = lex_count == 1;
break;
case CPP_PLUS:
if (want_value)
op.op = CPP_UPLUS;
break;
case CPP_MINUS:
if (want_value)
op.op = CPP_UMINUS;
break;
default:
if ((int) op.op <= (int) CPP_EQ || (int) op.op >= (int) CPP_PLUS_EQ)
SYNTAX_ERROR2 ("token \"%s\" is not valid in preprocessor expressions",
cpp_token_as_text (pfile, op.token));
break;
}
if (optab[op.op].flags & NO_L_OPERAND)
{
if (!want_value)
SYNTAX_ERROR2 ("missing binary operator before token \"%s\"",
cpp_token_as_text (pfile, op.token));
}
else if (want_value)
{
if (op.op == CPP_CLOSE_PAREN && top->op == CPP_OPEN_PAREN)
SYNTAX_ERROR ("missing expression between '(' and ')'");
if (op.op == CPP_EOF && top->op == CPP_EOF)
SYNTAX_ERROR ("#if with no expression");
if (top->op != CPP_EOF && top->op != CPP_OPEN_PAREN)
SYNTAX_ERROR2 ("operator '%s' has no right operand",
cpp_token_as_text (pfile, top->token));
else if (op.op == CPP_CLOSE_PAREN || op.op == CPP_EOF)
;
else
SYNTAX_ERROR2 ("operator '%s' has no left operand",
cpp_token_as_text (pfile, op.token));
}
top = reduce (pfile, top, op.op);
if (!top)
goto syntax_error;
if (op.op == CPP_EOF)
break;
switch (op.op)
{
case CPP_CLOSE_PAREN:
continue;
case CPP_OR_OR:
if (!num_zerop (top->value))
pfile->state.skip_eval++;
break;
case CPP_AND_AND:
case CPP_QUERY:
if (num_zerop (top->value))
pfile->state.skip_eval++;
break;
case CPP_COLON:
if (top->op != CPP_QUERY)
SYNTAX_ERROR (" ':' without preceding '?'");
if (!num_zerop (top[-1].value))
pfile->state.skip_eval++;
else
pfile->state.skip_eval--;
default:
break;
}
want_value = true;
if (++top == pfile->op_limit)
top = _cpp_expand_op_stack (pfile);
top->op = op.op;
top->token = op.token;
}
if (pfile->mi_ind_cmacro && !(saw_leading_not && lex_count == 3))
pfile->mi_ind_cmacro = 0;
if (top != pfile->op_stack)
{
cpp_error (pfile, CPP_DL_ICE, "unbalanced stack in #if");
syntax_error:
return false;
}
return !num_zerop (top->value);
}
static struct op *
reduce (cpp_reader *pfile, struct op *top, enum cpp_ttype op)
{
unsigned int prio;
if (top->op <= CPP_EQ || top->op > CPP_LAST_CPP_OP + 2)
{
bad_op:
cpp_error (pfile, CPP_DL_ICE, "impossible operator '%u'", top->op);
return 0;
}
if (op == CPP_OPEN_PAREN)
return top;
prio = optab[op].prio - ((optab[op].flags & LEFT_ASSOC) != 0);
while (prio < optab[top->op].prio)
{
if (CPP_OPTION (pfile, warn_num_sign_change)
&& optab[top->op].flags & CHECK_PROMOTION)
check_promotion (pfile, top);
switch (top->op)
{
case CPP_UPLUS:
case CPP_UMINUS:
case CPP_NOT:
case CPP_COMPL:
top[-1].value = num_unary_op (pfile, top->value, top->op);
break;
case CPP_PLUS:
case CPP_MINUS:
case CPP_RSHIFT:
case CPP_LSHIFT:
case CPP_MIN:
case CPP_MAX:
case CPP_COMMA:
top[-1].value = num_binary_op (pfile, top[-1].value,
top->value, top->op);
break;
case CPP_GREATER:
case CPP_LESS:
case CPP_GREATER_EQ:
case CPP_LESS_EQ:
top[-1].value
= num_inequality_op (pfile, top[-1].value, top->value, top->op);
break;
case CPP_EQ_EQ:
case CPP_NOT_EQ:
top[-1].value
= num_equality_op (pfile, top[-1].value, top->value, top->op);
break;
case CPP_AND:
case CPP_OR:
case CPP_XOR:
top[-1].value
= num_bitwise_op (pfile, top[-1].value, top->value, top->op);
break;
case CPP_MULT:
top[-1].value = num_mul (pfile, top[-1].value, top->value);
break;
case CPP_DIV:
case CPP_MOD:
top[-1].value = num_div_op (pfile, top[-1].value,
top->value, top->op);
break;
case CPP_OR_OR:
top--;
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;
continue;
case CPP_AND_AND:
top--;
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;
continue;
case CPP_OPEN_PAREN:
if (op != CPP_CLOSE_PAREN)
{
cpp_error (pfile, CPP_DL_ERROR, "missing ')' in expression");
return 0;
}
top--;
top->value = top[1].value;
return top;
case CPP_COLON:
top -= 2;
if (!num_zerop (top->value))
{
pfile->state.skip_eval--;
top->value = top[1].value;
}
else
top->value = top[2].value;
top->value.unsignedp = (top[1].value.unsignedp
|| top[2].value.unsignedp);
continue;
case CPP_QUERY:
cpp_error (pfile, CPP_DL_ERROR, "'?' without following ':'");
return 0;
default:
goto bad_op;
}
top--;
if (top->value.overflow && !pfile->state.skip_eval)
cpp_error (pfile, CPP_DL_PEDWARN,
"integer overflow in preprocessor expression");
}
if (op == CPP_CLOSE_PAREN)
{
cpp_error (pfile, CPP_DL_ERROR, "missing '(' in expression");
return 0;
}
return top;
}
struct op *
_cpp_expand_op_stack (cpp_reader *pfile)
{
size_t old_size = (size_t) (pfile->op_limit - pfile->op_stack);
size_t new_size = old_size * 2 + 20;
pfile->op_stack = xrealloc (pfile->op_stack, new_size * sizeof (struct op));
pfile->op_limit = pfile->op_stack + new_size;
return pfile->op_stack + old_size;
}
static void
check_promotion (cpp_reader *pfile, const struct op *op)
{
if (op->value.unsignedp == op[-1].value.unsignedp)
return;
if (op->value.unsignedp)
{
if (!num_positive (op[-1].value, CPP_OPTION (pfile, precision)))
cpp_error (pfile, CPP_DL_WARNING,
"the left operand of \"%s\" changes sign when promoted",
cpp_token_as_text (pfile, op->token));
}
else if (!num_positive (op->value, CPP_OPTION (pfile, precision)))
cpp_error (pfile, CPP_DL_WARNING,
"the right operand of \"%s\" changes sign when promoted",
cpp_token_as_text (pfile, op->token));
}
static cpp_num
num_trim (cpp_num num, size_t precision)
{
if (precision > PART_PRECISION)
{
precision -= PART_PRECISION;
if (precision < PART_PRECISION)
num.high &= ((cpp_num_part) 1 << precision) - 1;
}
else
{
if (precision < PART_PRECISION)
num.low &= ((cpp_num_part) 1 << precision) - 1;
num.high = 0;
}
return num;
}
static bool
num_positive (cpp_num num, size_t precision)
{
if (precision > PART_PRECISION)
{
precision -= PART_PRECISION;
return (num.high & (cpp_num_part) 1 << (precision - 1)) == 0;
}
return (num.low & (cpp_num_part) 1 << (precision - 1)) == 0;
}
cpp_num
cpp_num_sign_extend (cpp_num num, size_t precision)
{
if (!num.unsignedp)
{
if (precision > PART_PRECISION)
{
precision -= PART_PRECISION;
if (precision < PART_PRECISION
&& (num.high & (cpp_num_part) 1 << (precision - 1)))
num.high |= ~(~(cpp_num_part) 0 >> (PART_PRECISION - precision));
}
else if (num.low & (cpp_num_part) 1 << (precision - 1))
{
if (precision < PART_PRECISION)
num.low |= ~(~(cpp_num_part) 0 >> (PART_PRECISION - precision));
num.high = ~(cpp_num_part) 0;
}
}
return num;
}
static cpp_num
num_negate (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;
}
static bool
num_greater_eq (cpp_num pa, cpp_num pb, size_t precision)
{
bool unsignedp;
unsignedp = pa.unsignedp || pb.unsignedp;
if (!unsignedp)
{
unsignedp = num_positive (pa, precision);
if (unsignedp != num_positive (pb, precision))
return unsignedp;
}
return (pa.high > pb.high) || (pa.high == pb.high && pa.low >= pb.low);
}
static cpp_num
num_bitwise_op (cpp_reader *pfile ATTRIBUTE_UNUSED,
cpp_num lhs, cpp_num rhs, enum cpp_ttype op)
{
lhs.overflow = false;
lhs.unsignedp = lhs.unsignedp || rhs.unsignedp;
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;
}
static cpp_num
num_inequality_op (cpp_reader *pfile, cpp_num lhs, cpp_num 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
lhs.low = !gte || num_eq (lhs, rhs);
lhs.high = 0;
lhs.overflow = false;
lhs.unsignedp = false;
return lhs;
}
static cpp_num
num_equality_op (cpp_reader *pfile ATTRIBUTE_UNUSED,
cpp_num lhs, cpp_num rhs, enum cpp_ttype op)
{
bool eq = num_eq (lhs, rhs);
if (op == CPP_NOT_EQ)
eq = !eq;
lhs.low = eq;
lhs.high = 0;
lhs.overflow = false;
lhs.unsignedp = false;
return lhs;
}
static cpp_num
num_rshift (cpp_num num, size_t precision, size_t n)
{
cpp_num_part sign_mask;
bool x = num_positive (num, precision);
if (num.unsignedp || x)
sign_mask = 0;
else
sign_mask = ~(cpp_num_part) 0;
if (n >= precision)
num.high = num.low = sign_mask;
else
{
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;
}
static cpp_num
num_lshift (cpp_num num, size_t precision, size_t 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;
}
static cpp_num
num_unary_op (cpp_reader *pfile, cpp_num num, enum cpp_ttype op)
{
switch (op)
{
case CPP_UPLUS:
if (CPP_WTRADITIONAL (pfile) && !pfile->state.skip_eval)
cpp_error (pfile, CPP_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:
num.low = num_zerop (num);
num.high = 0;
num.overflow = false;
num.unsignedp = false;
break;
}
return num;
}
static cpp_num
num_binary_op (cpp_reader *pfile, cpp_num lhs, cpp_num rhs, enum cpp_ttype op)
{
cpp_num result;
size_t precision = CPP_OPTION (pfile, precision);
bool gte;
size_t n;
switch (op)
{
case CPP_LSHIFT:
case CPP_RSHIFT:
if (!rhs.unsignedp && !num_positive (rhs, precision))
{
if (op == CPP_LSHIFT)
op = CPP_RSHIFT;
else
op = CPP_LSHIFT;
rhs = num_negate (rhs, precision);
}
if (rhs.high)
n = ~0;
else
n = rhs.low;
if (op == CPP_LSHIFT)
lhs = num_lshift (lhs, precision, n);
else
lhs = num_rshift (lhs, precision, n);
break;
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;
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.unsignedp = lhs.unsignedp || rhs.unsignedp;
result.overflow = false;
result = num_trim (result, precision);
if (!result.unsignedp)
{
bool lhsp = num_positive (lhs, precision);
result.overflow = (lhsp == num_positive (rhs, precision)
&& lhsp != num_positive (result, precision));
}
return result;
default:
if (CPP_PEDANTIC (pfile) && (!CPP_OPTION (pfile, c99)
|| !pfile->state.skip_eval))
cpp_error (pfile, CPP_DL_PEDWARN,
"comma operator in operand of #if");
lhs = rhs;
break;
}
return lhs;
}
static cpp_num
num_part_mul (cpp_num_part lhs, cpp_num_part 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]);
result.unsignedp = true;
result.overflow = false;
return result;
}
static cpp_num
num_mul (cpp_reader *pfile, cpp_num lhs, cpp_num rhs)
{
cpp_num result, temp;
bool unsignedp = lhs.unsignedp || rhs.unsignedp;
bool overflow, negate = false;
size_t precision = CPP_OPTION (pfile, precision);
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;
}
static cpp_num
num_div_op (cpp_reader *pfile, cpp_num lhs, cpp_num 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);
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);
}
if (rhs.high)
{
i = precision - 1;
mask = (cpp_num_part) 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 = (cpp_num_part) 1 << i;
for (; ; i--, mask >>= 1)
if (rhs.low & mask)
break;
}
else
{
if (!pfile->state.skip_eval)
cpp_error (pfile, CPP_DL_ERROR, "division by zero in #if");
return lhs;
}
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 |= (cpp_num_part) 1 << (i - PART_PRECISION);
else
result.low |= (cpp_num_part) 1 << i;
}
if (i-- == 0)
break;
sub.low = (sub.low >> 1) | (sub.high << (PART_PRECISION - 1));
sub.high >>= 1;
}
if (op == CPP_DIV)
{
result.unsignedp = unsignedp;
result.overflow = false;
if (!unsignedp)
{
if (negate)
result = num_negate (result, precision);
result.overflow = num_positive (result, precision) ^ !negate;
}
return result;
}
lhs.unsignedp = unsignedp;
lhs.overflow = false;
if (lhs_neg)
lhs = num_negate (lhs, precision);
return lhs;
}