/*******************************************************************************
 * Copyright IBM Corp. and others 2016
 *
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which accompanies this
 * distribution and is available at https://www.eclipse.org/legal/epl-2.0/
 * or the Apache License, Version 2.0 which accompanies this distribution
 * and is available at https://www.apache.org/licenses/LICENSE-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License, v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception [1] and GNU General Public
 * License, version 2 with the OpenJDK Assembly Exception [2].
 *
 * [1] https://www.gnu.org/software/classpath/license.html
 * [2] https://openjdk.org/legal/assembly-exception.html
 *
 * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0
 *******************************************************************************/

#ifndef OMR_VIRTUALMACHINEREGISTERINSTRUCT_INCL
#define OMR_VIRTUALMACHINEREGISTERINSTRUCT_INCL

#include "ilgen/VirtualMachineRegister.hpp"
#include "ilgen/VirtualMachineRegisterInStruct.hpp"
#include "ilgen/IlBuilder.hpp"

namespace TR {
class VirtualMachineRegisterInStruct;
}

namespace OMR {

/**
 * @brief used to represent virtual machine variables that are maintained in a structure stored in a local variable,
 * such as a thread or frame object passed as a parameter to the method
 *
 * The value does not need to be a virtual machine register, but often it is the registers
 * of the virtual machine that are candidates for VirtualMachineRegisterInStruct. An
 * alternative is VirtualMachineRegister, which can be more convenient if the virtual
 * machine value is stored in a more arbitrary place or in a structure that isn't readily
 * accessible inside the compiled method.
 * VirtualMachineRegisterInStruct is a subclass of VirtualMachineRegister
 *
 * The simulated register value is simply stored in a single local variable, which
 * gives the compiler visibility to all changes to the register (and enables
 * optimization / simplification). Because there is just a single local variable,
 * the Merge() function does not need to do anything (the value is accessible from
 * the same location at all locations). The Commit() and Reload() functions simply
 * move the value back and forth between the local variable and the structure that
 * holds the actual virtual machine state.
 */

class VirtualMachineRegisterInStruct : public TR::VirtualMachineRegister {
public:
    /**
     * @brief public constructor used to create a virtual machine state variable from struct
     * @param b a builder object where the first Reload operations will be inserted
     * @param structName the name of the struct type that holds the virtual machine state variable
     * @param localNameHoldingStructAddress is the name of a local variable holding the struct base address; it must
     * have been stored in this name before control will reach the builder "b"
     * @param fieldName name of the field in "structName" that holds the virtual machine state variable
     * @param localName the name of the local variable where the simulated value is to be stored
     */
    VirtualMachineRegisterInStruct(TR::IlBuilder *b, const char * const structName,
        const char * const localNameHoldingStructAddress, const char * const fieldName, const char * const localName)
        : TR::VirtualMachineRegister(localName)
        , _structName(structName)
        , _fieldName(fieldName)
        , _localNameHoldingStructAddress(localNameHoldingStructAddress)
    {
        _integerTypeForAdjustments = b->typeDictionary()->GetFieldType(structName, fieldName);
        if (_integerTypeForAdjustments->isPointer()) {
            TR::IlType *baseType = _integerTypeForAdjustments->baseType();
            _integerTypeForAdjustments = b->typeDictionary()->getWord();
            _isAdjustable = true;
            _adjustByStep = static_cast<uint32_t>(baseType->getSize());
        } else {
            _isAdjustable = false;
            _adjustByStep = 0;
        }
        Reload(b);
    }

    /**
     * @brief public constructor used to create a virtual machine state variable from struct
     * @param structName the name of the struct type that holds the virtual machine state variable
     * @param localNameHoldingStructAddress is the name of a local variable holding the struct base address; it must
     * have been stored in this name before control will reach the builder "b"
     * @param fieldName name of the field in "structName" that holds the virtual machine state variable
     * @param localName the name of the local variable where the simulated value is to be stored
     */
    VirtualMachineRegisterInStruct(const char * const structName, const char * const localNameHoldingStructAddress,
        const char * const fieldName, const char * const localName)
        : TR::VirtualMachineRegister(localName)
        , _structName(structName)
        , _fieldName(fieldName)
        , _localNameHoldingStructAddress(localNameHoldingStructAddress)
    {}

    /**
     * @brief write the simulated register value to the proper field in the struct
     * @param b a builder object where the operations to do the write will be inserted
     */
    virtual void Commit(TR::IlBuilder *b)
    {
        b->StoreIndirect(_structName, _fieldName, b->Load(_localNameHoldingStructAddress), b->Load(_localName));
    }

    /**
     * @brief read the simulated register value from the proper field in the struct
     * @param b a builder object where the operations to do the write will be inserted
     */
    virtual void Reload(TR::IlBuilder *b)
    {
        b->Store(_localName, b->LoadIndirect(_structName, _fieldName, b->Load(_localNameHoldingStructAddress)));
    }

    /**
     * @brief create an identical copy of the current object.
     * @returns the copy of the current object
     */
    virtual TR::VirtualMachineState *MakeCopy();

    /**
     * @brief returns the client object associated with this object
     */
    virtual void *client();

    /**
     * @brief Set the Client Allocator function
     */
    static void setClientAllocator(ClientAllocator allocator) { _clientAllocator = allocator; }

    /**
     * @brief Set the Get Impl function
     *
     * @param getter function pointer to the impl getter
     */
    static void setGetImpl(ImplGetter getter) { _getImpl = getter; }

protected:
    const char * const _structName;
    const char * const _fieldName;
    const char * const _localNameHoldingStructAddress;

private:
    static ClientAllocator _clientAllocator;
    static ImplGetter _getImpl;
};
} // namespace OMR

#endif // !defined(OMR_VIRTUALMACHINEREGISTERINSTRUCT_INCL)
