/*************************************************************************
*									 *
*	 Extension for YAP Prolog 					 *
*									 *
* Copyright G.S.Oliveira, A.F.Silva and Universidade Estadual de Maringá *
*									 *
*	Yap Prolog was developed at NCCUP - Universidade do Porto	 *
* Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997	 *
*									 *
**************************************************************************
*									 *
* File:		jit_codegenpreds.c					 *
* comments:	JIT Compiler Codegen Options predicates			 *
*									 *
* Last rev:     2013-10-18                               		 *
*************************************************************************/

#include "jit_predicates.hpp"

/* Predicates for LLVM Target Options configuration */
static Int  p_enable_framepointer_elimination( USES_REGS1 );
static Int  p_more_precise_fp_mad_option( USES_REGS1 );
static Int  p_excess_fp_precision( USES_REGS1 );
static Int  p_safe_fp_math( USES_REGS1 );
static Int  p_rounding_mode_not_changed( USES_REGS1 );
static Int  p_no_use_soft_float( USES_REGS1 );
static Int  p_disable_jit_exception_handling( USES_REGS1 );
static Int  p_disable_jit_emit_debug_info( USES_REGS1 );
static Int  p_disable_jit_emit_debug_info_to_disk( USES_REGS1 );
static Int  p_no_guaranteed_tail_call_opt( USES_REGS1 );
static Int  p_enable_tail_calls( USES_REGS1 );
static Int  p_disable_fast_isel( USES_REGS1 );
static Int  p_disable_framepointer_elimination( USES_REGS1 );
static Int  p_less_precise_fp_mad_option( USES_REGS1 );
static Int  p_no_excess_fp_precision( USES_REGS1 );
static Int  p_unsafe_fp_math( USES_REGS1 );
static Int  p_rounding_mode_dynamically_changed( USES_REGS1 );
static Int  p_use_soft_float( USES_REGS1 );
static Int  p_enable_jit_exception_handling( USES_REGS1 );
static Int  p_enable_jit_emit_debug_info( USES_REGS1 );
static Int  p_enable_jit_emit_debug_info_to_disk( USES_REGS1 );
static Int  p_guaranteed_tail_call_opt( USES_REGS1 );
static Int  p_disable_tail_calls( USES_REGS1 );
static Int  p_enable_fast_isel( USES_REGS1 );
static Int  p_fp_abitype( USES_REGS1 );
static Int  p_default_fp_abitype( USES_REGS1 );

// LLVM Execution Engine level
static Int  p_engine_opt_level( USES_REGS1 );
static Int  p_reset_engine_opt_level( USES_REGS1 );

// LLVM Execution Engine reloc model
static Int  p_relocmodel( USES_REGS1 );
static Int  p_reset_relocmodel( USES_REGS1 );

// LLVM Execution Engine code model
static Int  p_codemodel( USES_REGS1 );
static Int  p_reset_codemodel( USES_REGS1 );

// Enable MC JIT (experimental)
static Int  p_enable_mcjit( USES_REGS1 );

// Disable MC JIT (experimental)
static Int  p_disable_mcjit( USES_REGS1 );

// LLVM Register Allocator (not implemented -- for some reason, LLVM crashes when I use it on 'JIT_Compiler.cpp')
static Int  p_register_allocator( USES_REGS1 );
static Int  p_reset_register_allocator( USES_REGS1 );

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wimplicit-function-declaration"

static Int
p_enable_framepointer_elimination( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.noframepointerelim = FALSE;
  return TRUE;
}

static Int
p_more_precise_fp_mad_option( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.lessprecisefpmadoption = FALSE;
  return TRUE;
}

static Int
p_excess_fp_precision( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.noexcessfpprecision = FALSE;
  return TRUE;
}

static Int
p_safe_fp_math( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.unsafefpmath = FALSE;
  return TRUE;
}

static Int
p_rounding_mode_not_changed( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.honorsigndependentroundingfpmathoption = FALSE;
  return TRUE;
}

