// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Test embedded constant pool builder code.

#include "src/v8.h"

#include "src/assembler.h"
#include "test/cctest/cctest.h"

using namespace v8::internal;

const ConstantPoolEntry::Type kPtrType = ConstantPoolEntry::INTPTR;
const ConstantPoolEntry::Type kDblType = ConstantPoolEntry::DOUBLE;
const ConstantPoolEntry::Access kRegAccess = ConstantPoolEntry::REGULAR;
const ConstantPoolEntry::Access kOvflAccess = ConstantPoolEntry::OVERFLOWED;

const int kReachBits = 6;  // Use reach of 64-bytes to test overflow.
const int kReach = 1 << kReachBits;


TEST(ConstantPoolPointers) {
  ConstantPoolBuilder builder(kReachBits, kReachBits);
  const int kRegularCount = kReach / kPointerSize;
  ConstantPoolEntry::Access access;
  int pos = 0;
  intptr_t value = 0;
  bool sharing_ok = true;

  CHECK(builder.IsEmpty());
  while (builder.NextAccess(kPtrType) == kRegAccess) {
    access = builder.AddEntry(pos++, value++, sharing_ok);
    CHECK_EQ(access, kRegAccess);
  }
  CHECK(!builder.IsEmpty());
  CHECK_EQ(pos, kRegularCount);

  access = builder.AddEntry(pos, value, sharing_ok);
  CHECK_EQ(access, kOvflAccess);
}


TEST(ConstantPoolDoubles) {
  ConstantPoolBuilder builder(kReachBits, kReachBits);
  const int kRegularCount = kReach / kDoubleSize;
  ConstantPoolEntry::Access access;
  int pos = 0;
  double value = 0.0;

  CHECK(builder.IsEmpty());
  while (builder.NextAccess(kDblType) == kRegAccess) {
    access = builder.AddEntry(pos++, value);
    value += 0.5;
    CHECK_EQ(access, kRegAccess);
  }
  CHECK(!builder.IsEmpty());
  CHECK_EQ(pos, kRegularCount);

  access = builder.AddEntry(pos, value);
  CHECK_EQ(access, kOvflAccess);
}


TEST(ConstantPoolMixedTypes) {
  ConstantPoolBuilder builder(kReachBits, kReachBits);
  const int kRegularCount = (((kReach / (kDoubleSize + kPointerSize)) * 2) +
                             ((kPointerSize < kDoubleSize) ? 1 : 0));
  ConstantPoolEntry::Type type = kPtrType;
  ConstantPoolEntry::Access access;
  int pos = 0;
  intptr_t ptrValue = 0;
  double dblValue = 0.0;
  bool sharing_ok = true;

  CHECK(builder.IsEmpty());
  while (builder.NextAccess(type) == kRegAccess) {
    if (type == kPtrType) {
      access = builder.AddEntry(pos++, ptrValue++, sharing_ok);
      type = kDblType;
    } else {
      access = builder.AddEntry(pos++, dblValue);
      dblValue += 0.5;
      type = kPtrType;
    }
    CHECK_EQ(access, kRegAccess);
  }
  CHECK(!builder.IsEmpty());
  CHECK_EQ(pos, kRegularCount);

  access = builder.AddEntry(pos++, ptrValue, sharing_ok);
  CHECK_EQ(access, kOvflAccess);
  access = builder.AddEntry(pos, dblValue);
  CHECK_EQ(access, kOvflAccess);
}


TEST(ConstantPoolMixedReach) {
  const int ptrReachBits = kReachBits + 2;
  const int ptrReach = 1 << ptrReachBits;
  const int dblReachBits = kReachBits;
  const int dblReach = kReach;
  const int dblRegularCount =
      Min(dblReach / kDoubleSize, ptrReach / (kDoubleSize + kPointerSize));
  const int ptrRegularCount =
      ((ptrReach - (dblRegularCount * (kDoubleSize + kPointerSize))) /
       kPointerSize) +
      dblRegularCount;
  ConstantPoolBuilder builder(ptrReachBits, dblReachBits);
  ConstantPoolEntry::Access access;
  int pos = 0;
  intptr_t ptrValue = 0;
  double dblValue = 0.0;
  bool sharing_ok = true;
  int ptrCount = 0;
  int dblCount = 0;

  CHECK(builder.IsEmpty());
  while (builder.NextAccess(kDblType) == kRegAccess) {
    access = builder.AddEntry(pos++, dblValue);
    dblValue += 0.5;
    dblCount++;
    CHECK_EQ(access, kRegAccess);

    access = builder.AddEntry(pos++, ptrValue++, sharing_ok);
    ptrCount++;
    CHECK_EQ(access, kRegAccess);
  }
  CHECK(!builder.IsEmpty());
  CHECK_EQ(dblCount, dblRegularCount);

  while (ptrCount < ptrRegularCount) {
    access = builder.AddEntry(pos++, dblValue);
    dblValue += 0.5;
    CHECK_EQ(access, kOvflAccess);

    access = builder.AddEntry(pos++, ptrValue++, sharing_ok);
    ptrCount++;
    CHECK_EQ(access, kRegAccess);
  }
  CHECK_EQ(builder.NextAccess(kPtrType), kOvflAccess);

  access = builder.AddEntry(pos++, ptrValue, sharing_ok);
  CHECK_EQ(access, kOvflAccess);
  access = builder.AddEntry(pos, dblValue);
  CHECK_EQ(access, kOvflAccess);
}


