/*

  $Id$

  Copyright 1989-2016 MINES ParisTech

  This file is part of PIPS.

  PIPS 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 3 of the License, or
  any later version.

  PIPS 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 PIPS.  If not, see <http://www.gnu.org/licenses/>.

*/
#ifdef HAVE_CONFIG_H
    #include "pips_config.h"
#endif
#include "genC.h"
#include "linear.h"
#include "ri.h"
#include "effects.h"

#include "resources.h"

#include "misc.h"
#include "ri-util.h"
#include "effects-util.h"

#include "sac.h"
#include "patterns.h"
#include "pipsdbm.h"

static operator_id_tree mappings = NULL;
void set_simd_operator_mappings(void * m)
{
    pips_assert("not already set",mappings==NULL);
    mappings=m;
}
void reset_simd_operator_mappings()
{
    pips_assert("already set",mappings);
    mappings=NULL;
}

/*
 * we manipulate tokens from
 * either the `ri'
 * or from the pattern lexer
 * binding is done here
 */

typedef struct {
    char* name;
    int id;
} oper_id_mapping;

static oper_id_mapping operators[] =
{
    { ASSIGN_OPERATOR_NAME,            ASSIGN_OPERATOR_TOK },
    { PLUS_OPERATOR_NAME,              PLUS_OPERATOR_TOK },
    { PLUS_C_OPERATOR_NAME,            PLUS_OPERATOR_TOK },
    { MINUS_OPERATOR_NAME,             MINUS_OPERATOR_TOK },
    { COS_OPERATOR_NAME,               COS_OPERATOR_TOK },
    { SIN_OPERATOR_NAME,               SIN_OPERATOR_TOK },
    { UNARY_MINUS_OPERATOR_NAME,       UNARY_MINUS_OPERATOR_TOK },
    { MULTIPLY_OPERATOR_NAME,          MULTIPLY_OPERATOR_TOK },
    { DIVIDE_OPERATOR_NAME,            DIVIDE_OPERATOR_TOK },
    { INVERSE_OPERATOR_NAME,           INVERSE_OPERATOR_TOK },
    { POWER_OPERATOR_NAME,             POWER_OPERATOR_TOK },
    { MODULO_OPERATOR_NAME,            MODULO_OPERATOR_TOK },
    { MIN_OPERATOR_NAME,               MIN_OPERATOR_TOK },
    { MIN0_OPERATOR_NAME,              MIN0_OPERATOR_TOK },
    { AMIN1_OPERATOR_NAME,             AMIN1_OPERATOR_TOK },
    { DMIN1_OPERATOR_NAME,             DMIN1_OPERATOR_TOK },
    { MAX_OPERATOR_NAME,               MAX_OPERATOR_TOK },
    { MAX0_OPERATOR_NAME,              MAX0_OPERATOR_TOK },
    { AMAX1_OPERATOR_NAME,             AMAX1_OPERATOR_TOK },
    { DMAX1_OPERATOR_NAME,             DMAX1_OPERATOR_TOK },
    { ABS_OPERATOR_NAME,               ABS_OPERATOR_TOK },
    { IABS_OPERATOR_NAME,              IABS_OPERATOR_TOK },
    { DABS_OPERATOR_NAME,              DABS_OPERATOR_TOK },
    { CABS_OPERATOR_NAME,              CABS_OPERATOR_TOK },

    { AND_OPERATOR_NAME,               AND_OPERATOR_TOK },
    { OR_OPERATOR_NAME,                OR_OPERATOR_TOK },
    { NOT_OPERATOR_NAME,               NOT_OPERATOR_TOK },
    { NON_EQUAL_OPERATOR_NAME,         NON_EQUAL_OPERATOR_TOK },
    { EQUIV_OPERATOR_NAME,             EQUIV_OPERATOR_TOK },
    { NON_EQUIV_OPERATOR_NAME,         NON_EQUIV_OPERATOR_TOK },

    { TRUE_OPERATOR_NAME,              TRUE_OPERATOR_TOK },
    { FALSE_OPERATOR_NAME,             FALSE_OPERATOR_TOK },

    { C_GREATER_OR_EQUAL_OPERATOR_NAME,  GREATER_OR_EQUAL_OPERATOR_TOK },
    { C_GREATER_THAN_OPERATOR_NAME,      GREATER_THAN_OPERATOR_TOK },
    { C_LESS_OR_EQUAL_OPERATOR_NAME,     LESS_OR_EQUAL_OPERATOR_TOK },
    { C_LESS_THAN_OPERATOR_NAME,         LESS_THAN_OPERATOR_TOK },
    { C_EQUAL_OPERATOR_NAME,             EQUAL_OPERATOR_TOK },
    { CONDITIONAL_OPERATOR_NAME,         PHI_TOK },

    { "__PIPS_SAC_MULADD",         	 MULADD_OPERATOR_TOK },

    { NULL,                              UNKNOWN_TOK }
};


static void insert_mapping(oper_id_mapping* item)
{
    char * s;
    operator_id_tree t;

    t = mappings;
    for(s = item->name; *s != 0; s++)
    {
        operator_id_tree next;
        intptr_t c = *s;

        next = (operator_id_tree)hash_get(operator_id_tree_sons(t), (void*)c);
        if (next == HASH_UNDEFINED_VALUE)
        {
            next = make_operator_id_tree(UNKNOWN_TOK,hash_table_make(hash_int,HASH_DEFAULT_SIZE));
            hash_put(operator_id_tree_sons(t), (void *)c, (void*)next);
        }

        t = next;
    }

    if (operator_id_tree_id(t) != UNKNOWN_TOK)
        pips_user_warning("overwriting previous mapping...\n");

    operator_id_tree_id(t) = item->id;
}

static int do_get_operator_id(const char* ename)
{
    operator_id_tree t = mappings;
    for(const char *s = ename; *s != 0; s++)
    {
        operator_id_tree next;
        intptr_t c = *s;

        next = (operator_id_tree)hash_get(operator_id_tree_sons(t), (void*)c);
        if (next == HASH_UNDEFINED_VALUE)
        {
            return UNKNOWN_TOK;
        }
        t = next;
    }
    return operator_id_tree_id(t);
}

int get_operator_id(entity e)
{
    const char* ename = entity_local_name(e);
    int res = do_get_operator_id(ename);
    if(res == UNKNOWN_TOK )
    {
        /* retry with uppercase version, cos -> COS :) */
        ename = strupper(strdup(ename),ename);
        res = do_get_operator_id(ename);
    }
    return res;
}

bool simd_operator_mappings(const string module_name)
{
  pips_assert("true", module_name==module_name);
    /* create a new operator id */
    operator_id_tree m= make_operator_id_tree(UNKNOWN_TOK,hash_table_make(hash_int,HASH_DEFAULT_SIZE));
    set_simd_operator_mappings(m);

    for(size_t i=0; operators[i].name != NULL; i++)
        insert_mapping(&operators[i]);

    /* put it in pipsdbm */
    DB_PUT_MEMORY_RESOURCE(DBR_SIMD_OPERATOR_MAPPINGS,"",m);

    reset_simd_operator_mappings();
    return true;
}

