/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
#define ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_

#include "base/array_ref.h"
#include "base/scoped_arena_allocator.h"
#include "base/scoped_arena_containers.h"
#include "data_type.h"
#include "dex/code_item_accessors.h"
#include "dex/dex_file.h"
#include "dex/dex_file_types.h"
#include "handle.h"
#include "nodes.h"

namespace art {

class ArenaBitVector;
class ArtField;
class ArtMethod;
class CodeGenerator;
class DexCompilationUnit;
class HBasicBlockBuilder;
class Instruction;
class InstructionOperands;
class OptimizingCompilerStats;
class ScopedObjectAccess;
class SsaBuilder;

namespace mirror {
class Class;
class MethodType;
}  // namespace mirror

class HInstructionBuilder : public ValueObject {
 public:
  HInstructionBuilder(HGraph* graph,
                      HBasicBlockBuilder* block_builder,
                      SsaBuilder* ssa_builder,
                      const DexFile* dex_file,
                      const CodeItemDebugInfoAccessor& accessor,
                      DataType::Type return_type,
                      const DexCompilationUnit* dex_compilation_unit,
                      const DexCompilationUnit* outer_compilation_unit,
                      CodeGenerator* code_generator,
                      OptimizingCompilerStats* compiler_stats,
                      ScopedArenaAllocator* local_allocator);

  bool Build();
  void BuildIntrinsic(ArtMethod* method);

 private:
  void InitializeBlockLocals();
  void PropagateLocalsToCatchBlocks();
  void SetLoopHeaderPhiInputs();

  bool ProcessDexInstruction(const Instruction& instruction, uint32_t dex_pc);
  ArenaBitVector* FindNativeDebugInfoLocations();

  HBasicBlock* FindBlockStartingAt(uint32_t dex_pc) const;

  ScopedArenaVector<HInstruction*>* GetLocalsFor(HBasicBlock* block);
  // Out of line version of GetLocalsFor(), which has a fast path that is
  // beneficial to get inlined by callers.
  ScopedArenaVector<HInstruction*>* GetLocalsForWithAllocation(
      HBasicBlock* block, ScopedArenaVector<HInstruction*>* locals, const size_t vregs);
  HInstruction* ValueOfLocalAt(HBasicBlock* block, size_t local);
  HInstruction* LoadLocal(uint32_t register_index, DataType::Type type) const;
  HInstruction* LoadNullCheckedLocal(uint32_t register_index, uint32_t dex_pc);
  void UpdateLocal(uint32_t register_index, HInstruction* instruction);

  void AppendInstruction(HInstruction* instruction);
  void InsertInstructionAtTop(HInstruction* instruction);
  void InitializeInstruction(HInstruction* instruction);

  void InitializeParameters();

  template<typename T>
  void Unop_12x(const Instruction& instruction, DataType::Type type, uint32_t dex_pc);

  template<typename T>
  void Binop_23x(const Instruction& instruction, DataType::Type type, uint32_t dex_pc);

  template<typename T>
  void Binop_23x_shift(const Instruction& instruction, DataType::Type type, uint32_t dex_pc);

  void Binop_23x_cmp(const Instruction& instruction,
                     DataType::Type type,
                     ComparisonBias bias,
                     uint32_t dex_pc);

  template<typename T>
  void Binop_12x(const Instruction& instruction, DataType::Type type, uint32_t dex_pc);

  template<typename T>
  void Binop_12x_shift(const Instruction& instruction, DataType::Type type, uint32_t dex_pc);

  template<typename T>
  void Binop_22b(const Instruction& instruction, bool reverse, uint32_t dex_pc);

  template<typename T>
  void Binop_22s(const Instruction& instruction, bool reverse, uint32_t dex_pc);

  template<typename T> void If_21t(const Instruction& instruction, uint32_t dex_pc);
  template<typename T> void If_22t(const Instruction& instruction, uint32_t dex_pc);

  void Conversion_12x(const Instruction& instruction,
                      DataType::Type input_type,
                      DataType::Type result_type,
                      uint32_t dex_pc);

  void BuildCheckedDivRem(uint16_t out_reg,
                          uint16_t first_reg,
                          int64_t second_reg_or_constant,
                          uint32_t dex_pc,
                          DataType::Type type,
                          bool second_is_lit,
                          bool is_div);

