root/usr/src/tools/smatch/src/smatch_sval.c
/*
 * Copyright (C) 2012 Oracle.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, see http://www.gnu.org/copyleft/gpl.txt
 */

/*
 * Basically the point of sval is that it can hold both ULLONG_MAX and
 * LLONG_MIN.  If it is an unsigned type then we use sval.uvalue or if it is
 * signed we use sval.value.
 *
 * I considered just using one bit to store whether the value was signed vs
 * unsigned but I think it might help to have the type information so we know
 * how to do type promotion.
 *
 */

#include "smatch.h"
#include "smatch_slist.h"
#include "smatch_extra.h"

__ALLOCATOR(sval_t, "svals", sval);

sval_t *sval_alloc(sval_t sval)
{
        sval_t *ret;

        ret = __alloc_sval(0);
        *ret = sval;
        return ret;
}

sval_t *sval_alloc_permanent(sval_t sval)
{
        sval_t *ret;

        ret = malloc(sizeof(*ret));
        *ret = sval;
        return ret;
}

sval_t sval_blank(struct expression *expr)
{
        sval_t ret;

        ret.type = get_type(expr);
        if (!ret.type)
                ret.type = &int_ctype;
        ret.value = 123456789;

        return ret;
}

sval_t sval_type_val(struct symbol *type, long long val)
{
        sval_t ret;

        if (!type)
                type = &llong_ctype;

        ret.type = type;
        ret.value = val;
        return ret;
}

sval_t sval_type_fval(struct symbol *type, long double fval)
{
        sval_t ret;

        ret.type = &ldouble_ctype;
        ret.ldvalue = fval;
        return sval_cast(type, ret);
}

sval_t sval_from_val(struct expression *expr, long long val)
{
        sval_t ret;

        ret = sval_blank(expr);
        ret.value = val;
        ret = sval_cast(get_type(expr), ret);

        return ret;
}

sval_t sval_from_fval(struct expression *expr, long double fval)
{
        sval_t ret;

        ret.type = &ldouble_ctype;
        ret.ldvalue = fval;
        ret = sval_cast(get_type(expr), ret);

        return ret;
}

int sval_is_ptr(sval_t sval)
{
        if (!sval.type)
                return 0;
        return (sval.type->type == SYM_PTR || sval.type->type == SYM_ARRAY);
}

bool sval_is_fp(sval_t sval)
{
        return type_is_fp(sval.type);
}

int sval_unsigned(sval_t sval)
{
        if (is_ptr_type(sval.type))
                return true;
        return type_unsigned(sval.type);
}

int sval_signed(sval_t sval)
{
        return !type_unsigned(sval.type);
}

int sval_bits(sval_t sval)
{
        return type_bits(sval.type);
}

int sval_bits_used(sval_t sval)
{
        int i;

        for (i = 64; i >= 1; i--) {
                if (sval.uvalue & (1ULL << (i - 1)))
                        return i;
        }
        return 0;
}

int sval_is_negative(sval_t sval)
{
        if (type_unsigned(sval.type))
                return 0;
        if (sval.value < 0)
                return 1;
        return 0;
}

int sval_is_positive(sval_t sval)
{
        return !sval_is_negative(sval);
}

static bool fp_is_min(sval_t sval)
{
        if (sval.type == &float_ctype)
                return sval.fvalue == -FLT_MAX;
        if (sval.type == &double_ctype)
                return sval.dvalue == -DBL_MAX;
        if (sval.type == &ldouble_ctype)
                return sval.ldvalue == -LDBL_MAX;
        sm_perror("%s: bad type: '%s'", __func__, type_to_str(sval.type));
        return false;
}

int sval_is_min(sval_t sval)
{
        sval_t min = sval_type_min(sval.type);

        if (sval_is_fp(sval))
                return fp_is_min(sval);

        if (sval_unsigned(sval)) {
                if (sval.uvalue == 0)
                        return 1;
                return 0;
        }
        /* return true for less than min as well */
        return (sval.value <= min.value);
}

static bool fp_is_max(sval_t sval)
{
        if (sval.type == &float_ctype)
                return sval.fvalue == FLT_MAX;
        if (sval.type == &double_ctype)
                return sval.dvalue == DBL_MAX;
        if (sval.type == &ldouble_ctype)
                return sval.ldvalue == LDBL_MAX;
        sm_perror("%s: bad type: '%s'", __func__, type_to_str(sval.type));
        return false;
}

int sval_is_max(sval_t sval)
{
        sval_t max = sval_type_max(sval.type);

        if (sval_is_fp(sval))
                return fp_is_max(sval);

        if (sval_unsigned(sval))
                return (sval.uvalue >= max.value);
        return (sval.value >= max.value);
}

