/*
 * Copyright (C) 2015 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.
 */

#include "intrinsics.h"

#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/utils.h"
#include "class_linker.h"
#include "class_root.h"
#include "dex/invoke_type.h"
#include "driver/compiler_options.h"
#include "gc/space/image_space.h"
#include "image-inl.h"
#include "intrinsic_objects.h"
#include "nodes.h"
#include "obj_ptr-inl.h"
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"

namespace art {

std::ostream& operator<<(std::ostream& os, const Intrinsics& intrinsic) {
  switch (intrinsic) {
    case Intrinsics::kNone:
      os << "None";
      break;
#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache, SideEffects, Exceptions, ...) \
    case Intrinsics::k ## Name: \
      os << # Name; \
      break;
#include "intrinsics_list.h"
      INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
#undef STATIC_INTRINSICS_LIST
#undef VIRTUAL_INTRINSICS_LIST
#undef OPTIMIZING_INTRINSICS
  }
  return os;
}

static const char kIntegerCacheDescriptor[] = "Ljava/lang/Integer$IntegerCache;";
static const char kIntegerDescriptor[] = "Ljava/lang/Integer;";
static const char kIntegerArrayDescriptor[] = "[Ljava/lang/Integer;";
static const char kLowFieldName[] = "low";
static const char kHighFieldName[] = "high";
static const char kValueFieldName[] = "value";

static ObjPtr<mirror::ObjectArray<mirror::Object>> GetBootImageLiveObjects()
    REQUIRES_SHARED(Locks::mutator_lock_) {
  gc::Heap* heap = Runtime::Current()->GetHeap();
  const std::vector<gc::space::ImageSpace*>& boot_image_spaces = heap->GetBootImageSpaces();
  DCHECK(!boot_image_spaces.empty());
  const ImageHeader& main_header = boot_image_spaces[0]->GetImageHeader();
  ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects =
      ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(
          main_header.GetImageRoot<kWithoutReadBarrier>(ImageHeader::kBootImageLiveObjects));
  DCHECK(boot_image_live_objects != nullptr);
  DCHECK(heap->ObjectIsInBootImageSpace(boot_image_live_objects));
  return boot_image_live_objects;
}

static ObjPtr<mirror::Class> LookupInitializedClass(Thread* self,
                                                    ClassLinker* class_linker,
                                                    const char* descriptor)
        REQUIRES_SHARED(Locks::mutator_lock_) {
  ObjPtr<mirror::Class> klass =
      class_linker->LookupClass(self, descriptor, /* class_loader= */ nullptr);
  DCHECK(klass != nullptr);
  DCHECK(klass->IsInitialized());
  return klass;
}

static ObjPtr<mirror::ObjectArray<mirror::Object>> GetIntegerCacheArray(
    ObjPtr<mirror::Class> cache_class) REQUIRES_SHARED(Locks::mutator_lock_) {
  ArtField* cache_field = cache_class->FindDeclaredStaticField("cache", kIntegerArrayDescriptor);
  DCHECK(cache_field != nullptr);
  return ObjPtr<mirror::ObjectArray<mirror::Object>>::DownCast(cache_field->GetObject(cache_class));
}

static int32_t GetIntegerCacheField(ObjPtr<mirror::Class> cache_class, const char* field_name)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ArtField* field = cache_class->FindDeclaredStaticField(field_name, "I");
  DCHECK(field != nullptr);
  return field->GetInt(cache_class);
}

