# Copyright (C) 2011-2018 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.

require "config"
require "set"

# Interesting invariant, which we take advantage of: branching instructions
# always begin with "b", and no non-branching instructions begin with "b".
# Terminal instructions are "jmp" and "ret".

MACRO_INSTRUCTIONS =
    [
     "emit",
     "addi",
     "andi",
     "andf",
     "andd",
     "lshifti",
     "lshiftp",
     "lshiftq",
     "muli",
     "negi",
     "negp",
     "negq",
     "noti",
     "ori",
     "orf",
     "ord",
     "orh",
     "rshifti",
     "urshifti",
     "rshiftp",
     "urshiftp",
     "rshiftq",
     "urshiftq",
     "lrotatei",
     "lrotateq",
     "rrotatei",
     "rrotateq",
     "subi",
     "xori",
     "loadi",
     "loadis",
     "loadb",
     "loadbsi",
     "loadbsq",
     "loadh",
     "loadhsi",
     "loadhsq",
     "storei",
     "storeh",
     "storeb",
     "loadf",
     "loadd",
     "moved",
     "storef",
     "stored",
     "addf",
     "addd",
     "divf",
     "divd",
     "subf",
     "subd",
     "mulf",
     "muld",
     "sqrtf",
     "sqrtd",
     "floorf",
     "floord",
     "roundf",
     "roundd",
     "truncatef",
     "truncated",
     "truncatef2i",
     "truncatef2q",
     "truncated2q",
     "truncated2i",
     "truncatef2is",
     "truncated2is",
     "truncatef2qs",
     "truncated2qs",
     "ci2d",
     "ci2ds",
     "ci2f",
     "ci2fs",
     "cq2f",
     "cq2fs",
     "cq2d",
     "cq2ds",
     "cd2f",
     "cf2d",
     "fii2d", # usage: fii2d <gpr with least significant bits>, <gpr with most significant bits>, <fpr>
     "fd2ii", # usage: fd2ii <fpr>, <gpr with least significant bits>, <gpr with most significant bits>
     "fq2d",
     "fd2q",
     "bdeq",
     "bdneq",
     "bdgt",
     "bdgteq",
     "bdlt",
     "bdlteq",
     "bdequn",
     "bdnequn",
     "bdgtun",
     "bdgtequn",
     "bdltun",
     "bdltequn",
     "bfeq",
     "bfgt",
     "bflt",
     "bfgtun",
     "bfgtequn",
     "bfltun",
     "bfltequn",
     "btd2i",
     "td2i",
     "bcd2i",
     "movdz",
     "pop",
     "push",
     "move",
     "sxi2q",
     "zxi2q",
     "nop",
     "bieq",
     "bineq",
     "bia",
     "biaeq",
     "bib",
     "bibeq",
     "bigt",
     "bigteq",
     "bilt",
     "bilteq",
     "bbeq",
     "bbneq",
     "bba",
     "bbaeq",
     "bbb",
     "bbbeq",
     "bbgt",
     "bbgteq",
     "bblt",
     "bblteq",
     "btis",
     "btiz",
     "btinz",
     "btbs",
     "btbz",
     "btbnz",
     "jmp",
     "baddio",
     "baddis",
     "baddiz",
     "baddinz",
     "bsubio",
     "bsubis",
     "bsubiz",
     "bsubinz",
     "bmulio",
     "bmulis",
     "bmuliz",
     "bmulinz",
     "borio",
     "boris",
     "boriz",
     "borinz",
     "break",
     "call",
     "ret",
     "cbeq",
     "cbneq",
     "cba",
     "cbaeq",
     "cbb",
     "cbbeq",
     "cbgt",
     "cbgteq",
     "cblt",
     "cblteq",
     "cieq",
     "cineq",
     "cia",
     "ciaeq",
     "cib",
     "cibeq",
     "cigt",
     "cigteq",
     "cilt",
     "cilteq",
     "tis",
     "tiz",
     "tinz",
     "tbs",
     "tbz",
     "tbnz",
     "tps",
     "tpz",
     "tpnz",
     "peek",
     "poke",
     "bpeq",
     "bpneq",
     "bpa",
     "bpaeq",
     "bpb",
     "bpbeq",
     "bpgt",
     "bpgteq",
     "bplt",
     "bplteq",
     "addp",
     "mulp",
     "andp",
     "orp",
     "subp",
     "xorp",
     "loadp",
     "cpeq",
     "cpneq",
     "cpa",
     "cpaeq",
     "cpb",
     "cpbeq",
     "cpgt",
     "cpgteq",
     "cplt",
     "cplteq",
     "storep",
     "btps",
     "btpz",
     "btpnz",
     "baddpo",
     "baddps",
     "baddpz",
     "baddpnz",
     "tqs",
     "tqz",
     "tqnz",
     "bqeq",
     "bqneq",
     "bqa",
     "bqaeq",
     "bqb",
     "bqbeq",
     "bqgt",
     "bqgteq",
     "bqlt",
     "bqlteq",
     "addq",
     "mulq",
     "andq",
     "orq",
     "subq",
     "xorq",
     "loadq",
     "cqeq",
     "cqneq",
     "cqa",
     "cqaeq",
     "cqb",
     "cqbeq",
     "cqgt",
     "cqgteq",
     "cqlt",
     "cqlteq",
     "storeq",
     "btqs",
     "btqz",
     "btqnz",
     "baddqo",
     "baddqs",
     "baddqz",
     "baddqnz",
     "bo",
     "bs",
     "bz",
     "bnz",
     "leai",
     "leap",
     "memfence",
     "tagReturnAddress",
     "untagReturnAddress",
     "removeCodePtrTag",
     "untagArrayPtr",    
     "tzcnti",
     "tzcntq",
     "lzcnti",
     "lzcntq",
     "absf",
     "absd",
     "negf",
     "negd",
     "ceilf",
     "ceild",
     "cfeq",
     "cdeq",
     "cfneq",
     "cfnequn",
     "cdneq",
     "cdnequn",
     "cflt",
     "cdlt",
     "cflteq",
     "cdlteq",
     "cfgt",
     "cdgt",
     "cfgteq",
     "cdgteq",
     "fi2f",
     "ff2i",
     "tls_loadp",
     "tls_storep",
    ]

