#
# x86 CPU recognition
#
#  Copyright (C) 2002-2007  Peter Johnson
#
# 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 THE AUTHOR AND OTHER 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 THE AUTHOR OR OTHER 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.
%{
#include <util.h>

#include <ctype.h>
#include <libyasm.h>
#include <libyasm/phash.h>

#include "modules/arch/x86/x86arch.h"

#define PROC_8086	0
#define PROC_186	1
#define PROC_286	2
#define PROC_386	3
#define PROC_486	4
#define PROC_586	5
#define PROC_686	6
#define PROC_p2		7
#define PROC_p3		8
#define PROC_p4		9
#define PROC_prescott	10
#define PROC_conroe	11
#define PROC_penryn	12
#define PROC_nehalem	13
#define PROC_westmere   14
#define PROC_sandybridge 15

static void
x86_cpu_intel(wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data)
{
    BitVector_Empty(cpu);

    BitVector_Bit_On(cpu, CPU_Priv);
    if (data >= PROC_286)
        BitVector_Bit_On(cpu, CPU_Prot);
    if (data >= PROC_386)
        BitVector_Bit_On(cpu, CPU_SMM);
    if (data >= PROC_sandybridge)
        BitVector_Bit_On(cpu, CPU_AVX);
    if (data >= PROC_westmere) {
        BitVector_Bit_On(cpu, CPU_AES);
        BitVector_Bit_On(cpu, CPU_CLMUL);
    }
    if (data >= PROC_nehalem) {
        BitVector_Bit_On(cpu, CPU_SSE42);
        BitVector_Bit_On(cpu, CPU_XSAVE);
    }
    if (data >= PROC_penryn)
        BitVector_Bit_On(cpu, CPU_SSE41);
    if (data >= PROC_conroe)
        BitVector_Bit_On(cpu, CPU_SSSE3);
    if (data >= PROC_prescott)
        BitVector_Bit_On(cpu, CPU_SSE3);
    if (data >= PROC_p4)
        BitVector_Bit_On(cpu, CPU_SSE2);
    if (data >= PROC_p3)
        BitVector_Bit_On(cpu, CPU_SSE);
    if (data >= PROC_p2)
        BitVector_Bit_On(cpu, CPU_MMX);
    if (data >= PROC_486)
        BitVector_Bit_On(cpu, CPU_FPU);
    if (data >= PROC_prescott)
        BitVector_Bit_On(cpu, CPU_EM64T);

    if (data >= PROC_p4)
        BitVector_Bit_On(cpu, CPU_P4);
    if (data >= PROC_p3)
        BitVector_Bit_On(cpu, CPU_P3);
    if (data >= PROC_686)
        BitVector_Bit_On(cpu, CPU_686);
    if (data >= PROC_586)
        BitVector_Bit_On(cpu, CPU_586);
    if (data >= PROC_486)
        BitVector_Bit_On(cpu, CPU_486);
    if (data >= PROC_386)
        BitVector_Bit_On(cpu, CPU_386);
    if (data >= PROC_286)
        BitVector_Bit_On(cpu, CPU_286);
    if (data >= PROC_186)
        BitVector_Bit_On(cpu, CPU_186);
    BitVector_Bit_On(cpu, CPU_086);

    /* Use Intel long NOPs if 686 or better */
    if (data >= PROC_686)
        arch_x86->nop = X86_NOP_INTEL;
    else
        arch_x86->nop = X86_NOP_BASIC;
}

static void
x86_cpu_ia64(wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data)
{
    BitVector_Empty(cpu);
    BitVector_Bit_On(cpu, CPU_Priv);
    BitVector_Bit_On(cpu, CPU_Prot);
    BitVector_Bit_On(cpu, CPU_SMM);
    BitVector_Bit_On(cpu, CPU_SSE2);
    BitVector_Bit_On(cpu, CPU_SSE);
    BitVector_Bit_On(cpu, CPU_MMX);
    BitVector_Bit_On(cpu, CPU_FPU);
    BitVector_Bit_On(cpu, CPU_IA64);
    BitVector_Bit_On(cpu, CPU_P4);
    BitVector_Bit_On(cpu, CPU_P3);
    BitVector_Bit_On(cpu, CPU_686);
    BitVector_Bit_On(cpu, CPU_586);
    BitVector_Bit_On(cpu, CPU_486);
    BitVector_Bit_On(cpu, CPU_386);
    BitVector_Bit_On(cpu, CPU_286);
    BitVector_Bit_On(cpu, CPU_186);
    BitVector_Bit_On(cpu, CPU_086);
}

#define PROC_bulldozer	11
#define PROC_k10    10
#define PROC_venice 9
#define PROC_hammer 8
#define PROC_k7     7
#define PROC_k6     6

static void
x86_cpu_amd(wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data)
{
    BitVector_Empty(cpu);

    BitVector_Bit_On(cpu, CPU_Priv);
    BitVector_Bit_On(cpu, CPU_Prot);
    BitVector_Bit_On(cpu, CPU_SMM);
    BitVector_Bit_On(cpu, CPU_3DNow);
    if (data >= PROC_bulldozer) {
        BitVector_Bit_On(cpu, CPU_XOP);
        BitVector_Bit_On(cpu, CPU_FMA4);
    }
    if (data >= PROC_k10)
        BitVector_Bit_On(cpu, CPU_SSE4a);
    if (data >= PROC_venice)
        BitVector_Bit_On(cpu, CPU_SSE3);
    if (data >= PROC_hammer)
        BitVector_Bit_On(cpu, CPU_SSE2);
    if (data >= PROC_k7)
        BitVector_Bit_On(cpu, CPU_SSE);
    if (data >= PROC_k6)
        BitVector_Bit_On(cpu, CPU_MMX);
    BitVector_Bit_On(cpu, CPU_FPU);

    if (data >= PROC_hammer)
        BitVector_Bit_On(cpu, CPU_Hammer);
    if (data >= PROC_k7)
        BitVector_Bit_On(cpu, CPU_Athlon);
    if (data >= PROC_k6)
        BitVector_Bit_On(cpu, CPU_K6);
    BitVector_Bit_On(cpu, CPU_686);
    BitVector_Bit_On(cpu, CPU_586);
    BitVector_Bit_On(cpu, CPU_486);
    BitVector_Bit_On(cpu, CPU_386);
    BitVector_Bit_On(cpu, CPU_286);
    BitVector_Bit_On(cpu, CPU_186);
    BitVector_Bit_On(cpu, CPU_086);

    /* Use AMD long NOPs if k6 or better */
    if (data >= PROC_k6)
        arch_x86->nop = X86_NOP_AMD;
    else
        arch_x86->nop = X86_NOP_BASIC;
}

static void
x86_cpu_set(wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data)
{
    BitVector_Bit_On(cpu, data);
}

static void
x86_cpu_clear(wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data)
{
    BitVector_Bit_Off(cpu, data);
}

static void
x86_cpu_set_sse4(wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data)
{
    BitVector_Bit_On(cpu, CPU_SSE41);
    BitVector_Bit_On(cpu, CPU_SSE42);
}

static void
x86_cpu_clear_sse4(wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data)
{
    BitVector_Bit_Off(cpu, CPU_SSE41);
    BitVector_Bit_Off(cpu, CPU_SSE42);
}

static void
x86_nop(wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data)
{
    arch_x86->nop = data;
}

%}
%ignore-case
%language=ANSI-C
%compare-strncmp
%readonly-tables
%enum
%struct-type
%define hash-function-name cpu_hash
%define lookup-function-name cpu_find
struct cpu_parse_data {
    const char *name;
    void (*handler) (wordptr cpu, yasm_arch_x86 *arch_x86, unsigned int data);
    unsigned int data;
};
%%
8086,		x86_cpu_intel,	PROC_8086
186,		x86_cpu_intel,	PROC_186
80186,		x86_cpu_intel,	PROC_186
i186,		x86_cpu_intel,	PROC_186
286,		x86_cpu_intel,	PROC_286
80286,		x86_cpu_intel,	PROC_286
i286,		x86_cpu_intel,	PROC_286
386,		x86_cpu_intel,	PROC_386
80386,		x86_cpu_intel,	PROC_386
i386,		x86_cpu_intel,	PROC_386
486,		x86_cpu_intel,	PROC_486
80486,		x86_cpu_intel,	PROC_486
i486,		x86_cpu_intel,	PROC_486
586,		x86_cpu_intel,	PROC_586
i586,		x86_cpu_intel,	PROC_586
pentium,	x86_cpu_intel,	PROC_586
p5,		x86_cpu_intel,	PROC_586
686,		x86_cpu_intel,	PROC_686
i686,		x86_cpu_intel,	PROC_686
p6,		x86_cpu_intel,	PROC_686
ppro,		x86_cpu_intel,	PROC_686
pentiumpro,	x86_cpu_intel,	PROC_686
p2,		x86_cpu_intel,	PROC_p2
pentium2,	x86_cpu_intel,	PROC_p2
pentium-2,	x86_cpu_intel,	PROC_p2
pentiumii,	x86_cpu_intel,	PROC_p2
pentium-ii,	x86_cpu_intel,	PROC_p2
p3,		x86_cpu_intel,	PROC_p3
pentium3,	x86_cpu_intel,	PROC_p3
pentium-3,	x86_cpu_intel,	PROC_p3
pentiumiii,	x86_cpu_intel,	PROC_p3
pentium-iii,	x86_cpu_intel,	PROC_p3
katmai,		x86_cpu_intel,	PROC_p3
p4,		x86_cpu_intel,	PROC_p4
pentium4,	x86_cpu_intel,	PROC_p4
pentium-4,	x86_cpu_intel,	PROC_p4
pentiumiv,	x86_cpu_intel,	PROC_p4
pentium-iv,	x86_cpu_intel,	PROC_p4
williamette,	x86_cpu_intel,	PROC_p4
ia64,		x86_cpu_ia64,	0
ia-64,		x86_cpu_ia64,	0
itanium,	x86_cpu_ia64,	0
k6,		x86_cpu_amd,	PROC_k6
k7,		x86_cpu_amd,	PROC_k7
athlon,		x86_cpu_amd,	PROC_k7
k8,		x86_cpu_amd,	PROC_hammer
hammer,		x86_cpu_amd,	PROC_hammer
clawhammer,	x86_cpu_amd,	PROC_hammer
opteron,	x86_cpu_amd,	PROC_hammer
athlon64,	x86_cpu_amd,	PROC_hammer
athlon-64,	x86_cpu_amd,	PROC_hammer
venice,		x86_cpu_amd,	PROC_venice
k10,		x86_cpu_amd,	PROC_k10
phenom,		x86_cpu_amd,	PROC_k10
family10h,	x86_cpu_amd,	PROC_k10
bulldozer,	x86_cpu_amd,	PROC_bulldozer
prescott,	x86_cpu_intel,	PROC_prescott
conroe,		x86_cpu_intel,	PROC_conroe
core2,		x86_cpu_intel,	PROC_conroe
penryn,		x86_cpu_intel,	PROC_penryn
nehalem,	x86_cpu_intel,	PROC_nehalem
corei7,		x86_cpu_intel,	PROC_nehalem
westmere,	x86_cpu_intel,	PROC_westmere
sandybridge,	x86_cpu_intel,	PROC_sandybridge
#
# Features have "no" versions to disable them, and only set/reset the
# specific feature being changed.  All other bits are left alone.
#
fpu,		x86_cpu_set,	CPU_FPU
nofpu,		x86_cpu_clear,	CPU_FPU
mmx,		x86_cpu_set,	CPU_MMX
nommx,		x86_cpu_clear,	CPU_MMX
sse,		x86_cpu_set,	CPU_SSE
nosse,		x86_cpu_clear,	CPU_SSE
sse2,		x86_cpu_set,	CPU_SSE2
nosse2,		x86_cpu_clear,	CPU_SSE2
sse3,		x86_cpu_set,	CPU_SSE3
nosse3,		x86_cpu_clear,	CPU_SSE3
#pni,		x86_cpu_set,	CPU_PNI
#nopni,		x86_cpu_clear,	CPU_PNI
3dnow,		x86_cpu_set,	CPU_3DNow
no3dnow,	x86_cpu_clear,	CPU_3DNow
cyrix,		x86_cpu_set,	CPU_Cyrix
nocyrix,	x86_cpu_clear,	CPU_Cyrix
amd,		x86_cpu_set,	CPU_AMD
noamd,		x86_cpu_clear,	CPU_AMD
smm,		x86_cpu_set,	CPU_SMM
nosmm,		x86_cpu_clear,	CPU_SMM
prot,		x86_cpu_set,	CPU_Prot
noprot,		x86_cpu_clear,	CPU_Prot
protected,	x86_cpu_set,	CPU_Prot
noprotected,	x86_cpu_clear,	CPU_Prot
undoc,		x86_cpu_set,	CPU_Undoc
noundoc,	x86_cpu_clear,	CPU_Undoc
undocumented,	x86_cpu_set,	CPU_Undoc
noundocumented,	x86_cpu_clear,	CPU_Undoc
obs,		x86_cpu_set,	CPU_Obs
noobs,		x86_cpu_clear,	CPU_Obs
obsolete,	x86_cpu_set,	CPU_Obs
noobsolete,	x86_cpu_clear,	CPU_Obs
priv,		x86_cpu_set,	CPU_Priv
nopriv,		x86_cpu_clear,	CPU_Priv
privileged,	x86_cpu_set,	CPU_Priv
noprivileged,	x86_cpu_clear,	CPU_Priv
svm,		x86_cpu_set,	CPU_SVM
nosvm,		x86_cpu_clear,	CPU_SVM
padlock,	x86_cpu_set,	CPU_PadLock
nopadlock,	x86_cpu_clear,	CPU_PadLock
em64t,		x86_cpu_set,	CPU_EM64T
noem64t,	x86_cpu_clear,	CPU_EM64T
ssse3,		x86_cpu_set,	CPU_SSSE3
nossse3,	x86_cpu_clear,	CPU_SSSE3
sse4.1,		x86_cpu_set,	CPU_SSE41
nosse4.1,	x86_cpu_clear,	CPU_SSE41
sse41,		x86_cpu_set,	CPU_SSE41
nosse41,	x86_cpu_clear,	CPU_SSE41
sse4.2,		x86_cpu_set,	CPU_SSE42
nosse4.2,	x86_cpu_clear,	CPU_SSE42
sse42,		x86_cpu_set,	CPU_SSE42
nosse42,	x86_cpu_clear,	CPU_SSE42
sse4a,		x86_cpu_set,	CPU_SSE4a
nosse4a,	x86_cpu_clear,	CPU_SSE4a
sse4,		x86_cpu_set_sse4,	0
nosse4,		x86_cpu_clear_sse4,	0
xsave,		x86_cpu_set,	CPU_XSAVE
noxsave,	x86_cpu_clear,	CPU_XSAVE
avx,		x86_cpu_set,	CPU_AVX
noavx,		x86_cpu_clear,	CPU_AVX
fma,		x86_cpu_set,	CPU_FMA
nofma,		x86_cpu_clear,	CPU_FMA
aes,		x86_cpu_set,	CPU_AES
noaes,		x86_cpu_clear,	CPU_AES
clmul,		x86_cpu_set,	CPU_CLMUL
noclmul,	x86_cpu_clear,	CPU_CLMUL
pclmulqdq,	x86_cpu_set,	CPU_CLMUL
nopclmulqdq,	x86_cpu_clear,	CPU_CLMUL
movbe,		x86_cpu_set,	CPU_MOVBE
nomovbe,	x86_cpu_clear,	CPU_MOVBE
xop,		x86_cpu_set,	CPU_XOP
noxop,		x86_cpu_clear,	CPU_XOP
fma4,		x86_cpu_set,	CPU_FMA4
nofma4,		x86_cpu_clear,	CPU_FMA4
f16c,		x86_cpu_set,	CPU_F16C
nof16c,		x86_cpu_clear,	CPU_F16C
fsgsbase,	x86_cpu_set,	CPU_FSGSBASE
nofsgsbase,	x86_cpu_clear,	CPU_FSGSBASE
rdrand,		x86_cpu_set,	CPU_RDRAND
nordrand,	x86_cpu_clear,	CPU_RDRAND
xsaveopt,	x86_cpu_set,	CPU_XSAVEOPT
noxsaveopt,	x86_cpu_clear,	CPU_XSAVEOPT
eptvpid,	x86_cpu_set,	CPU_EPTVPID
noeptvpid,	x86_cpu_clear,	CPU_EPTVPID
smx,		x86_cpu_set,	CPU_SMX
nosmx,		x86_cpu_clear,	CPU_SMX
avx2,		x86_cpu_set,	CPU_AVX2
noavx2,		x86_cpu_clear,	CPU_AVX2
bmi1,		x86_cpu_set,	CPU_BMI1
nobmi1,		x86_cpu_clear,	CPU_BMI1
bmi2,		x86_cpu_set,	CPU_BMI2
nobmi2,		x86_cpu_clear,	CPU_BMI2
invpcid,	x86_cpu_set,	CPU_INVPCID
noinvpcid,	x86_cpu_clear,	CPU_INVPCID
lzcnt,		x86_cpu_set,	CPU_LZCNT
nolzcnt,	x86_cpu_clear,	CPU_LZCNT
# Change NOP patterns
basicnop,	x86_nop,	X86_NOP_BASIC
intelnop,	x86_nop,	X86_NOP_INTEL
amdnop,		x86_nop,	X86_NOP_AMD
%%