TEST(ConstantPoolSharing) {
  ConstantPoolBuilder builder(kReachBits, kReachBits);
  const int kRegularCount = (((kReach / (kDoubleSize + kPointerSize)) * 2) +
                             ((kPointerSize < kDoubleSize) ? 1 : 0));
  ConstantPoolEntry::Access access;

  CHECK(builder.IsEmpty());

  ConstantPoolEntry::Type type = kPtrType;
  int pos = 0;
  intptr_t ptrValue = 0;
  double dblValue = 0.0;
  bool sharing_ok = true;
  while (builder.NextAccess(type) == kRegAccess) {
    if (type == kPtrType) {
      access = builder.AddEntry(pos++, ptrValue++, sharing_ok);
      type = kDblType;
    } else {
      access = builder.AddEntry(pos++, dblValue);
      dblValue += 0.5;
      type = kPtrType;
    }
    CHECK_EQ(access, kRegAccess);
  }
  CHECK(!builder.IsEmpty());
  CHECK_EQ(pos, kRegularCount);

  type = kPtrType;
  ptrValue = 0;
  dblValue = 0.0;
  while (pos < kRegularCount * 2) {
    if (type == kPtrType) {
      access = builder.AddEntry(pos++, ptrValue++, sharing_ok);
      type = kDblType;
    } else {
      access = builder.AddEntry(pos++, dblValue);
      dblValue += 0.5;
      type = kPtrType;
    }
    CHECK_EQ(access, kRegAccess);
  }

  access = builder.AddEntry(pos++, ptrValue, sharing_ok);
  CHECK_EQ(access, kOvflAccess);
  access = builder.AddEntry(pos, dblValue);
  CHECK_EQ(access, kOvflAccess);
}


TEST(ConstantPoolNoSharing) {
  ConstantPoolBuilder builder(kReachBits, kReachBits);
  const int kRegularCount = (((kReach / (kDoubleSize + kPointerSize)) * 2) +
                             ((kPointerSize < kDoubleSize) ? 1 : 0));
  ConstantPoolEntry::Access access;

  CHECK(builder.IsEmpty());

  ConstantPoolEntry::Type type = kPtrType;
  int pos = 0;
  intptr_t ptrValue = 0;
  double dblValue = 0.0;
  bool sharing_ok = false;
  while (builder.NextAccess(type) == kRegAccess) {
    if (type == kPtrType) {
      access = builder.AddEntry(pos++, ptrValue++, sharing_ok);
      type = kDblType;
    } else {
      access = builder.AddEntry(pos++, dblValue);
      dblValue += 0.5;
      type = kPtrType;
    }
    CHECK_EQ(access, kRegAccess);
  }
  CHECK(!builder.IsEmpty());
  CHECK_EQ(pos, kRegularCount);

  type = kPtrType;
  ptrValue = 0;
  dblValue = 0.0;
  sharing_ok = true;
  while (pos < kRegularCount * 2) {
    if (type == kPtrType) {
      access = builder.AddEntry(pos++, ptrValue++, sharing_ok);
      type = kDblType;
      CHECK_EQ(access, kOvflAccess);
    } else {
      access = builder.AddEntry(pos++, dblValue);
      dblValue += 0.5;
      type = kPtrType;
      CHECK_EQ(access, kRegAccess);
    }
  }

  access = builder.AddEntry(pos++, ptrValue, sharing_ok);
  CHECK_EQ(access, kOvflAccess);
  access = builder.AddEntry(pos, dblValue);
  CHECK_EQ(access, kOvflAccess);
}
