#ifndef LOC2STAP_H
#define LOC2STAP_H 1

#include "staptree.h"
#include <elfutils/libdw.h>

enum location_type
{
  loc_address, loc_register, loc_noncontiguous, loc_unavailable,
  loc_value, loc_constant, loc_implicit_pointer,
  loc_decl, loc_fragment, loc_final
};

struct location
{
  location_type type;
  unsigned byte_size;

  // loc_address, loc_value, loc_fragment, loc_final
  expression *program;

  // loc_register
  unsigned int regno;

  // loc_noncontiguous
  location *pieces;
  location *piece_next;

  // loc_constant
  const void *constant_block;

  // loc_implicit_pointer
  location *target;

  // loc_register, loc_implicit_pointer
  Dwarf_Word offset;

  location(location_type t = loc_unavailable)
    : type(t), byte_size(0), program(0), regno(0), pieces(0),
      piece_next(0), constant_block(0), target(0), offset(0)
  { }
};

class dwflpp;
class location_context
{
public:
  const target_symbol *e_orig; // unmodified original
  target_symbol *e; // deep-copied + rewritten, for use within synthetic _dwarf_tvar* function body
  
  // These three form the argument list to the function, in sequence.
  // They will be referenced by the expression(s) computing the location.
  vardecl *pointer;
  std::vector<vardecl *> indicies;
  vardecl *value;

  Dwarf_Attribute *attr;
  Dwarf_Addr dwbias;
  Dwarf_Addr pc;
  Dwarf_Attribute *fb_attr;
  const Dwarf_Op *cfa_ops;
  dwflpp *dw;

  // Temporaries required while computing EVALS and LOCATIONS.
  symbol *frame_base;
  std::vector<vardecl *> locals;
  std::vector<statement *> evals;

  std::vector<vardecl *> globals;
  std::map<Dwarf_Addr, block *> entry_probes;

  Dwarf_Die *function;
  Dwarf_Die *parameter_ref;
  std::vector<location_context> call_site_values;
  int param_ref_depth;

  // A set of locations which have been requested to be evaluated.
  // The last one can be considered "current", and thus the result
  // is locations.back()->program.
  // ??? This is the old loc->next list.  Should we just have a
  // single pointer here instead?
  std::vector<location *> locations;	// ??? old loc->next list

  // The dwarf is within a user (vs kernel) context.
  bool userspace_p;

  location *new_location(location_type);
  location *new_location(const location &old);

  symbol *new_symref(vardecl *var);
  symbol *new_local(const char *namebase);
  expression *new_target_reg(unsigned regno);
  expression *new_plus_const(expression *, int64_t);
  expression *save_expression(expression *);
  symbol *frame_location();
  void adapt_pointer_to_bpf(int size, int offset, bool is_signed);
  expression *handle_GNU_entry_value(Dwarf_Op expr);
  expression *handle_GNU_parameter_ref(Dwarf_Op expr);

  location *translate(const Dwarf_Op *expr, size_t len, size_t start,
		      location *input, bool may_use_fb, bool computing_value);
  location *location_from_address (const Dwarf_Op *expr, size_t len,
				   location *input);
  location *translate_offset (const Dwarf_Op *expr, size_t len, size_t i,
			      location *input, Dwarf_Word offset);
  location *location_relative (const Dwarf_Op *expr, size_t len,
			       location *input);
  location *translate_array_1(Dwarf_Die *anydie, Dwarf_Word stride,
			      location *loc, expression *index);

public:
  location_context(target_symbol *, expression * = NULL);

  expression *translate_address(Dwarf_Addr a);
  location *translate_constant(Dwarf_Attribute *a);
  location *translate_location(const Dwarf_Op *locexpr,
			       size_t locexprlen, location *input);
  location *translate_argument (expression *value);
  location *translate_argument (vardecl *var);
  location *translate_array(Dwarf_Die *typedie, location *, expression *);
  location *translate_array_pointer(Dwarf_Die *typedie, location *input,
				    expression *index);
  location *translate_array_pointer (Dwarf_Die *, location *input,
				     vardecl *index);
  location *discontiguify(location *loc, Dwarf_Word total_bytes,
			  Dwarf_Word max_piece_bytes);

};

#endif