int sval_is_a_min(sval_t sval)
{
        if (sval_is_min(sval))
                return 1;

        if (sval_is_fp(sval))
                return 0;

        if (sval_signed(sval) && sval.value == SHRT_MIN)
                return 1;
        if (sval_signed(sval) && sval.value == INT_MIN)
                return 1;
        if (sval_signed(sval) && sval.value == LLONG_MIN)
                return 1;
        return 0;
}

int sval_is_a_max(sval_t sval)
{
        if (sval_is_max(sval))
                return 1;

        if (sval_is_fp(sval))
                return 0;

        if (sval.uvalue == SHRT_MAX)
                return 1;
        if (sval.uvalue == INT_MAX)
                return 1;
        if (sval.uvalue == LLONG_MAX)
                return 1;
        if (sval.uvalue == USHRT_MAX)
                return 1;
        if (sval.uvalue == UINT_MAX)
                return 1;
        if (sval_unsigned(sval) && sval.uvalue == ULLONG_MAX)
                return 1;
        if (sval.value > valid_ptr_max - 1000 &&
            sval.value < valid_ptr_max + 1000)
                return 1;
        return 0;
}

int sval_is_negative_min(sval_t sval)
{
        if (sval_is_fp(sval))
                return 0;

        if (!sval_is_negative(sval))
                return 0;
        return sval_is_min(sval);
}

int sval_cmp_t(struct symbol *type, sval_t one, sval_t two)
{
        sval_t one_cast, two_cast;

        one_cast = sval_cast(type, one);
        two_cast = sval_cast(type, two);
        return sval_cmp(one_cast, two_cast);
}

int sval_cmp_val(sval_t one, long long val)
{
        sval_t sval;

        sval = sval_type_val(&llong_ctype, val);
        return sval_cmp(one, sval);
}

sval_t sval_min(sval_t one, sval_t two)
{
        if (sval_cmp(one, two) > 0)
                return two;
        return one;
}

sval_t sval_max(sval_t one, sval_t two)
{
        if (sval_cmp(one, two) < 0)
                return two;
        return one;
}

int sval_too_low(struct symbol *type, sval_t sval)
{
        if (sval_is_negative(sval) && type_unsigned(type))
                return 1;
        if (type_signed(type) && sval_unsigned(sval))
                return 0;
        if (type_signed(sval.type) &&
            sval.value < sval_type_min(type).value)
                return 1;
        if (sval_cmp(sval, sval_type_min(type)) < 0)
                return 1;
        return 0;
}

int sval_too_high(struct symbol *type, sval_t sval)
{
        if (sval_is_negative(sval))
                return 0;
        if (sval.uvalue > sval_type_max(type).uvalue)
                return 1;
        return 0;
}

int sval_fits(struct symbol *type, sval_t sval)
{
        /* everything fits into floating point */
        if (type_is_fp(type))
                return 1;
        /* floating points don't fit into int */
        if (type_is_fp(sval.type))
                return 0;

        if (sval_too_low(type, sval))
                return 0;
        if (sval_too_high(type, sval))
                return 0;
        return 1;
}

static sval_t cast_to_fp(struct symbol *type, sval_t sval)
{
        sval_t ret = {};

        ret.type = type;
        if (type == &float_ctype) {
                if (!sval_is_fp(sval)) {
                        if (sval_unsigned(sval))
                                ret.fvalue = sval.uvalue;
                        else
                                ret.fvalue = sval.value;
                } else if (sval.type == &float_ctype)
                        ret.fvalue = sval.fvalue;
                else if (sval.type == &double_ctype)
                        ret.fvalue = sval.dvalue;
                else
                        ret.fvalue = sval.ldvalue;
        } else if (type == &double_ctype) {
                if (!sval_is_fp(sval)) {
                        if (sval_unsigned(sval))
                                ret.dvalue = sval.uvalue;
                        else
                                ret.dvalue = sval.value;
                } else if (sval.type == &float_ctype)
                        ret.dvalue = sval.fvalue;
                else if (sval.type == &double_ctype)
                        ret.dvalue = sval.dvalue;
                else
                        ret.dvalue = sval.ldvalue;
        } else if (type == &ldouble_ctype) {
                if (!sval_is_fp(sval)) {
                        if (sval_unsigned(sval))
                                ret.ldvalue = (long double)sval.uvalue;
                        else
                                ret.ldvalue = (long double)sval.value;
                } else if (sval.type == &float_ctype)
                        ret.ldvalue = sval.fvalue;
                else if (sval.type == &double_ctype)
                        ret.ldvalue = sval.dvalue;
                else
                        ret.ldvalue = sval.ldvalue;
        } else {
                sm_perror("%s: bad type: %s", __func__, type_to_str(type));
        }

        return ret;
}

