/*
 * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#ifndef SHARE_VM_C1_C1_RANGECHECKELIMINATION_HPP
#define SHARE_VM_C1_C1_RANGECHECKELIMINATION_HPP

#include "c1/c1_Instruction.hpp"

// Base class for range check elimination
class RangeCheckElimination : AllStatic {
public:
  static void eliminate(IR *ir);
};

// Implementation
class RangeCheckEliminator {
private:
  int _number_of_instructions;
  bool _optimistic; // Insert predicates and deoptimize when they fail
  IR *_ir;

  typedef GrowableArray<BlockBegin*> BlockBeginList;
  typedef GrowableArray<int> IntegerStack;

  class Verification : public BlockClosure {
  // RangeCheckEliminator::Verification should never get instatiated on the heap.
  private:
    void* operator new(size_t size) throw();
    void* operator new[](size_t size) throw();
    void operator delete(void* p) { ShouldNotReachHere(); }
    void operator delete[](void* p) { ShouldNotReachHere(); }

    IR *_ir;
    boolArray _used;
    BlockBeginList _current;
    BlockBeginList _successors;

  public:
    Verification(IR *ir);
    virtual void block_do(BlockBegin *block);
    bool can_reach(BlockBegin *start, BlockBegin *end, BlockBegin *dont_use = NULL);
    bool dominates(BlockBegin *dominator, BlockBegin *block);
  };

public:
  // Bounds for an instruction in the form x + c which c integer
  // constant and x another instruction
  class Bound : public CompilationResourceObj {
  private:
    int _upper;
    Value _upper_instr;
    int _lower;
    Value _lower_instr;

  public:
    Bound();
    Bound(Value v);
    Bound(Instruction::Condition cond, Value v, int constant = 0);
    Bound(int lower, Value lower_instr, int upper, Value upper_instr);
    ~Bound();

#ifdef ASSERT
    void add_assertion(Instruction *instruction, Instruction *position, int i, Value instr, Instruction::Condition cond);
#endif
    int upper();
    Value upper_instr();
    int lower();
    Value lower_instr();
    void print();
    bool check_no_overflow(int const_value);
    void or_op(Bound *b);
    void and_op(Bound *b);
    bool has_upper();
    bool has_lower();
    void set_upper(int upper, Value upper_instr);
    void set_lower(int lower, Value lower_instr);
    bool is_smaller(Bound *b);
    void remove_upper();
    void remove_lower();
    void add_constant(int value);
    Bound *copy();

  private:
    void init();
  };


  class Visitor : public InstructionVisitor {
  private:
    Bound *_bound;
    RangeCheckEliminator *_rce;

  public:
    void set_range_check_eliminator(RangeCheckEliminator *rce) { _rce = rce; }
    Bound *bound() const { return _bound; }
    void clear_bound() { _bound = NULL; }

  protected:
    // visitor functions
    void do_Constant       (Constant*        x);
    void do_IfOp           (IfOp*            x);
    void do_LogicOp        (LogicOp*         x);
    void do_ArithmeticOp   (ArithmeticOp*    x);
    void do_Phi            (Phi*             x);

    void do_StoreField     (StoreField*      x) { /* nothing to do */ };
    void do_StoreIndexed   (StoreIndexed*    x) { /* nothing to do */ };
    void do_MonitorEnter   (MonitorEnter*    x) { /* nothing to do */ };
    void do_MonitorExit    (MonitorExit*     x) { /* nothing to do */ };
    void do_Invoke         (Invoke*          x) { /* nothing to do */ };
    void do_UnsafePutRaw   (UnsafePutRaw*    x) { /* nothing to do */ };
    void do_UnsafePutObject(UnsafePutObject* x) { /* nothing to do */ };
    void do_Intrinsic      (Intrinsic*       x) { /* nothing to do */ };
    void do_Local          (Local*           x) { /* nothing to do */ };
    void do_LoadField      (LoadField*       x) { /* nothing to do */ };
    void do_ArrayLength    (ArrayLength*     x) { /* nothing to do */ };
    void do_LoadIndexed    (LoadIndexed*     x) { /* nothing to do */ };
    void do_NegateOp       (NegateOp*        x) { /* nothing to do */ };
    void do_ShiftOp        (ShiftOp*         x) { /* nothing to do */ };
    void do_CompareOp      (CompareOp*       x) { /* nothing to do */ };
    void do_Convert        (Convert*         x) { /* nothing to do */ };
    void do_NullCheck      (NullCheck*       x) { /* nothing to do */ };
    void do_TypeCast       (TypeCast*        x) { /* nothing to do */ };
    void do_NewInstance    (NewInstance*     x) { /* nothing to do */ };
    void do_NewTypeArray   (NewTypeArray*    x) { /* nothing to do */ };
    void do_NewObjectArray (NewObjectArray*  x) { /* nothing to do */ };
    void do_NewMultiArray  (NewMultiArray*   x) { /* nothing to do */ };
    void do_CheckCast      (CheckCast*       x) { /* nothing to do */ };
    void do_InstanceOf     (InstanceOf*      x) { /* nothing to do */ };
    void do_BlockBegin     (BlockBegin*      x) { /* nothing to do */ };
    void do_Goto           (Goto*            x) { /* nothing to do */ };
    void do_If             (If*              x) { /* nothing to do */ };
    void do_IfInstanceOf   (IfInstanceOf*    x) { /* nothing to do */ };
    void do_TableSwitch    (TableSwitch*     x) { /* nothing to do */ };
    void do_LookupSwitch   (LookupSwitch*    x) { /* nothing to do */ };
    void do_Return         (Return*          x) { /* nothing to do */ };
    void do_Throw          (Throw*           x) { /* nothing to do */ };
    void do_Base           (Base*            x) { /* nothing to do */ };
    void do_OsrEntry       (OsrEntry*        x) { /* nothing to do */ };
    void do_ExceptionObject(ExceptionObject* x) { /* nothing to do */ };
    void do_RoundFP        (RoundFP*         x) { /* nothing to do */ };
    void do_UnsafeGetRaw   (UnsafeGetRaw*    x) { /* nothing to do */ };
    void do_UnsafeGetObject(UnsafeGetObject* x) { /* nothing to do */ };
    void do_UnsafeGetAndSetObject(UnsafeGetAndSetObject* x) { /* nothing to do */ };
    void do_ProfileCall    (ProfileCall*     x) { /* nothing to do */ };
    void do_ProfileReturnType (ProfileReturnType*  x) { /* nothing to do */ };
    void do_ProfileInvoke  (ProfileInvoke*   x) { /* nothing to do */ };
    void do_RuntimeCall    (RuntimeCall*     x) { /* nothing to do */ };
    void do_MemBar         (MemBar*          x) { /* nothing to do */ };
    void do_RangeCheckPredicate(RangeCheckPredicate* x) { /* nothing to do */ };