void
yasm_x86__parse_cpu(yasm_arch_x86 *arch_x86, const char *cpuid,
                    size_t cpuid_len)
{
    /*@null@*/ const struct cpu_parse_data *pdata;
    wordptr new_cpu;
    size_t i;
    static char lcaseid[16];

    if (cpuid_len > 15)
        return;
    for (i=0; i<cpuid_len; i++)
        lcaseid[i] = tolower(cpuid[i]);
    lcaseid[cpuid_len] = '\0';

    pdata = cpu_find(lcaseid, cpuid_len);
    if (!pdata) {
        yasm_warn_set(YASM_WARN_GENERAL,
                      N_("unrecognized CPU identifier `%s'"), cpuid);
        return;
    }

    new_cpu = BitVector_Clone(arch_x86->cpu_enables[arch_x86->active_cpu]);
    pdata->handler(new_cpu, arch_x86, pdata->data);

    /* try to find an existing match in the CPU table first */
    for (i=0; i<arch_x86->cpu_enables_size; i++) {
        if (BitVector_equal(arch_x86->cpu_enables[i], new_cpu)) {
            arch_x86->active_cpu = i;
            BitVector_Destroy(new_cpu);
            return;
        }
    }

    /* not found, need to add a new entry */
    arch_x86->active_cpu = arch_x86->cpu_enables_size++;
    arch_x86->cpu_enables =
        yasm_xrealloc(arch_x86->cpu_enables,
                      arch_x86->cpu_enables_size*sizeof(wordptr));
    arch_x86->cpu_enables[arch_x86->active_cpu] = new_cpu;
}