static sval_t cast_from_fp(struct symbol *type, sval_t sval)
{
        sval_t ret = {};

        ret.type = &llong_ctype;
        if (sval.type == &float_ctype)
                ret.value = sval.fvalue;
        else if (sval.type == &double_ctype)
                ret.value = sval.dvalue;
        else if (sval.type == &ldouble_ctype)
                ret.value = sval.ldvalue;
        else
                sm_perror("%s: bad type: %s", __func__, type_to_str(type));

        return sval_cast(type, ret);
}

sval_t sval_cast(struct symbol *type, sval_t sval)
{
        sval_t ret;

        if (!type)
                type = &int_ctype;

        if (type_is_fp(type))
                return cast_to_fp(type, sval);
        if (type_is_fp(sval.type))
                return cast_from_fp(type, sval);

        ret.type = type;
        switch (sval_bits(ret)) {
        case 1:
                ret.value = !!sval.value;
                break;
        case 8:
                if (sval_unsigned(ret))
                        ret.value = (long long)(unsigned char)sval.value;
                else
                        ret.value = (long long)(char)sval.value;
                break;
        case 16:
                if (sval_unsigned(ret))
                        ret.value = (long long)(unsigned short)sval.value;
                else
                        ret.value = (long long)(short)sval.value;
                break;
        case 32:
                if (sval_unsigned(ret))
                        ret.value = (long long)(unsigned int)sval.value;
                else
                        ret.value = (long long)(int)sval.value;
                break;
        default:
                ret.value = sval.value;
        }
        return ret;

}

sval_t sval_preop(sval_t sval, int op)
{
        switch (op) {
        case '!':
                sval.value = !sval.value;
                break;
        case '~':
                sval.value = ~sval.value;
                sval = sval_cast(sval.type, sval);
                break;
        case '-':
                sval.value = -sval.value;
                sval = sval_cast(sval.type, sval);
                break;
        }
        return sval;
}

static sval_t sval_binop_unsigned(struct symbol *type, sval_t left, int op, sval_t right)
{
        sval_t ret;

        ret.type = type;
        switch (op) {
        case '*':
                ret.uvalue =  left.uvalue * right.uvalue;
                break;
        case '/':
                if (right.uvalue == 0) {
                        sm_debug("%s: divide by zero", __func__);
                        ret.uvalue = 123456789;
                } else {
                        ret.uvalue = left.uvalue / right.uvalue;
                }
                break;
        case '+':
                ret.uvalue = left.uvalue + right.uvalue;
                break;
        case '-':
                ret.uvalue = left.uvalue - right.uvalue;
                break;
        case '%':
                if (right.uvalue == 0) {
                        sm_perror(" %s: MOD by zero", __func__);
                        ret.uvalue = 123456789;
                } else {
                        ret.uvalue = left.uvalue % right.uvalue;
                }
                break;
        case '|':
                ret.uvalue = left.uvalue | right.uvalue;
                break;
        case '&':
                ret.uvalue = left.uvalue & right.uvalue;
                break;
        case SPECIAL_RIGHTSHIFT:
                ret.uvalue = left.uvalue >> right.uvalue;
                break;
        case SPECIAL_LEFTSHIFT:
                ret.uvalue = left.uvalue << right.uvalue;
                break;
        case '^':
                ret.uvalue = left.uvalue ^ right.uvalue;
                break;
        default:
                sm_perror(" %s: unhandled binop %s", __func__,
                       show_special(op));
                ret.uvalue = 1234567;
        }
        return ret;
}


static sval_t sval_binop_signed(struct symbol *type, sval_t left, int op, sval_t right)
{
        sval_t ret;

        ret.type = type;
        switch (op) {
        case '*':
                ret.value =  left.value * right.value;
                break;
        case '/':
                if (right.value == 0) {
                        sm_debug("%s: divide by zero", __func__);
                        ret.value = 123456789;
                } else if (left.value == LLONG_MIN && right.value == -1) {
                        sm_debug("%s: invalid divide LLONG_MIN/-1", __func__);
                        ret.value = 12345678;
                } else {
                        ret.value = left.value / right.value;
                }
                break;
        case '+':
                ret.value = left.value + right.value;
                break;
        case '-':
                ret.value = left.value - right.value;
                break;
        case '%':
                if (right.value == 0) {
                        sm_perror(" %s: MOD by zero", __func__);
                        ret.value = 123456789;
                } else {
                        ret.value = left.value % right.value;
                }
                break;
        case '|':
                ret.value = left.value | right.value;
                break;
        case '&':
                ret.value = left.value & right.value;
                break;
        case SPECIAL_RIGHTSHIFT:
                ret.value = left.value >> right.value;
                break;
        case SPECIAL_LEFTSHIFT:
                ret.value = left.value << right.value;
                break;
        case '^':
                ret.value = left.value ^ right.value;
                break;
        default:
                sm_perror(" %s: unhandled binop %s", __func__,
                       show_special(op));
                ret.value = 1234567;
        }
        return ret;
}

