// University of Illinois/NCSA
// Open Source License
//
// Copyright (c) 2013-2015, Advanced Micro Devices, Inc.
// All rights reserved.
//
// Developed by:
//
//     HSA Team
//
//     Advanced Micro Devices, Inc
//
//     www.amd.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal with
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is furnished to do
// so, subject to the following conditions:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimers.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimers in the
//       documentation and/or other materials provided with the distribution.
//
//     * Neither the names of the LLVM Team, University of Illinois at
//       Urbana-Champaign, nor the names of its contributors may be used to
//       endorse or promote products derived from this Software without specific
//       prior written permission.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
// SOFTWARE.
//==============================================================================
// This file describes mappings of HDL properties to C++ constants
// These constants are used by autogenerated code
//==============================================================================

#include "HSAILInstProps.h"
#include "HSAILItems.h"
#include "HSAILExtManager.h"

using namespace HSAIL_PROPS;

namespace HSAIL_ASM
{

//============================================================================
// Autogenerated names of Brig properties

#include "HSAILBrigPropsName_gen.hpp"

//==============================================================================
// Autogenerated code for getting and setting BRIG properties

#include "HSAILBrigPropsAcc_gen.hpp"

//============================================================================
// Operations with Brig properties

const char* validateProp(unsigned propId, unsigned val, unsigned* vals, unsigned length, unsigned model, unsigned profile)
{

    assert(PROP_MINID < propId && propId < PROP_MAXID);
    
    if (profile == BRIG_PROFILE_FULL) return 0;

    if (propId == PROP_FTZ)
    {
        // ftz=0 is allowed only if instruction does not support ftz, i.e. list of possible values only include 0
        // NB: if length=1, there is only one valid value x. If x!=val, we will fail elsewhere.
        if (val == 0 && length > 1) return "Base profile requires ftz modifier to be specified";
    }
    else if (propId == PROP_ROUND)
    {
        switch (val)
        {
        case BRIG_ROUND_NONE:
        case BRIG_ROUND_FLOAT_DEFAULT:
        case BRIG_ROUND_INTEGER_ZERO:
        case BRIG_ROUND_INTEGER_ZERO_SAT:
        case BRIG_ROUND_INTEGER_SIGNALING_ZERO:
        case BRIG_ROUND_INTEGER_SIGNALING_ZERO_SAT: return 0;
        default:
            if (isFloatRounding(val)) return "Base profile only supports default floating-point rounding mode";
            if (isIntRounding(val))   return "Base profile only supports 'zeroi', 'zeroi_sat', 'szeroi' and 'szeroi_sat' integer rounding modes";
            assert(false);
            break;
        }
    }
    return 0;
}

const char* validateProp(unsigned propId, unsigned val, unsigned model, unsigned profile, bool imageExt)
{
    assert(PROP_MINID < propId && propId < PROP_MAXID);
    assert(model   == BRIG_MACHINE_SMALL || model == BRIG_MACHINE_LARGE);
    assert(profile == BRIG_PROFILE_BASE  || profile == BRIG_PROFILE_FULL);

    if (isTypeProp(propId))
    {
        if (isImageExtType(val) && !imageExt)                           return "Image and sampler types are only supported if the IMAGE extension has been specified";
        if (isFullProfileOnlyType(val) && profile != BRIG_PROFILE_FULL) return "f64 and f64x2 types are not supported by the Base pofile";
        if (val == BRIG_TYPE_SIG64 && model != BRIG_MACHINE_LARGE)      return "sig64 type is not supported by the small machine model";
        if (val == BRIG_TYPE_SIG32 && model != BRIG_MACHINE_SMALL)      return "sig32 type is not supported by the large machine model";
    }
    else if (propId == PROP_IMAGESEGMENTMEMORYSCOPE)
    {
        if (val != BRIG_MEMORY_SCOPE_NONE && !imageExt) return "Image segment is only allowed if the IMAGE extension has been specified";
    }

    return 0;
}

// Used by TestGen
const char* validateProp(Inst inst, unsigned propId, unsigned model, unsigned profile, bool imageExt)
{
    assert(PROP_MINID < propId && propId < PROP_MAXID);

    if (propId == PROP_IMAGESEGMENTMEMORYSCOPE || isTypeProp(propId))
    {
        return validateProp(propId, getBrigProp(inst, propId), model, profile, imageExt);
    }
    return 0;
}

bool hasImageExtProps(Inst inst)
{
    if (isImageInst(inst)       ||
        isImageExtType(getType(inst))    ||
        isImageExtType(getSrcType(inst)) ||
        isImageExtType(getImgType(inst))) return true;

    if (InstMemFence i = inst)
    {
        if (i.imageSegmentMemoryScope() != BRIG_MEMORY_SCOPE_NONE) return true;
    }

    for (unsigned i = 0; i < MAX_OPERANDS_NUM; ++i)
    {
        if (OperandAddress addr = inst.operand(i))
        {
            if (DirectiveVariable var = addr.symbol())
            {
                if (isImageExtType(var.elementType())) return true;
            }
        }
    }

    return false;
}

const char* stdPropVal2mnemo(unsigned prop, unsigned val)
{
    switch(prop)
    {
    case PROP_TYPE:
    case PROP_SOURCETYPE:
    case PROP_IMAGETYPE:
    case PROP_COORDTYPE:
    case PROP_SIGNALTYPE:               return type2name(val);
    case PROP_PACK:                     return pack2str(val);
    case PROP_WIDTH:                    return width2str(val);
    case PROP_MEMORYORDER:              return memoryOrder2str(val);

    case PROP_GLOBALSEGMENTMEMORYSCOPE:
    case PROP_GROUPSEGMENTMEMORYSCOPE:
    case PROP_IMAGESEGMENTMEMORYSCOPE:
    case PROP_MEMORYSCOPE:              return memoryScope2str(val);

    case PROP_ROUND:                    return round2str(val);
    case PROP_SEGMENT:                  return segment2str(val);
    case PROP_ALIGN:                    return align2str(val);
    case PROP_COMPARE:                  return compareOperation2str(val);
    case PROP_ATOMICOPERATION:          return atomicOperation2str(val);
    case PROP_SIGNALOPERATION:          return atomicOperation2str(val);
    case PROP_GEOMETRY:                 return imageGeometry2str(val);
    case PROP_IMAGEQUERY:               return imageQuery2str(val);
    case PROP_SAMPLERQUERY:             return samplerQuery2str(val);
    case PROP_OPCODE:                   return opcode2str(val);

    case PROP_FTZ:                      return val? "ftz"    : "none";
    case PROP_ISCONST:                  return val? "const"  : "none";
    case PROP_ISNONULL:                 return val? "nonull" : "none";

    case PROP_EQUIVCLASS:      
        assert(false);
        return 0;

    case PROP_D0:
    case PROP_D1:
    case PROP_S0:
    case PROP_S1:
    case PROP_S2:
    case PROP_S3:
    case PROP_S4:
    case PROP_S5:
    case PROP_TYPESIZE:
    case PROP_STYPESIZE:
    case PROP_OPERAND:
    default:
        assert(false);
        return 0;
    }
}

const char* stdPropVal2str(unsigned prop, unsigned val)
{
    const char* res = 0;

    switch(prop)
    {
    case PROP_TYPE:
    case PROP_SOURCETYPE:
    case PROP_IMAGETYPE:
    case PROP_COORDTYPE:
    case PROP_SIGNALTYPE:      res = (val == BRIG_TYPE_NONE)?         "none" : type2name(val);               break;
    case PROP_PACK:            res = (val == BRIG_PACK_NONE)?         "none" : pack2str(val);                break;
    case PROP_WIDTH:           res = (val == BRIG_WIDTH_NONE)?        "none" : width2str(val);               break;
    case PROP_MEMORYORDER:     res = (val == BRIG_MEMORY_ORDER_NONE)? "none" : memoryOrder2str(val);         break;

    case PROP_GLOBALSEGMENTMEMORYSCOPE:
    case PROP_GROUPSEGMENTMEMORYSCOPE:
    case PROP_IMAGESEGMENTMEMORYSCOPE:
    case PROP_MEMORYSCOPE:     res = (val == BRIG_MEMORY_SCOPE_NONE)? "none" : (val == BRIG_MEMORY_SCOPE_WORKITEM)? "wi"     : memoryScope2str(val); break;

    case PROP_ROUND:           res = (val == BRIG_ROUND_NONE)?        "none" : (val == BRIG_ROUND_FLOAT_DEFAULT) ? "default" : round2str(val);   break;
    case PROP_SEGMENT:         res = (val == BRIG_SEGMENT_NONE)?      "none" : (val == BRIG_SEGMENT_FLAT)?         "flat"    : segment2str(val); break;
    case PROP_ALIGN:           res = (val == BRIG_ALIGNMENT_NONE)?    "none" : (val == BRIG_ALIGNMENT_1)?          "1"       : align2str(val);   break;

    case PROP_FTZ:             res = val? "ftz"    : "none"; break;
    case PROP_ISCONST:         res = val? "const"  : "none"; break;
    case PROP_ISNONULL:        res = val? "nonull" : "none"; break;

    case PROP_COMPARE:         res = compareOperation2str(val); break;
    case PROP_ATOMICOPERATION: res = atomicOperation2str(val);  break;
    case PROP_SIGNALOPERATION: res = atomicOperation2str(val);  break;
    case PROP_GEOMETRY:        res = imageGeometry2str(val);    break;
    case PROP_IMAGEQUERY:      res = imageQuery2str(val);       break;
    case PROP_SAMPLERQUERY:    res = samplerQuery2str(val);     break;
    case PROP_EQUIVCLASS:      res = "";  break; // no sense printing as any value would be valid

    case PROP_OPCODE:          res = opcode2str(val); break;

    case PROP_D0:
    case PROP_D1:

    case PROP_S0:
    case PROP_S1:
    case PROP_S2:
    case PROP_S3:
    case PROP_S4:
    case PROP_S5:              res = operand2str(val); break;

    case PROP_TYPESIZE:  // A special case
    case PROP_STYPESIZE: // A special case
    case PROP_OPERAND:
    default:
        assert(false);
        break;
    }

    return res;
}

const char* prop2str(unsigned prop)
{
    switch(prop)
    {
    case PROP_OPCODE:          return "opcode";
    case PROP_TYPE:            return "type";
    case PROP_SOURCETYPE:      return "src type";
    case PROP_IMAGETYPE:       return "image type";
    case PROP_COORDTYPE:       return "coord type";
    case PROP_SIGNALTYPE:      return "signal type";

    case PROP_PACK:            return "packing";
    case PROP_COMPARE:         return "comparison operator";
    case PROP_ROUND:           return "rounding";
    case PROP_FTZ:             return "modifier"; // output looks like this: "invalid modifier (ftz), expected: none"
    case PROP_ISCONST:         return "modifier"; // output looks like this: "invalid modifier (const), expected: none"
    case PROP_ISNONULL:        return "modifier"; // output looks like this: "invalid modifier (nonull), expected: none"
    case PROP_ALIGN:           return "align";
    case PROP_ATOMICOPERATION: return "atomic operation";
    case PROP_SIGNALOPERATION: return "signal operation";
    case PROP_MEMORYORDER:     return "memory order";

    case PROP_GLOBALSEGMENTMEMORYSCOPE: return "global segment memory scope";
    case PROP_GROUPSEGMENTMEMORYSCOPE:  return "group segment memory scope";
    case PROP_IMAGESEGMENTMEMORYSCOPE:  return "image segment memory scope";
    case PROP_MEMORYSCOPE:              return "memory scope";

    case PROP_GEOMETRY:        return "image geometry";
    case PROP_IMAGEQUERY:      return "image query";
    case PROP_SAMPLERQUERY:    return "sampler query";
    case PROP_SEGMENT:         return "segment";
    case PROP_WIDTH:           return "width";
    case PROP_EQUIVCLASS:      return "equivalence class";

    case PROP_D0:              return "operand 0";
    case PROP_D1:              return "operand 1";

    case PROP_S0:              return "operand 0";
    case PROP_S1:              return "operand 1";
    case PROP_S2:              return "operand 2";
    case PROP_S3:              return "operand 3";
    case PROP_S4:              return "operand 4";
    case PROP_S5:              return "operand 5";

    case PROP_TYPESIZE:  // A special case
    case PROP_STYPESIZE: // A special case
    case PROP_OPERAND:
    default:
        assert(false);
        return "";
    }
}

const char* prop2key(unsigned prop)
{
    switch(prop)
    {
    case PROP_FTZ:         return "modifier";      // output looks like this: "modifier=ftz"
    case PROP_ISCONST:     return "modifier";      // output looks like this: "modifier=const"
    case PROP_ISNONULL:    return "modifier";      // output looks like this: "modifier=nonull"

    case PROP_D0:          return "operand 0";
    case PROP_D1:          return "operand 1";

    case PROP_S0:          return "operand 0";
    case PROP_S1:          return "operand 1";
    case PROP_S2:          return "operand 2";
    case PROP_S3:          return "operand 3";
    case PROP_S4:          return "operand 4";
    case PROP_S5:          return "operand 5";

    case PROP_TYPESIZE:  // A special case
    case PROP_STYPESIZE: // A special case
    case PROP_OPERAND:
        assert(false);
        return "";

    default: return getBrigPropName(prop);
    }
};

unsigned attr2type(Inst inst, unsigned attr)
{
    switch(attr)
    {
    case OPERAND_ATTR_DTYPE:    return inst.type();
    case OPERAND_ATTR_STYPE:    return getSrcType(inst);
    case OPERAND_ATTR_CTYPE:    return getCrdType(inst);
    case OPERAND_ATTR_ITYPE:    return getImgType(inst);

    case OPERAND_ATTR_B1:       return BRIG_TYPE_B1;
    case OPERAND_ATTR_B8:       return BRIG_TYPE_B8;
    case OPERAND_ATTR_B16:      return BRIG_TYPE_B16;
    case OPERAND_ATTR_B32:      return BRIG_TYPE_B32;
    case OPERAND_ATTR_B64:      return BRIG_TYPE_B64;
    case OPERAND_ATTR_B128:     return BRIG_TYPE_B128;

    case OPERAND_ATTR_U8:       return BRIG_TYPE_U8;
    case OPERAND_ATTR_U16:      return BRIG_TYPE_U16;
    case OPERAND_ATTR_U32:      return BRIG_TYPE_U32;
    case OPERAND_ATTR_U64:      return BRIG_TYPE_U64;

    case OPERAND_ATTR_S8:       return BRIG_TYPE_S8;
    case OPERAND_ATTR_S16:      return BRIG_TYPE_S16;
    case OPERAND_ATTR_S32:      return BRIG_TYPE_S32;
    case OPERAND_ATTR_S64:      return BRIG_TYPE_S64;

    case OPERAND_ATTR_F16:      return BRIG_TYPE_F16;
    case OPERAND_ATTR_F32:      return BRIG_TYPE_F32;
    case OPERAND_ATTR_F64:      return BRIG_TYPE_F64;

    case OPERAND_ATTR_SAMP:     return BRIG_TYPE_SAMP;
    case OPERAND_ATTR_SIG32:    return BRIG_TYPE_SIG32;
    case OPERAND_ATTR_SIG64:    return BRIG_TYPE_SIG64;
    case OPERAND_ATTR_P2U:      return packedType2uType(inst.type());

    default:
        assert(false);
        return BRIG_TYPE_NONE;
    }
}

const char* operand2str(unsigned valId)
{
    switch(valId)
    {
    case OPERAND_VAL_NULL:      return "none";

    case OPERAND_VAL_REG:       return "a register";
    case OPERAND_VAL_VEC_2:     return "a vector of 2 registers";
    case OPERAND_VAL_VEC_3:     return "a vector of 3 registers";
    case OPERAND_VAL_VEC_4:     return "a vector of 4 registers";

    case OPERAND_VAL_IMM:       return "an immediate or wavesize"; // NB: wavesize may be rejected later by attr checks
    case OPERAND_VAL_CNST:      return "an immediate";
    case OPERAND_VAL_LAB:       return "a label";
    case OPERAND_VAL_ADDR:      return "an address";
    case OPERAND_VAL_FUNC:      return "a function";
    case OPERAND_VAL_IFUNC:     return "an indirect function";
    case OPERAND_VAL_KERNEL:    return "a kernel";
    case OPERAND_VAL_ARGLIST:   return "a list of arguments";
    case OPERAND_VAL_JUMPTAB:   return "a list of labels";
    case OPERAND_VAL_CALLTAB:   return "a list of functions";
    case OPERAND_VAL_SIGNATURE: return "a signature";
    case OPERAND_VAL_FBARRIER:  return "an fbarrier";

    case OPERAND_VAL_IMM0T2:    return "a 32-bit immediate with value 0, 1 or 2";
    case OPERAND_VAL_IMM0T3:    return "a 32-bit immediate with value 0, 1, 2 or 3";

    default:
        assert(false);
        return "?";
    }
}

bool isBrigEnumProp(unsigned propId)
{
    return BRIG_PROP_MIN_ID < propId && propId < EXT_PROP_MIN_ID && propId != PROP_EQUIVCLASS;
}

}; // namespace HSAIL_ASM
