//----------------------------------------------------------------------
// Some of the following do not have any use right now, since we
// always end up with a single bitcode file and don't care about
// multiple compilation units, etc.
//
// We assume the following, due to our whole-program analysis scheme:
//
//   1. For any referenced func, there will exist at least one
//      func declaration (that may also be a definition).
//
//   2. If a func definition exists, then no declarations will be
//      visible. This means that we do not have to deal with any
//      indirections when creating the mapping from call instrs
//      to funcs being called. When a func definition exists,
//      then the call instr will be mapped to it. Otherwise, it
//      will be mapped to a func declaration.
//
//   3. In principle, not all func declarations will map directly
//      to a func definition. For instance, func calls that
//      belong to dynamically linked libraries cannot be resolved.
//      Thus, they will be mapped to func declarations instead.
//
//   4. There will exist at most one definition per func.
//----------------------------------------------------------------------

.decl constant_references_func(c:Constant, f:FunctionDecl)
.decl constant_references_variable(c:Constant, v:GlobalVariable)

// Retrieve func names from constants
constant_references_func(FC, Func) :-
  func_constant(FC),
  func_constant_fn_name(FC, FuncName),
  func_name(Func, FuncName).

// Retrieve variable names from constants
constant_references_variable(GC, GVar) :-
  global_var_constant(GC),
  global_var_constant_name(GC, Name),
  global_var_name(GVar, Name).


// A constant may be enclosed by a bitcast constant
// expression
constant_references_func(Outer, Func) :-
   bitcast_constant_expression_from(Outer, Inner),
   constant_references_func(Inner, Func).

constant_references_variable(Outer, GVar) :-
   bitcast_constant_expression_from(Outer, Inner),
   constant_references_variable(Inner, GVar).

//-------------------------------------------------------------
// [Aliases]
//
// An alias may also be used as a func operand.
//-------------------------------------------------------------

.decl aliased_constants(c1:Constant, c2:Constant)
.decl _alias(c:Constant, a:Alias)
.decl _aliasee(c:Constant, a:Alias)

// `Constant` is an aliasee of `Alias`
_aliasee(Constant, Alias) :-
   alias_aliasee(Alias, Aliasee),
   constant_has_value(Aliasee, Value),
   constant_has_value(Constant, Value).

// `Constant` is actually an alias (via `Alias`)
_alias(Constant, Alias) :-
   constant_has_value(Constant, AliasName),
   alias_name(Alias, AliasName).

// `OtherConstant` is an alias of `Constant`
aliased_constants(OtherConstant, Constant) :-
   _alias(OtherConstant, Alias),
   _aliasee(Constant, Alias).

constant_references_func(OtherConstant, Func) :-
   aliased_constants(OtherConstant, Constant),
   constant_references_func(Constant, Func).

constant_references_variable(OtherConstant, GVar) :-
   aliased_constants(OtherConstant, Constant),
   constant_references_variable(Constant, GVar).


//------------------------------------------------------------------------------
// Mapping from call instrs to func declarations
//------------------------------------------------------------------------------

.decl _instr_calls_func(instr:Instruction, target:FunctionDecl)

_instr_calls_func(Instr, Func) :-
   direct_call_instr(Instr),
   call_instr_func_operand(Instr, FunctionConstant),
   constant(FunctionConstant),
   constant_references_func(FunctionConstant, Func).

_instr_calls_func(Instr, Func) :-
   direct_invoke_instr(Instr),
   invoke_instr_func_operand(Instr, FunctionConstant),
   constant(FunctionConstant),
   constant_references_func(FunctionConstant, Func).


// Is this always true for our tests?
schema_invalid_instr(Instr, __FILE__, __LINE__) :-
   schema_sanity(),
   direct_call_instr(Instr),
   !call_instr_fn_target(Instr, _).

schema_invalid_instr(Instr, __FILE__, __LINE__) :-
   schema_sanity(),
   direct_invoke_instr(Instr),
   !invoke_instr_fn_target(Instr, _).


//----------------------------------------------------------------------
// This is for future use (if we ever introduce multiple compilation
// units as input).
//
// * Each func declaration should be mapped to exactly one
//   func definition.
//
// * Each func declaration, which also happens to be a definition,
//   maps to itself.
//----------------------------------------------------------------------

.decl func_decl_to_defn(fdecl:FunctionDecl, func:Function)

func_decl_to_defn(Func, Func) :-
   func(Func).

// Map call instrs to funcs. If definition is obtainable,
// map to it; else map to declaration

call_instr_fn_target(Instr, Func) :-
   call_instr(Instr),
   _instr_calls_func(Instr, SomeFunc),
   func_decl_to_defn(SomeFunc, Func).

call_instr_fn_target(Instr, SomeFunc) :-
   call_instr(Instr),
   _instr_calls_func(Instr, SomeFunc),
   !func_decl_to_defn(SomeFunc, _).

invoke_instr_fn_target(Instr, Func) :-
   invoke_instr(Instr),
   _instr_calls_func(Instr, SomeFunc),
   func_decl_to_defn(SomeFunc, Func).

invoke_instr_fn_target(Instr, SomeFunc) :-
   invoke_instr(Instr),
   _instr_calls_func(Instr, SomeFunc),
   !func_decl_to_defn(SomeFunc, _).