static bool CheckIntegerCache(Thread* self,
                              ClassLinker* class_linker,
                              ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects,
                              ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_cache)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  DCHECK(boot_image_cache != nullptr);

  // Since we have a cache in the boot image, both java.lang.Integer and
  // java.lang.Integer$IntegerCache must be initialized in the boot image.
  ObjPtr<mirror::Class> cache_class =
      LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor);
  ObjPtr<mirror::Class> integer_class =
      LookupInitializedClass(self, class_linker, kIntegerDescriptor);

  // Check that the current cache is the same as the `boot_image_cache`.
  ObjPtr<mirror::ObjectArray<mirror::Object>> current_cache = GetIntegerCacheArray(cache_class);
  if (current_cache != boot_image_cache) {
    return false;  // Messed up IntegerCache.cache.
  }

  // Check that the range matches the boot image cache length.
  int32_t low = GetIntegerCacheField(cache_class, kLowFieldName);
  int32_t high = GetIntegerCacheField(cache_class, kHighFieldName);
  if (boot_image_cache->GetLength() != high - low + 1) {
    return false;  // Messed up IntegerCache.low or IntegerCache.high.
  }

  // Check that the elements match the boot image intrinsic objects and check their values as well.
  ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
  DCHECK(value_field != nullptr);
  for (int32_t i = 0, len = boot_image_cache->GetLength(); i != len; ++i) {
    ObjPtr<mirror::Object> boot_image_object =
        IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, i);
    DCHECK(Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(boot_image_object));
    // No need for read barrier for comparison with a boot image object.
    ObjPtr<mirror::Object> current_object =
        boot_image_cache->GetWithoutChecks<kVerifyNone, kWithoutReadBarrier>(i);
    if (boot_image_object != current_object) {
      return false;  // Messed up IntegerCache.cache[i]
    }
    if (value_field->GetInt(boot_image_object) != low + i) {
      return false;  // Messed up IntegerCache.cache[i].value.
    }
  }

  return true;
}

void IntrinsicVisitor::ComputeIntegerValueOfLocations(HInvoke* invoke,
                                                      CodeGenerator* codegen,
                                                      Location return_location,
                                                      Location first_argument_location) {
  // The intrinsic will call if it needs to allocate a j.l.Integer.
  LocationSummary::CallKind call_kind = LocationSummary::kCallOnMainOnly;
  const CompilerOptions& compiler_options = codegen->GetCompilerOptions();
  if (compiler_options.IsBootImage()) {
    // Piggyback on the method load kind to determine whether we can use PC-relative addressing.
    // This should cover both the testing config (non-PIC boot image) and codegens that reject
    // PC-relative load kinds and fall back to the runtime call.
    if (!invoke->AsInvokeStaticOrDirect()->HasPcRelativeMethodLoadKind()) {
      return;
    }
    if (!compiler_options.IsImageClass(kIntegerCacheDescriptor) ||
        !compiler_options.IsImageClass(kIntegerDescriptor)) {
      return;
    }
    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
    Thread* self = Thread::Current();
    ScopedObjectAccess soa(self);
    ObjPtr<mirror::Class> cache_class = class_linker->LookupClass(
        self, kIntegerCacheDescriptor, /* class_loader= */ nullptr);
    DCHECK(cache_class != nullptr);
    if (UNLIKELY(!cache_class->IsInitialized())) {
      LOG(WARNING) << "Image class " << cache_class->PrettyDescriptor() << " is uninitialized.";
      return;
    }
    ObjPtr<mirror::Class> integer_class =
        class_linker->LookupClass(self, kIntegerDescriptor, /* class_loader= */ nullptr);
    DCHECK(integer_class != nullptr);
    if (UNLIKELY(!integer_class->IsInitialized())) {
      LOG(WARNING) << "Image class " << integer_class->PrettyDescriptor() << " is uninitialized.";
      return;
    }
    int32_t low = GetIntegerCacheField(cache_class, kLowFieldName);
    int32_t high = GetIntegerCacheField(cache_class, kHighFieldName);
    if (kIsDebugBuild) {
      ObjPtr<mirror::ObjectArray<mirror::Object>> current_cache = GetIntegerCacheArray(cache_class);
      CHECK(current_cache != nullptr);
      CHECK_EQ(current_cache->GetLength(), high - low + 1);
      ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
      CHECK(value_field != nullptr);
      for (int32_t i = 0, len = current_cache->GetLength(); i != len; ++i) {
        ObjPtr<mirror::Object> current_object = current_cache->GetWithoutChecks(i);
        CHECK(current_object != nullptr);
        CHECK_EQ(value_field->GetInt(current_object), low + i);
      }
    }
    if (invoke->InputAt(0)->IsIntConstant()) {
      int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
      if (static_cast<uint32_t>(value) - static_cast<uint32_t>(low) <
          static_cast<uint32_t>(high - low + 1)) {
        // No call, we shall use direct pointer to the Integer object.
        call_kind = LocationSummary::kNoCall;
      }
    }
  } else {
    Runtime* runtime = Runtime::Current();
    if (runtime->GetHeap()->GetBootImageSpaces().empty()) {
      return;  // Running without boot image, cannot use required boot image objects.
    }
    Thread* self = Thread::Current();
    ScopedObjectAccess soa(self);
    ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = GetBootImageLiveObjects();
    ObjPtr<mirror::ObjectArray<mirror::Object>> cache =
        IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects);
    if (cache == nullptr) {
      return;  // No cache in the boot image.
    }
    if (runtime->UseJitCompilation()) {
      if (!CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache)) {
        return;  // The cache was somehow messed up, probably by using reflection.
      }
    } else {
      DCHECK(runtime->IsAotCompiler());
      DCHECK(CheckIntegerCache(self, runtime->GetClassLinker(), boot_image_live_objects, cache));
      if (invoke->InputAt(0)->IsIntConstant()) {
        int32_t value = invoke->InputAt(0)->AsIntConstant()->GetValue();
        // Retrieve the `value` from the lowest cached Integer.
        ObjPtr<mirror::Object> low_integer =
            IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u);
        ObjPtr<mirror::Class> integer_class =
            low_integer->GetClass<kVerifyNone, kWithoutReadBarrier>();
        ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
        DCHECK(value_field != nullptr);
        int32_t low = value_field->GetInt(low_integer);
        if (static_cast<uint32_t>(value) - static_cast<uint32_t>(low) <
            static_cast<uint32_t>(cache->GetLength())) {
          // No call, we shall use direct pointer to the Integer object. Note that we cannot
          // do this for JIT as the "low" can change through reflection before emitting the code.
          call_kind = LocationSummary::kNoCall;
        }
      }
    }
  }

  ArenaAllocator* allocator = invoke->GetBlock()->GetGraph()->GetAllocator();
  LocationSummary* locations = new (allocator) LocationSummary(invoke, call_kind, kIntrinsified);
  if (call_kind == LocationSummary::kCallOnMainOnly) {
    locations->SetInAt(0, Location::RegisterOrConstant(invoke->InputAt(0)));
    locations->AddTemp(first_argument_location);
    locations->SetOut(return_location);
  } else {
    locations->SetInAt(0, Location::ConstantLocation(invoke->InputAt(0)->AsConstant()));
    locations->SetOut(Location::RequiresRegister());
  }
}