static sval_t ptr_binop(struct symbol *type, sval_t left, int op, sval_t right)
{
        sval_t ret;
        int align;

        if (op != '+' && op != '-')
                return sval_binop_unsigned(type, left, op, right);

        ret.type = type;
        if (type->type == SYM_PTR)
                type = get_real_base_type(type);
        align = type->ctype.alignment;
        if (align <= 0)
                align = 1;

        if (op == '+') {
                if (type_is_ptr(left.type))
                        ret.value = left.value + right.value * align;
                else
                        ret.value = left.value * align + right.value;
        } else {
                if (!type_is_ptr(left.type)) {
                        left.value = -left.value;
                        ret = ptr_binop(type, left, '+', right);
                } else if (!type_is_ptr(right.type)) {
                        right.value = -right.value;
                        ret = ptr_binop(type, left, '+', right);
                } else {
                        ret.value = (left.value - right.value) / align;
                }
        }

        if (op == '-')
                ret.type = ssize_t_ctype;
        return ret;
}

sval_t sval_binop(sval_t left, int op, sval_t right)
{
        struct symbol *type;
        sval_t ret;

        type = get_promoted_type(left.type, right.type);

        if (type_is_ptr(type))
                ret = ptr_binop(type, left, op, right);
        else if (type_unsigned(type))
                ret = sval_binop_unsigned(type, left, op, right);
        else
                ret = sval_binop_signed(type, left, op, right);
        return sval_cast(type, ret);
}

int sval_unop_overflows(sval_t sval, int op)
{
        if (op != '-')
                return 0;
        if (sval_positive_bits(sval) == 32 && sval.value == INT_MIN)
                return 1;
        if (sval_positive_bits(sval) == 64 && sval.value == LLONG_MIN)
                return 1;
        if (sval_is_negative(sval))
                return 0;
        if (sval_signed(sval))
                return 0;
        if (sval_bits(sval) == 32 && sval.uvalue > INT_MAX)
                return 1;
        if (sval_bits(sval) == 64 && sval.uvalue > LLONG_MAX)
                return 1;
        return 0;
}

int sval_binop_overflows(sval_t left, int op, sval_t right)
{
        struct symbol *type;
        sval_t max, min;

        type = left.type;
        if (type_positive_bits(right.type) > type_positive_bits(left.type))
                type = right.type;
        if (type_positive_bits(type) < 31)
                type = &int_ctype;

        max = sval_type_max(type);
        min = sval_type_min(type);

        switch (op) {
        case '+':
                if (sval_is_negative(left) && sval_is_negative(right)) {
                        if (left.value < min.value + right.value)
                                return 1;
                        return 0;
                }
                if (sval_is_negative(left) || sval_is_negative(right))
                        return 0;
                if (left.uvalue > max.uvalue - right.uvalue)
                                return 1;
                return 0;
        case '*':
                if (type_signed(type)) {
                        if (left.value == 0 || right.value == 0)
                                return 0;
                        if (left.value > max.value / right.value)
                                return 1;
                        if (left.value == -1 || right.value == -1)
                                return 0;
                        return left.value != left.value * right.value / right.value;

                }
                return right.uvalue != 0 && left.uvalue > max.uvalue / right.uvalue;
        case '-':
                if (type_unsigned(type)) {
                        if (sval_cmp(left, right) < 0)
                                return 1;
                        return 0;
                }
                if (sval_is_negative(left) && sval_is_negative(right))
                        return 0;

                if (sval_is_negative(left)) {
                        if (left.value < min.value + right.value)
                                return 1;
                        return 0;
                }
                if (sval_is_negative(right)) {
                        if (right.value == min.value)
                                return 1;
                        right = sval_preop(right, '-');
                        if (sval_binop_overflows(left, '+', right))
                                return 1;
                        return 0;
                }
                return 0;
        case SPECIAL_LEFTSHIFT:
                if (sval_cmp(left, sval_binop(max, invert_op(op), right)) > 0)
                        return 1;
                return 0;
        }
        return 0;
}