X86_INSTRUCTIONS =
    [
     "cdqi",
     "idivi",
     "udivi",
     "cqoq",
     "idivq",
     "udivq",
    ]

ARM_INSTRUCTIONS =
    [
     "clrbp",
     "mvlbl",
     "globaladdr"
    ]

ARM64_INSTRUCTIONS =
    [
     "bfiq", # Bit field insert <source reg> <last bit written> <width immediate> <dest reg>
     "pcrtoaddr",   # Address from PC relative offset - adr instruction
     "globaladdr",
     "divi",
     "divis",
     "divq",
     "divqs",
    ]

RISC_INSTRUCTIONS =
    [
     "smulli",  # Multiply two 32-bit words and produce a 64-bit word
     "addis",   # Add integers and set a flag.
     "subis",   # Same, but for subtraction.
     "oris",    # Same, but for bitwise or.
     "addps"    # addis but for pointers.
    ]

MIPS_INSTRUCTIONS =
    [
    "la",
    "movz",
    "movn",
    "setcallreg",
    "slt",
    "sltu",
    "pichdr"
    ]

CXX_INSTRUCTIONS =
    [
     "cloopCrash",              # no operands
     "cloopCallJSFunction",     # operands: callee
     "cloopCallNative",         # operands: callee
     "cloopCallSlowPath",       # operands: callTarget, currentFrame, currentPC
     "cloopCallSlowPathVoid",   # operands: callTarget, currentFrame, currentPC

     # For debugging only:
     # Takes no operands but simply emits whatever follows in // comments as
     # a line of C++ code in the generated LLIntAssembly.h file. This can be
     # used to insert instrumentation into the interpreter loop to inspect
     # variables of interest. Do not leave these instructions in production
     # code.
     "cloopDo",              # no operands
    ]

INSTRUCTIONS = MACRO_INSTRUCTIONS + X86_INSTRUCTIONS + ARM_INSTRUCTIONS + ARM64_INSTRUCTIONS + RISC_INSTRUCTIONS + MIPS_INSTRUCTIONS + CXX_INSTRUCTIONS

INSTRUCTION_SET = INSTRUCTIONS.to_set

def isBranch(instruction)
    instruction =~ /^b/
end

def hasFallThrough(instruction)
    instruction != "ret" and instruction != "jmp"
end

def isPowerOfTwo(value)
    return false if value <= 0
    (value & (value - 1)).zero?
end