static Int
p_no_use_soft_float( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.usesoftfloat = FALSE;
  return TRUE;
}

static Int
p_disable_jit_exception_handling( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.jitexceptionhandling = FALSE;
  return TRUE;
}

static Int
p_disable_jit_emit_debug_info( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.jitemitdebuginfo = FALSE;
  return TRUE;
}

static Int
p_disable_jit_emit_debug_info_to_disk( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.jitemitdebuginfotodisk = FALSE;
  return TRUE;
}

static Int
p_no_guaranteed_tail_call_opt( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.guaranteedtailcallopt = FALSE;
  return TRUE;
}

static Int
p_enable_tail_calls( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.disabletailcalls = FALSE;
  return TRUE;
}

static Int
p_disable_fast_isel( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.fastisel = FALSE;
  return TRUE;
}

static Int
p_disable_framepointer_elimination( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.noframepointerelim = TRUE;
  return TRUE;
}

static Int
p_less_precise_fp_mad_option( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.lessprecisefpmadoption = TRUE;
  return TRUE;
}

static Int
p_no_excess_fp_precision( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.noexcessfpprecision = TRUE;
  return TRUE;
}

static Int
p_unsafe_fp_math( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.unsafefpmath = TRUE;
  return TRUE;
}

static Int
p_rounding_mode_dynamically_changed( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.honorsigndependentroundingfpmathoption = TRUE;
  return TRUE;
}

static Int
p_use_soft_float( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.usesoftfloat = TRUE;
  return TRUE;
}

static Int
p_enable_jit_exception_handling( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.jitexceptionhandling = TRUE;
  return TRUE;
}

static Int
p_enable_jit_emit_debug_info( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.jitemitdebuginfo = TRUE;
  return TRUE;
}

static Int
p_enable_jit_emit_debug_info_to_disk( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.jitemitdebuginfotodisk = TRUE;
  return TRUE;
}

static Int
p_guaranteed_tail_call_opt( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.guaranteedtailcallopt = TRUE;
  return TRUE;
}

static Int
p_disable_tail_calls( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.disabletailcalls = TRUE;
  return TRUE;
}

static Int
p_enable_fast_isel( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.fastisel = TRUE;
  return TRUE;
}