  void BuildReturn(const Instruction& instruction, DataType::Type type, uint32_t dex_pc);

  // Builds an instance field access node and returns whether the instruction is supported.
  bool BuildInstanceFieldAccess(const Instruction& instruction,
                                uint32_t dex_pc,
                                bool is_put);

  void BuildUnresolvedStaticFieldAccess(const Instruction& instruction,
                                        uint32_t dex_pc,
                                        bool is_put,
                                        DataType::Type field_type);
  // Builds a static field access node.
  void BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put);

  void BuildArrayAccess(const Instruction& instruction,
                        uint32_t dex_pc,
                        bool is_get,
                        DataType::Type anticipated_type);

  // Builds an invocation node and returns whether the instruction is supported.
  bool BuildInvoke(const Instruction& instruction,
                   uint32_t dex_pc,
                   uint32_t method_idx,
                   const InstructionOperands& operands);

  // Builds an invocation node for invoke-polymorphic and returns whether the
  // instruction is supported.
  bool BuildInvokePolymorphic(uint32_t dex_pc,
                              uint32_t method_idx,
                              dex::ProtoIndex proto_idx,
                              const InstructionOperands& operands);

  // Builds an invocation node for invoke-custom and returns whether the
  // instruction is supported.
  bool BuildInvokeCustom(uint32_t dex_pc,
                         uint32_t call_site_idx,
                         const InstructionOperands& operands);

  // Builds a new array node.
  HNewArray* BuildNewArray(uint32_t dex_pc, dex::TypeIndex type_index, HInstruction* length);

  // Builds a new array node and the instructions that fill it.
  HNewArray* BuildFilledNewArray(uint32_t dex_pc,
                                 dex::TypeIndex type_index,
                                 const InstructionOperands& operands);

  void BuildFillArrayData(const Instruction& instruction, uint32_t dex_pc);

  // Fills the given object with data as specified in the fill-array-data
  // instruction. Currently only used for non-reference and non-floating point
  // arrays.
  template <typename T>
  void BuildFillArrayData(HInstruction* object,
                          const T* data,
                          uint32_t element_count,
                          DataType::Type anticipated_type,
                          uint32_t dex_pc);

  // Fills the given object with data as specified in the fill-array-data
  // instruction. The data must be for long and double arrays.
  void BuildFillWideArrayData(HInstruction* object,
                              const int64_t* data,
                              uint32_t element_count,
                              uint32_t dex_pc);

  // Builds a `HInstanceOf`, or a `HCheckCast` instruction.
  void BuildTypeCheck(bool is_instance_of,
                      HInstruction* object,
                      dex::TypeIndex type_index,
                      uint32_t dex_pc);
  void BuildTypeCheck(const Instruction& instruction,
                      uint8_t destination,
                      uint8_t reference,
                      dex::TypeIndex type_index,
                      uint32_t dex_pc);

  // Builds an instruction sequence for a switch statement.
  void BuildSwitch(const Instruction& instruction, uint32_t dex_pc);

  // Builds a `HLoadString` loading the given `string_index`.
  void BuildLoadString(dex::StringIndex string_index, uint32_t dex_pc);

  // Builds a `HLoadClass` loading the given `type_index`.
  HLoadClass* BuildLoadClass(dex::TypeIndex type_index, uint32_t dex_pc);

  HLoadClass* BuildLoadClass(dex::TypeIndex type_index,
                             const DexFile& dex_file,
                             Handle<mirror::Class> klass,
                             uint32_t dex_pc,
                             bool needs_access_check)
      REQUIRES_SHARED(Locks::mutator_lock_);

  Handle<mirror::Class> ResolveClass(ScopedObjectAccess& soa, dex::TypeIndex type_index)
      REQUIRES_SHARED(Locks::mutator_lock_);

  bool LoadClassNeedsAccessCheck(dex::TypeIndex type_index, ObjPtr<mirror::Class> klass)
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Builds a `HLoadMethodHandle` loading the given `method_handle_index`.
  void BuildLoadMethodHandle(uint16_t method_handle_idx, uint32_t dex_pc);

  // Builds a `HLoadMethodType` loading the given `proto_index`.
  void BuildLoadMethodType(dex::ProtoIndex proto_index, uint32_t dex_pc);

  void PotentiallySimplifyFakeString(uint16_t original_dex_register,
                                     uint32_t dex_pc,
                                     HInvoke* invoke);

