void InstructionCodeGeneratorX86::VisitArraySet()

in compiler/optimizing/code_generator_x86.cc [5311:5564]


void InstructionCodeGeneratorX86::VisitArraySet(HArraySet* instruction) {
  LocationSummary* locations = instruction->GetLocations();
  Location array_loc = locations->InAt(0);
  Register array = array_loc.AsRegister<Register>();
  Location index = locations->InAt(1);
  Location value = locations->InAt(2);
  Primitive::Type value_type = instruction->GetComponentType();
  uint32_t class_offset = mirror::Object::ClassOffset().Int32Value();
  uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value();
  uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value();
  bool may_need_runtime_call_for_type_check = instruction->NeedsTypeCheck();
  bool needs_write_barrier =
      CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue());

  switch (value_type) {
    case Primitive::kPrimBoolean:
    case Primitive::kPrimByte: {
      uint32_t offset = mirror::Array::DataOffset(sizeof(uint8_t)).Uint32Value();
      Address address = index.IsConstant()
          ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_1) + offset)
          : Address(array, index.AsRegister<Register>(), TIMES_1, offset);
      if (value.IsRegister()) {
        __ movb(address, value.AsRegister<ByteRegister>());
      } else {
        __ movb(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
      }
      codegen_->MaybeRecordImplicitNullCheck(instruction);
      break;
    }

    case Primitive::kPrimShort:
    case Primitive::kPrimChar: {
      uint32_t offset = mirror::Array::DataOffset(sizeof(uint16_t)).Uint32Value();
      Address address = index.IsConstant()
          ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_2) + offset)
          : Address(array, index.AsRegister<Register>(), TIMES_2, offset);
      if (value.IsRegister()) {
        __ movw(address, value.AsRegister<Register>());
      } else {
        __ movw(address, Immediate(value.GetConstant()->AsIntConstant()->GetValue()));
      }
      codegen_->MaybeRecordImplicitNullCheck(instruction);
      break;
    }

    case Primitive::kPrimNot: {
      uint32_t offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
      Address address = index.IsConstant()
          ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
          : Address(array, index.AsRegister<Register>(), TIMES_4, offset);

      if (!value.IsRegister()) {
        // Just setting null.
        DCHECK(instruction->InputAt(2)->IsNullConstant());
        DCHECK(value.IsConstant()) << value;
        __ movl(address, Immediate(0));
        codegen_->MaybeRecordImplicitNullCheck(instruction);
        DCHECK(!needs_write_barrier);
        DCHECK(!may_need_runtime_call_for_type_check);
        break;
      }

      DCHECK(needs_write_barrier);
      Register register_value = value.AsRegister<Register>();
      NearLabel done, not_null, do_put;
      SlowPathCode* slow_path = nullptr;
      Register temp = locations->GetTemp(0).AsRegister<Register>();
      if (may_need_runtime_call_for_type_check) {
        slow_path = new (GetGraph()->GetArena()) ArraySetSlowPathX86(instruction);
        codegen_->AddSlowPath(slow_path);
        if (instruction->GetValueCanBeNull()) {
          __ testl(register_value, register_value);
          __ j(kNotEqual, &not_null);
          __ movl(address, Immediate(0));
          codegen_->MaybeRecordImplicitNullCheck(instruction);
          __ jmp(&done);
          __ Bind(&not_null);
        }

        if (kEmitCompilerReadBarrier) {
          // When read barriers are enabled, the type checking
          // instrumentation requires two read barriers:
          //
          //   __ movl(temp2, temp);
          //   // /* HeapReference<Class> */ temp = temp->component_type_
          //   __ movl(temp, Address(temp, component_offset));
          //   codegen_->GenerateReadBarrierSlow(
          //       instruction, temp_loc, temp_loc, temp2_loc, component_offset);
          //
          //   // /* HeapReference<Class> */ temp2 = register_value->klass_
          //   __ movl(temp2, Address(register_value, class_offset));
          //   codegen_->GenerateReadBarrierSlow(
          //       instruction, temp2_loc, temp2_loc, value, class_offset, temp_loc);
          //
          //   __ cmpl(temp, temp2);
          //
          // However, the second read barrier may trash `temp`, as it
          // is a temporary register, and as such would not be saved
          // along with live registers before calling the runtime (nor
          // restored afterwards).  So in this case, we bail out and
          // delegate the work to the array set slow path.
          //
          // TODO: Extend the register allocator to support a new
          // "(locally) live temp" location so as to avoid always
          // going into the slow path when read barriers are enabled.
          __ jmp(slow_path->GetEntryLabel());
        } else {
          // /* HeapReference<Class> */ temp = array->klass_
          __ movl(temp, Address(array, class_offset));
          codegen_->MaybeRecordImplicitNullCheck(instruction);
          __ MaybeUnpoisonHeapReference(temp);

          // /* HeapReference<Class> */ temp = temp->component_type_
          __ movl(temp, Address(temp, component_offset));
          // If heap poisoning is enabled, no need to unpoison `temp`
          // nor the object reference in `register_value->klass`, as
          // we are comparing two poisoned references.
          __ cmpl(temp, Address(register_value, class_offset));

          if (instruction->StaticTypeOfArrayIsObjectArray()) {
            __ j(kEqual, &do_put);
            // If heap poisoning is enabled, the `temp` reference has
            // not been unpoisoned yet; unpoison it now.
            __ MaybeUnpoisonHeapReference(temp);

            // /* HeapReference<Class> */ temp = temp->super_class_
            __ movl(temp, Address(temp, super_offset));
            // If heap poisoning is enabled, no need to unpoison
            // `temp`, as we are comparing against null below.
            __ testl(temp, temp);
            __ j(kNotEqual, slow_path->GetEntryLabel());
            __ Bind(&do_put);
          } else {
            __ j(kNotEqual, slow_path->GetEntryLabel());
          }
        }
      }

      if (kPoisonHeapReferences) {
        __ movl(temp, register_value);
        __ PoisonHeapReference(temp);
        __ movl(address, temp);
      } else {
        __ movl(address, register_value);
      }
      if (!may_need_runtime_call_for_type_check) {
        codegen_->MaybeRecordImplicitNullCheck(instruction);
      }

      Register card = locations->GetTemp(1).AsRegister<Register>();
      codegen_->MarkGCCard(
          temp, card, array, value.AsRegister<Register>(), instruction->GetValueCanBeNull());
      __ Bind(&done);

      if (slow_path != nullptr) {
        __ Bind(slow_path->GetExitLabel());
      }

      break;
    }

    case Primitive::kPrimInt: {
      uint32_t offset = mirror::Array::DataOffset(sizeof(int32_t)).Uint32Value();
      Address address = index.IsConstant()
          ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
          : Address(array, index.AsRegister<Register>(), TIMES_4, offset);
      if (value.IsRegister()) {
        __ movl(address, value.AsRegister<Register>());
      } else {
        DCHECK(value.IsConstant()) << value;
        int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant());
        __ movl(address, Immediate(v));
      }
      codegen_->MaybeRecordImplicitNullCheck(instruction);
      break;
    }

    case Primitive::kPrimLong: {
      uint32_t data_offset = mirror::Array::DataOffset(sizeof(int64_t)).Uint32Value();
      if (index.IsConstant()) {
        size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
        if (value.IsRegisterPair()) {
          __ movl(Address(array, offset), value.AsRegisterPairLow<Register>());
          codegen_->MaybeRecordImplicitNullCheck(instruction);
          __ movl(Address(array, offset + kX86WordSize), value.AsRegisterPairHigh<Register>());
        } else {
          DCHECK(value.IsConstant());
          int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
          __ movl(Address(array, offset), Immediate(Low32Bits(val)));
          codegen_->MaybeRecordImplicitNullCheck(instruction);
          __ movl(Address(array, offset + kX86WordSize), Immediate(High32Bits(val)));
        }
      } else {
        if (value.IsRegisterPair()) {
          __ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset),
                  value.AsRegisterPairLow<Register>());
          codegen_->MaybeRecordImplicitNullCheck(instruction);
          __ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
                  value.AsRegisterPairHigh<Register>());
        } else {
          DCHECK(value.IsConstant());
          int64_t val = value.GetConstant()->AsLongConstant()->GetValue();
          __ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset),
                  Immediate(Low32Bits(val)));
          codegen_->MaybeRecordImplicitNullCheck(instruction);
          __ movl(Address(array, index.AsRegister<Register>(), TIMES_8, data_offset + kX86WordSize),
                  Immediate(High32Bits(val)));
        }
      }
      break;
    }

    case Primitive::kPrimFloat: {
      uint32_t offset = mirror::Array::DataOffset(sizeof(float)).Uint32Value();
      Address address = index.IsConstant()
          ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + offset)
          : Address(array, index.AsRegister<Register>(), TIMES_4, offset);
      if (value.IsFpuRegister()) {
        __ movss(address, value.AsFpuRegister<XmmRegister>());
      } else {
        DCHECK(value.IsConstant());
        int32_t v = bit_cast<int32_t, float>(value.GetConstant()->AsFloatConstant()->GetValue());
        __ movl(address, Immediate(v));
      }
      codegen_->MaybeRecordImplicitNullCheck(instruction);
      break;
    }

    case Primitive::kPrimDouble: {
      uint32_t offset = mirror::Array::DataOffset(sizeof(double)).Uint32Value();
      Address address = index.IsConstant()
          ? Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + offset)
          : Address(array, index.AsRegister<Register>(), TIMES_8, offset);
      if (value.IsFpuRegister()) {
        __ movsd(address, value.AsFpuRegister<XmmRegister>());
      } else {
        DCHECK(value.IsConstant());
        Address address_hi = index.IsConstant() ?
            Address(array, (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) +
                           offset + kX86WordSize) :
            Address(array, index.AsRegister<Register>(), TIMES_8, offset + kX86WordSize);
        int64_t v = bit_cast<int64_t, double>(value.GetConstant()->AsDoubleConstant()->GetValue());
        __ movl(address, Immediate(Low32Bits(v)));
        codegen_->MaybeRecordImplicitNullCheck(instruction);
        __ movl(address_hi, Immediate(High32Bits(v)));
      }
      break;
    }

    case Primitive::kPrimVoid:
      LOG(FATAL) << "Unreachable type " << instruction->GetType();
      UNREACHABLE();
  }
}