static Int
p_fp_abitype( USES_REGS1 )
{
  Term t = Deref(ARG1);
  Int v;
  // valid values for ARG1 are 'integer' and 'atom'
  if (IsIntTerm(t)) {
    // ARG1 is integer
    v = IntOfTerm(t);
    if (v < 0 || v > 2) {
      // value passed by argument is out of known range (0 = default; 1 = soft; 2 = hard)
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_targetopt.floatabitype = v; // setting 'float abi type'
    return TRUE;
  }
  else if (IsAtomTerm(t)) {
    // ARG1 is atom
    int i = 0, j = 0;
    char *tmp;
    // gets string from atom and stores it on 'str'
    char *str = (char*)malloc(YAP_AtomNameLength(AtomOfTerm(t))*sizeof(char));
    strcpy(str, AtomName(AtomOfTerm(t)));
    // Makes upper characters of 'str' (for comparison)
    UPPER_ENTRY(str);

    // Detects float abi type chosen by user
    if (strcmp(str, "DEFAULT") == 0) v = 0;
    else if (strcmp(str, "SOFT") == 0) v = 1;
    else if (strcmp(str, "HARD") == 0) v = 2;
    else {
      // value passed by argument is out of known range
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_targetopt.floatabitype = v; // setting 'float abi type'
    return TRUE;
  }
  else {
    // ARG1 is not an integer or atom
    Yap_NilError(INVALID_PARAMETER_TYPE_ERROR,"fp_abitype");
    return FALSE;
  }
}

static Int
p_default_fp_abitype( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_targetopt.floatabitype = 0;
  return TRUE;
}

static Int
p_engine_opt_level( USES_REGS1 )
{
  Term t = Deref(ARG1);
  Int v;
  // valid values for ARG1 are 'integer' and 'atom'
  if (IsIntTerm(t)) {
    // ARG1 is integer
    v = IntOfTerm(t);
    if (v < 0 || v > 3) {
      // value passed by argument is out of known range (0 = none; 1 = less; 2 = default; 3 = aggressive)
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_enginebuilder.engineoptlevel = v; // setting 'engine opt level'
    return TRUE;
  }
  else if (IsAtomTerm(t)) {
    // ARG1 is atom
    int i = 0, j = 0;
    char *tmp;
    // gets string from atom and stores it on 'str'
    char *str = (char*)malloc(YAP_AtomNameLength(AtomOfTerm(t))*sizeof(char));
    strcpy(str, AtomName(AtomOfTerm(t)));
    // Makes upper characters of 'str' (for comparison)
    UPPER_ENTRY(str);

    // Detects engine opt level chosen by user
    if (strcmp(str, "NONE") == 0) v = 0;
    else if (strcmp(str, "LESS") == 0) v = 1;
    else if (strcmp(str, "DEFAULT") == 0) v = 2;
    else if (strcmp(str, "AGGRESSIVE") == 0) v = 3;
    else {
      // value passed by argument is out of known range
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_enginebuilder.engineoptlevel = v; // setting 'engine opt level'
    return TRUE;
  }
  else {
    // ARG1 is not an integer or atom
    Yap_NilError(INVALID_PARAMETER_TYPE_ERROR,"engine_opt_level");
    return FALSE;
  }
}

static Int
p_reset_engine_opt_level( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_enginebuilder.engineoptlevel = 3;
  return TRUE;
}

static Int
p_relocmodel( USES_REGS1 )
{
  Term t = Deref(ARG1);
  Int v;
  // valid values for ARG1 are 'integer' and 'atom'
  if (IsIntTerm(t)) {
    // ARG1 is integer
    v = IntOfTerm(t);
    if (v < 0 || v > 3) {
      // value passed by argument is out of known range (0 = default; 1 = static; 2 = PIC; 3 = DynamicNoPIC)
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_enginebuilder.relocmodel = v; // setting 'reloc model'
    return TRUE;
  }
  else if (IsAtomTerm(t)) {
    // ARG1 is atom
    int i = 0, j = 0;
    char *tmp;
    // gets string from atom and stores it on 'str'
    char *str = (char*)malloc(YAP_AtomNameLength(AtomOfTerm(t))*sizeof(char));
    strcpy(str, AtomName(AtomOfTerm(t)));
    // Makes upper characters of 'str' (for comparison)
    UPPER_ENTRY(str);

    // Detects reloc model chosen by user
    if (strcmp(str, "DEFAULT") == 0) v = 0;
    else if (strcmp(str, "STATIC") == 0) v = 1;
    else if (strcmp(str, "PIC") == 0) v = 2;
    else if (strcmp(str, "DYNAMICNOPIC") == 0) v = 3;
    else {
      // value passed by argument is out of known range
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_enginebuilder.relocmodel = v; // setting 'reloc model'
    return TRUE;
  }
  else {
    // ARG1 is not an integer or atom
    Yap_NilError(INVALID_PARAMETER_TYPE_ERROR,"relocmodel");
    return FALSE;
  }
}

static Int
p_reset_relocmodel( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_enginebuilder.relocmodel = 0;
  return TRUE;
}

static Int
p_codemodel( USES_REGS1 )
{
  Term t = Deref(ARG1);
  Int v;
  // valid values for ARG1 are 'integer' and 'atom'
  if (IsIntTerm(t)) {
    // ARG1 is integer
    v = IntOfTerm(t);
    if (v < 0 || v > 5) {
      // value passed by argument is out of known range (0 = default; 1 = JITDefault; 2 = small; 3 = kernel; 4 = medium; 5 = large)
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_enginebuilder.codemodel = v; // setting 'code model'
    return TRUE;
  }
  else if (IsAtomTerm(t)) {
    // ARG1 is atom
    int i = 0, j = 0;
    char *tmp;
    // gets string from atom and stores it on 'str'
    char *str = (char*)malloc(YAP_AtomNameLength(AtomOfTerm(t))*sizeof(char));
    strcpy(str, AtomName(AtomOfTerm(t)));
    // Makes upper characters of 'str' (for comparison)
    UPPER_ENTRY(str);

    // Detects code chosen by user
    if (strcmp(str, "DEFAULT") == 0) v = 0;
    else if (strcmp(str, "JITDEFAULT") == 0) v = 1;
    else if (strcmp(str, "SMALL") == 0) v = 2;
    else if (strcmp(str, "KERNEL") == 0) v = 3;
    else if (strcmp(str, "MEDIUM") == 0) v = 4;
    else if (strcmp(str, "LARGE") == 0) v = 5;
    else {
      // value passed by argument is out of known range
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_enginebuilder.codemodel = v; // setting 'code model'
    return TRUE;
  }
  else {
    // ARG1 is not an integer or atom
    Yap_NilError(INVALID_PARAMETER_TYPE_ERROR,"codemodel");
    return FALSE;
  }
}

static Int
p_reset_codemodel( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_enginebuilder.codemodel = 1;
  return TRUE;
}

static Int
p_enable_mcjit( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_enginebuilder.usemcjit = 1;
  return TRUE;
}

static Int
p_disable_mcjit( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_enginebuilder.usemcjit = 0;
  return TRUE;
}

static Int
p_register_allocator( USES_REGS1 )
{
  Term t = Deref(ARG1);
  // valid values for ARG1 are 'integer' and 'atom'
  if (IsIntTerm(t)) {
    // ARG1 is integer
    Int v = IntOfTerm(t);
    if (v < 0 || v > 3) {
      // value passed by argument is out of known range (0 = basic; 1 = fast; 2 = greedy; 3 = PBQP)
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_enginebuilder.regallocator = (enumRegAllocator)v; // setting 'register allocator'
    return TRUE;
  }
  else if (IsAtomTerm(t)) {
    // ARG1 is atom
    enumRegAllocator v;
    int i = 0, j = 0;
    char *tmp;
    // gets string from atom and stores it on 'str'
    char *str = (char*)malloc(YAP_AtomNameLength(AtomOfTerm(t))*sizeof(char));
    strcpy(str, AtomName(AtomOfTerm(t)));
    // Makes upper characters of 'str' (for comparison)
    UPPER_ENTRY(str);

    // Detects register allocator chosen by user
    if (strcmp(str, "BASIC") == 0) v = REG_ALLOC_BASIC;
    else if (strcmp(str, "FAST") == 0) v = REG_ALLOC_FAST;
    else if (strcmp(str, "GREEDY") == 0) v = REG_ALLOC_GREEDY;
    else if (strcmp(str, "PBQP") == 0) v = REG_ALLOC_PBQP;
    else {
      // value passed by argument is out of known range
      Yap_Error(OUT_OF_KNOWNRANGE_ERROR,t,"");
      return FALSE;
    }
    ExpEnv.codegen_struc.struc_enginebuilder.regallocator = v; // setting 'register allocator'
    return TRUE;
  }
  else {
    // ARG1 is not an integer or atom
    Yap_NilError(INVALID_PARAMETER_TYPE_ERROR,"register_allocator");
    return FALSE;
  }
}

static Int
p_reset_register_allocator( USES_REGS1 )
{
  ExpEnv.codegen_struc.struc_enginebuilder.regallocator = REG_ALLOC_GREEDY;
  return TRUE;
}

#pragma GCC diagnostic pop

void
Yap_InitJitCodegenPreds( void )
{
  Yap_InitCPred("enable_framepointer_elimination", 0, p_enable_framepointer_elimination, SafePredFlag);
  Yap_InitCPred("more_precise_fp_mad_option", 0, p_more_precise_fp_mad_option, SafePredFlag);
  Yap_InitCPred("excess_fp_precision", 0, p_excess_fp_precision, SafePredFlag);
  Yap_InitCPred("safe_fp_math", 0, p_safe_fp_math, SafePredFlag);
  Yap_InitCPred("rounding_mode_not_changed", 0, p_rounding_mode_not_changed, SafePredFlag);
  Yap_InitCPred("no_use_soft_float", 0, p_no_use_soft_float, SafePredFlag);
  Yap_InitCPred("disable_jit_exception_handling", 0, p_disable_jit_exception_handling, SafePredFlag);
  Yap_InitCPred("disable_jit_emit_debug_info", 0, p_disable_jit_emit_debug_info, SafePredFlag);
  Yap_InitCPred("disable_jit_emit_debug_info_to_disk", 0, p_disable_jit_emit_debug_info_to_disk, SafePredFlag);
  Yap_InitCPred("no_guaranteed_tail_call_opt", 0, p_no_guaranteed_tail_call_opt, SafePredFlag);
  Yap_InitCPred("enable_tail_calls", 0, p_enable_tail_calls, SafePredFlag);
  Yap_InitCPred("disable_fast_isel", 0, p_disable_fast_isel, SafePredFlag);
  Yap_InitCPred("disable_framepointer_elimination", 0, p_disable_framepointer_elimination, SafePredFlag);
  Yap_InitCPred("less_precise_fp_mad_option", 0, p_less_precise_fp_mad_option, SafePredFlag);
  Yap_InitCPred("no_excess_fp_precision", 0, p_no_excess_fp_precision, SafePredFlag);
  Yap_InitCPred("unsafe_fp_math", 0, p_unsafe_fp_math, SafePredFlag);
  Yap_InitCPred("rounding_mode_dynamically_changed", 0, p_rounding_mode_dynamically_changed, SafePredFlag);
  Yap_InitCPred("use_soft_float", 0, p_use_soft_float, SafePredFlag);
  Yap_InitCPred("enable_jit_exception_handling", 0, p_enable_jit_exception_handling, SafePredFlag);
  Yap_InitCPred("enable_jit_emit_debug_info", 0, p_enable_jit_emit_debug_info, SafePredFlag);
  Yap_InitCPred("enable_jit_emit_debug_info_to_disk", 0, p_enable_jit_emit_debug_info_to_disk, SafePredFlag);
  Yap_InitCPred("guaranteed_tail_call_opt", 0, p_guaranteed_tail_call_opt, SafePredFlag);
  Yap_InitCPred("disable_tail_calls", 0, p_disable_tail_calls, SafePredFlag);
  Yap_InitCPred("enable_fast_isel", 0, p_enable_fast_isel, SafePredFlag);
  Yap_InitCPred("fp_abitype", 1, p_fp_abitype, SafePredFlag);
  Yap_InitCPred("default_fp_abitype", 0, p_default_fp_abitype, SafePredFlag);
  Yap_InitCPred("engine_opt_level", 1, p_engine_opt_level, SafePredFlag);
  Yap_InitCPred("reset_engine_opt_level", 0, p_reset_engine_opt_level, SafePredFlag);
  Yap_InitCPred("relocmodel", 1, p_relocmodel, SafePredFlag);
  Yap_InitCPred("reset_relocmodel", 0, p_reset_relocmodel, SafePredFlag);
  Yap_InitCPred("codemodel", 1, p_codemodel, SafePredFlag);
  Yap_InitCPred("reset_codemodel", 0, p_reset_codemodel, SafePredFlag);
  Yap_InitCPred("enable_mcjit", 0, p_enable_mcjit, SafePredFlag);
  Yap_InitCPred("disable_mcjit", 0, p_disable_mcjit, SafePredFlag);
  Yap_InitCPred("register_allocator", 1, p_register_allocator, SafePredFlag);
  Yap_InitCPred("reset_register_allocator", 0, p_reset_register_allocator, SafePredFlag);
}