static int32_t GetIntegerCacheLowFromIntegerCache(Thread* self, ClassLinker* class_linker)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ObjPtr<mirror::Class> cache_class =
      LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor);
  return GetIntegerCacheField(cache_class, kLowFieldName);
}

static uint32_t CalculateBootImageOffset(ObjPtr<mirror::Object> object)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  gc::Heap* heap = Runtime::Current()->GetHeap();
  DCHECK(heap->ObjectIsInBootImageSpace(object));
  return reinterpret_cast<const uint8_t*>(object.Ptr()) - heap->GetBootImageSpaces()[0]->Begin();
}

inline IntrinsicVisitor::IntegerValueOfInfo::IntegerValueOfInfo()
    : value_offset(0),
      low(0),
      length(0u),
      integer_boot_image_offset(kInvalidReference),
      value_boot_image_reference(kInvalidReference) {}

IntrinsicVisitor::IntegerValueOfInfo IntrinsicVisitor::ComputeIntegerValueOfInfo(
    HInvoke* invoke, const CompilerOptions& compiler_options) {
  // Note that we could cache all of the data looked up here. but there's no good
  // location for it. We don't want to add it to WellKnownClasses, to avoid creating global
  // jni values. Adding it as state to the compiler singleton seems like wrong
  // separation of concerns.
  // The need for this data should be pretty rare though.

  // Note that at this point we can no longer abort the code generation. Therefore,
  // we need to provide data that shall not lead to a crash even if the fields were
  // modified through reflection since ComputeIntegerValueOfLocations() when JITting.

  Runtime* runtime = Runtime::Current();
  ClassLinker* class_linker = runtime->GetClassLinker();
  Thread* self = Thread::Current();
  ScopedObjectAccess soa(self);

  IntegerValueOfInfo info;
  if (compiler_options.IsBootImage()) {
    ObjPtr<mirror::Class> integer_class =
        LookupInitializedClass(self, class_linker, kIntegerDescriptor);
    ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
    DCHECK(value_field != nullptr);
    info.value_offset = value_field->GetOffset().Uint32Value();
    ObjPtr<mirror::Class> cache_class =
        LookupInitializedClass(self, class_linker, kIntegerCacheDescriptor);
    info.low = GetIntegerCacheField(cache_class, kLowFieldName);
    int32_t high = GetIntegerCacheField(cache_class, kHighFieldName);
    info.length = dchecked_integral_cast<uint32_t>(high - info.low + 1);

    info.integer_boot_image_offset = IntegerValueOfInfo::kInvalidReference;
    if (invoke->InputAt(0)->IsIntConstant()) {
      int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue();
      uint32_t index = static_cast<uint32_t>(input_value) - static_cast<uint32_t>(info.low);
      if (index < static_cast<uint32_t>(info.length)) {
        info.value_boot_image_reference = IntrinsicObjects::EncodePatch(
            IntrinsicObjects::PatchType::kIntegerValueOfObject, index);
      } else {
        // Not in the cache.
        info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference;
      }
    } else {
      info.array_data_boot_image_reference =
          IntrinsicObjects::EncodePatch(IntrinsicObjects::PatchType::kIntegerValueOfArray);
    }
  } else {
    ObjPtr<mirror::ObjectArray<mirror::Object>> boot_image_live_objects = GetBootImageLiveObjects();
    ObjPtr<mirror::Object> low_integer =
        IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, 0u);
    ObjPtr<mirror::Class> integer_class = low_integer->GetClass<kVerifyNone, kWithoutReadBarrier>();
    ArtField* value_field = integer_class->FindDeclaredInstanceField(kValueFieldName, "I");
    DCHECK(value_field != nullptr);
    info.value_offset = value_field->GetOffset().Uint32Value();
    if (runtime->UseJitCompilation()) {
      // Use the current `IntegerCache.low` for JIT to avoid truly surprising behavior if the
      // code messes up the `value` field in the lowest cached Integer using reflection.
      info.low = GetIntegerCacheLowFromIntegerCache(self, class_linker);
    } else {
      // For app AOT, the `low_integer->value` should be the same as `IntegerCache.low`.
      info.low = value_field->GetInt(low_integer);
      DCHECK_EQ(info.low, GetIntegerCacheLowFromIntegerCache(self, class_linker));
    }
    // Do not look at `IntegerCache.high`, use the immutable length of the cache array instead.
    info.length = dchecked_integral_cast<uint32_t>(
        IntrinsicObjects::GetIntegerValueOfCache(boot_image_live_objects)->GetLength());

    info.integer_boot_image_offset = CalculateBootImageOffset(integer_class);
    if (invoke->InputAt(0)->IsIntConstant()) {
      int32_t input_value = invoke->InputAt(0)->AsIntConstant()->GetValue();
      uint32_t index = static_cast<uint32_t>(input_value) - static_cast<uint32_t>(info.low);
      if (index < static_cast<uint32_t>(info.length)) {
        ObjPtr<mirror::Object> integer =
            IntrinsicObjects::GetIntegerValueOfObject(boot_image_live_objects, index);
        info.value_boot_image_reference = CalculateBootImageOffset(integer);
      } else {
        // Not in the cache.
        info.value_boot_image_reference = IntegerValueOfInfo::kInvalidReference;
      }
    } else {
      info.array_data_boot_image_reference =
          CalculateBootImageOffset(boot_image_live_objects) +
          IntrinsicObjects::GetIntegerValueOfArrayDataOffset(boot_image_live_objects).Uint32Value();
    }
  }

  return info;
}

void IntrinsicVisitor::AssertNonMovableStringClass() {
  if (kIsDebugBuild) {
    ScopedObjectAccess soa(Thread::Current());
    ObjPtr<mirror::Class> string_class = GetClassRoot<art::mirror::String>();
    CHECK(!art::Runtime::Current()->GetHeap()->IsMovableObject(string_class));
  }
}

}  // namespace art