int sval_binop_overflows_no_sign(sval_t left, int op, sval_t right)
{

        struct symbol *type;

        type = left.type;
        if (type_positive_bits(right.type) > type_positive_bits(left.type))
                type = right.type;
        if (type_positive_bits(type) <= 31)
                type = &uint_ctype;
        else
                type = &ullong_ctype;

        left = sval_cast(type, left);
        right = sval_cast(type, right);
        return sval_binop_overflows(left, op, right);
}

int find_first_zero_bit(unsigned long long uvalue)
{
        int i;

        for (i = 0; i < 64; i++) {
                if (!(uvalue & (1ULL << i)))
                        return i;
        }
        return i;
}

int sm_fls64(unsigned long long uvalue)
{
        int high_bit = 0;

        while (uvalue) {
                uvalue >>= 1;
                high_bit++;
        }

        return high_bit;
}

unsigned long long fls_mask(unsigned long long uvalue)
{
        int high_bit = 0;

        high_bit = sm_fls64(uvalue);
        if (high_bit == 0)
                return 0;

        return ((unsigned long long)-1) >> (64 - high_bit);
}

unsigned long long sval_fls_mask(sval_t sval)
{
        return fls_mask(sval.uvalue);
}

static char *fp_to_str(sval_t sval)
{
        char buf[32];

        if (sval.type == &float_ctype)
                snprintf(buf, sizeof(buf), "%f", sval.fvalue);
        else if (sval.type == &double_ctype)
                snprintf(buf, sizeof(buf), "%e", sval.dvalue);
        else if (sval.type == &ldouble_ctype) {
                snprintf(buf, sizeof(buf), "%Lf", sval.ldvalue);
        } else
                snprintf(buf, sizeof(buf), "nan");

        return alloc_sname(buf);
}

const char *sval_to_str(sval_t sval)
{
        char buf[32];

        if (sval_is_fp(sval))
                return fp_to_str(sval);

        if (sval_is_ptr(sval) && sval.value == valid_ptr_max)
                return "ptr_max";
        if (sval_unsigned(sval) && sval.value == ULLONG_MAX)
                return "u64max";
        if (sval_unsigned(sval) && sval.value == UINT_MAX)
                return "u32max";
        if (sval.value == USHRT_MAX)
                return "u16max";

        if (sval_signed(sval) && sval.value == LLONG_MAX)
                return "s64max";
        if (sval.value == INT_MAX)
                return "s32max";
        if (sval.value == SHRT_MAX)
                return "s16max";

        if (sval_signed(sval) && sval.value == SHRT_MIN)
                return "s16min";
        if (sval_signed(sval) && sval.value == INT_MIN)
                return "s32min";
        if (sval_signed(sval) && sval.value == LLONG_MIN)
                return "s64min";

        if (sval_unsigned(sval))
                snprintf(buf, sizeof(buf), "%llu", sval.value);
        else if (sval.value < 0)
                snprintf(buf, sizeof(buf), "(%lld)", sval.value);
        else
                snprintf(buf, sizeof(buf), "%lld", sval.value);

        return alloc_sname(buf);
}

const char *sval_to_str_or_err_ptr(sval_t sval)
{
        char buf[12];

        if (option_project != PROJ_KERNEL ||
            !is_ptr_type(sval.type))
                return sval_to_str(sval);

        if (!sval_is_fp(sval) && sval.uvalue >= -4905ULL) {
                snprintf(buf, sizeof(buf), "(%lld)", sval.value);
                return alloc_sname(buf);
        }

        return sval_to_str(sval);
}

const char *sval_to_numstr(sval_t sval)
{
        char buf[30];

        if (type_is_fp(sval.type))
                return fp_to_str(sval);

        if (sval_unsigned(sval))
                snprintf(buf, sizeof(buf), "%llu", sval.value);
        else if (sval.value < 0)
                snprintf(buf, sizeof(buf), "(%lld)", sval.value);
        else
                snprintf(buf, sizeof(buf), "%lld", sval.value);

        return alloc_sname(buf);
}

sval_t ll_to_sval(long long val)
{
        sval_t ret;

        ret.type = &llong_ctype;
        ret.value = val;
        return ret;
}

static void free_svals(struct symbol *sym)
{
        if (__inline_fn)
                return;
        clear_sval_alloc();
}

void register_sval(int my_id)
{
        add_hook(&free_svals, AFTER_FUNC_HOOK);
}