#ifdef ASSERT
    void do_Assert         (Assert*          x) { /* nothing to do */ };
#endif
  };

#ifdef ASSERT
  void add_assertions(Bound *bound, Instruction *instruction, Instruction *position);
#endif

  typedef GrowableArray<Bound*> BoundStack;
  typedef GrowableArray<BoundStack*> BoundMap;
  typedef GrowableArray<AccessIndexed*> AccessIndexedList;
  typedef GrowableArray<Instruction*> InstructionList;

  class AccessIndexedInfo : public CompilationResourceObj  {
  public:
    AccessIndexedList *_list;
    int _min;
    int _max;
  };

  typedef GrowableArray<AccessIndexedInfo*> AccessIndexedInfoArray;
  BoundMap _bounds; // Mapping from Instruction's id to current bound
  AccessIndexedInfoArray _access_indexed_info; // Mapping from Instruction's id to AccessIndexedInfo for in block motion
  Visitor _visitor;

public:
  RangeCheckEliminator(IR *ir);

  IR *ir() const { return _ir; }

  // Pass over the dominator tree to identify blocks where there's an oppportunity for optimization
  bool set_process_block_flags(BlockBegin *block);
  // The core of the optimization work: pass over the dominator tree
  // to propagate bound information, insert predicate out of loops,
  // eliminate bound checks when possible and perform in block motion
  void calc_bounds(BlockBegin *block, BlockBegin *loop_header);
  // reorder bound checks within a block in order to eliminate some of them
  void in_block_motion(BlockBegin *block, AccessIndexedList &accessIndexed, InstructionList &arrays);

  // update/access current bound
  void update_bound(IntegerStack &pushed, Value v, Instruction::Condition cond, Value value, int constant);
  void update_bound(IntegerStack &pushed, Value v, Bound *bound);
  Bound *get_bound(Value v);

  bool loop_invariant(BlockBegin *loop_header, Instruction *instruction);                                    // check for loop invariance
  void add_access_indexed_info(InstructionList &indices, int i, Value instruction, AccessIndexed *ai); // record indexed access for in block motion
  void remove_range_check(AccessIndexed *ai);                                                                // Mark this instructions as not needing a range check
  void add_if_condition(IntegerStack &pushed, Value x, Value y, Instruction::Condition condition);           // Update bound for an If
  bool in_array_bound(Bound *bound, Value array);                                                            // Check whether bound is known to fall within array

  // helper functions to work with predicates
  Instruction* insert_after(Instruction* insert_position, Instruction* instr, int bci);
  Instruction* predicate(Instruction* left, Instruction::Condition cond, Instruction* right, ValueStack* state, Instruction *insert_position, int bci=-1);
  Instruction* predicate_cmp_with_const(Instruction* instr, Instruction::Condition cond, int constant, ValueStack* state, Instruction *insert_position, int bci=1);
  Instruction* predicate_add(Instruction* left, int left_const, Instruction::Condition cond, Instruction* right, ValueStack* state, Instruction *insert_position, int bci=-1);
  Instruction* predicate_add_cmp_with_const(Instruction* left, int left_const, Instruction::Condition cond, int constant, ValueStack* state, Instruction *insert_position, int bci=-1);

  void insert_deoptimization(ValueStack *state, Instruction *insert_position, Instruction *array_instr,      // Add predicate
                             Instruction *length_instruction, Instruction *lower_instr, int lower,
                             Instruction *upper_instr, int upper, AccessIndexed *ai);
  bool is_ok_for_deoptimization(Instruction *insert_position, Instruction *array_instr,                      // Can we safely add a predicate?
                                Instruction *length_instr, Instruction *lower_instr,
                                int lower, Instruction *upper_instr, int upper);
  void process_if(IntegerStack &pushed, BlockBegin *block, If *cond);                                        // process If Instruction
  void process_access_indexed(BlockBegin *loop_header, BlockBegin *block, AccessIndexed *ai);                // process indexed access

  void dump_condition_stack(BlockBegin *cur_block);
  static void print_statistics();
};

#endif // SHARE_VM_C1_C1_RANGECHECKELIMINATION_HPP