  enum class ReceiverArg {
    kNone,             // No receiver, static method.
    kNullCheckedArg,   // Normal instance invoke, null check and pass the argument.
    kNullCheckedOnly,  // Null check but do not use the arg, used for intrinsic replacements.
    kPlainArg,         // Do not null check but pass the argument, used for unresolved methods.
    kIgnored,          // No receiver despite allocated vreg, used for String.<init>.
  };
  bool SetupInvokeArguments(HInstruction* invoke,
                            const InstructionOperands& operands,
                            const char* shorty,
                            ReceiverArg receiver_arg);

  bool HandleInvoke(HInvoke* invoke,
                    const InstructionOperands& operands,
                    const char* shorty,
                    bool is_unresolved);

  bool HandleStringInit(HInvoke* invoke,
                        const InstructionOperands& operands,
                        const char* shorty);
  void HandleStringInitResult(HInvokeStaticOrDirect* invoke);

  HClinitCheck* ProcessClinitCheckForInvoke(
      uint32_t dex_pc,
      ArtMethod* method,
      HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement);

  // Try to build a replacement for an intrinsic invoke. Returns true on success,
  // false on failure. Failure can be either lack of replacement HIR classes, or
  // input register mismatch.
  bool BuildSimpleIntrinsic(ArtMethod* method,
                            uint32_t dex_pc,
                            const InstructionOperands& operands,
                            const char* shorty);

  // Build a HNewInstance instruction.
  HNewInstance* BuildNewInstance(dex::TypeIndex type_index, uint32_t dex_pc);

  // Build a HConstructorFence for HNewInstance and HNewArray instructions. This ensures the
  // happens-before ordering for default-initialization of the object referred to by new_instance.
  void BuildConstructorFenceForAllocation(HInstruction* allocation);

  // Return whether the compiler can assume `cls` is initialized.
  bool IsInitialized(ObjPtr<mirror::Class> cls) const
      REQUIRES_SHARED(Locks::mutator_lock_);

  // Try to resolve a field using the class linker. Return null if it could not
  // be found.
  ArtField* ResolveField(uint16_t field_idx, bool is_static, bool is_put);

  ObjPtr<mirror::Class> LookupResolvedType(dex::TypeIndex type_index,
                                           const DexCompilationUnit& compilation_unit) const
      REQUIRES_SHARED(Locks::mutator_lock_);

  ObjPtr<mirror::Class> LookupReferrerClass() const REQUIRES_SHARED(Locks::mutator_lock_);

  ArenaAllocator* const allocator_;
  HGraph* const graph_;

  // The dex file where the method being compiled is, and the bytecode data.
  const DexFile* const dex_file_;
  const CodeItemDebugInfoAccessor code_item_accessor_;  // null for intrinsic graph.

  // The return type of the method being compiled.
  const DataType::Type return_type_;

  HBasicBlockBuilder* const block_builder_;
  SsaBuilder* const ssa_builder_;

  CodeGenerator* const code_generator_;

  // The compilation unit of the current method being compiled. Note that
  // it can be an inlined method.
  const DexCompilationUnit* const dex_compilation_unit_;

  // The compilation unit of the outermost method being compiled. That is the
  // method being compiled (and not inlined), and potentially inlining other
  // methods.
  const DexCompilationUnit* const outer_compilation_unit_;

  OptimizingCompilerStats* const compilation_stats_;

  ScopedArenaAllocator* const local_allocator_;
  ScopedArenaVector<ScopedArenaVector<HInstruction*>> locals_for_;
  HBasicBlock* current_block_;
  ScopedArenaVector<HInstruction*>* current_locals_;
  HInstruction* latest_result_;
  // Current "this" parameter.
  // Valid only after InitializeParameters() finishes.
  // * Null for static methods.
  // * Non-null for instance methods.
  HParameterValue* current_this_parameter_;

  ScopedArenaVector<HBasicBlock*> loop_headers_;

  // Cached resolved types for the current compilation unit's DexFile.
  // Handle<>s reference entries in the `graph_->GetHandleCache()`.
  ScopedArenaSafeMap<dex::TypeIndex, Handle<mirror::Class>> class_cache_;

  static constexpr int kDefaultNumberOfLoops = 2;

  DISALLOW_COPY_AND_ASSIGN(HInstructionBuilder);
};

}  // namespace art

#endif  // ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_
