/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-â
âvi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8                                :viâ
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ¡
â Copyright 2020 Justine Alexandra Roberts Tunney                              â
â                                                                              â
â Permission to use, copy, modify, and/or distribute this software for         â
â any purpose with or without fee is hereby granted, provided that the         â
â above copyright notice and this permission notice appear in all copies.      â
â                                                                              â
â THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                â
â WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                â
â WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             â
â AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         â
â DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        â
â PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               â
â TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             â
â PERFORMANCE OF THIS SOFTWARE.                                                â
ââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ*/
#include "libc/mem/gc.internal.h"
#include "libc/testlib/ezbench.h"
#include "libc/testlib/testlib.h"
#include "test/libc/xed/lib.h"
#include "third_party/xed/x86.h"

/**
 * @fileoverview Instruction Length Decoding Tests.
 *
 * It is demonstrated that our 3.5kb x86 parser supports all legal x86
 * instruction set architectures and addressing modes since the 1970's,
 * including the really sophisticated ones, e.g. avx512, as well as the
 * less popular ones, e.g. 3dnow.
 */

TEST(x86ild, testSomeThingsNeverChange) {
  ASSERT_EQ(3, ildreal(u"Ã¢ââ¨"));   /* add $23,%ax  */
  ASSERT_EQ(3, ildlegacy(u"Ã¢ââ¨")); /* add $23,%eax */
  ASSERT_EQ(3, ild(u"Ã¢ââ¨"));       /* add $23,%eax */
}

TEST(x86ild, testSomeThingsDoChange) {
  ASSERT_EQ(3, ildreal(u"â£7â¼ÃÃ"));   /* add $0x1337,%ax */
  ASSERT_EQ(5, ildlegacy(u"â£7â¼ÃÃ")); /* add $0x90901337,%eax */
  ASSERT_EQ(5, ild(u"â£7â¼ÃÃ"));       /* add $0x90901337,%eax */
  ASSERT_EQ(1, ildreal(u"@Ã"));      /* inc %ax */
  ASSERT_EQ(1, ildlegacy(u"@Ã"));    /* inc %eax */
  ASSERT_EQ(2, ild(u"@Ã"));          /* rex xchg %eax,%eax */
}

TEST(x86ild, testHugeInstructions) {
  ASSERT_EQ(10, ild(u"HâÂ Â Â Â Â Â Â Ã"));   /* movabs $0x8000000000000000,%rdx */
  ASSERT_EQ(11, ild(u"Hââ£8lÂ Â Â Ã Â "));  /* movq $0x209000,0x6c38(%rip) */
  ASSERT_EQ(12, ild(u"HâÃ¤$Ã¡Â Â Â Â Â Â Â ")); /* movq $0x0,0xa0(%rsp) */
}

TEST(x86ild, testLaughOutLoudLargeCanonicalInstructions) {
  ASSERT_EQ(15, /* lock addl $0x12331337,%fs:-0x1337(%ebx,%esi,1) */
            ildreal(u"â¡dfgÃ¼Ã¤3ââÎ»Î»7â¼3â"));
}

TEST(x86ild, testEncodingDisagreements) {
  ASSERT_EQ(13, /* lock addw $0x1337,%fs:-0x1337(%ebx,%esi,1) */
            ild(u"fgâ¡dÃ¼Ã¤3ââÎ»Î»7â¼")); /* â xed encoding */
  ASSERT_EQ(13, /* lock addw $0x1337,%fs:-0x1337(%ebx,%esi,1) */
            ild(u"dgfâ¡Ã¼Ã¤3ââÎ»Î»7â¼")); /* â gas encoding */
}

TEST(x86ild, testOverlongInstructions) {
  ASSERT_EQ(3, ild(u"â¤â¤Ã")); /* rep pause */
  ASSERT_EQ(-XED_ERROR_BUFFER_TOO_SHORT /* suboptimal error code */,
            ildreal(u"â¡â¡â¡â¡dfgÃ¼Ã¤3ââÎ»Î»7â¼3â"));
}

TEST(x86ild, testAvx512_inRealMode_throwsError) {
  ASSERT_EQ(-XED_ERROR_INVALID_MODE, ildreal(u"bÃTXYÃ¶âÂ¶â¦Â Â "));
}

TEST(x86ild, testAvx512) {
  ASSERT_EQ(6, /* vaddps %zmm17,%zmm16,%zmm16 */
            ild(u"bÂ¡|@Xâ´"));
  ASSERT_EQ(11, /* vmulps 0x414(%rsp,%rbx,1){1to16},%zmm5,%zmm18 */
            ild(u"bÃTXYÃ¶âÂ¶â¦Â Â "));
}

TEST(x86ild, testCascadeLake_advancedNeuralNetworkInstructions) {
  ASSERT_EQ(6, ild(u"bâ¥mâPâ¦")); /* vpdpbusd %xmm3,%xmm2,%xmm1 */
  ASSERT_EQ(11 /* vpdpbusd 0x10000000(%rcx,%r14,8),%xmm2,%xmm1 */,
            ild(u"bâmâPÃ®Â±Â Â Â âº"));
}

TEST(x86ild, testAmd3dnow) {
  ASSERT_EQ(4, ild(u"â¼â¼âÂª")); /* pfrcpit1 %mm0,%mm1 */
  struct XedDecodedInst xedd;
  ASSERT_EQ(
      0, xed_instruction_length_decode(
             xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LEGACY_32),
             gc(unbingx86op(u"â¼â¼âÂª")), 4));
  ASSERT_EQ(true, (int)xedd.op.amd3dnow);
  ASSERT_EQ(0xa6, xedd.op.opcode);
}

TEST(x86ild, testPopToMemory) {
  ASSERT_EQ(3, ild(u"ÃFâ")); /* 8f 46 08 */
}

TEST(x86ild, testFinit) {
  ASSERT_EQ(1, ild(u"Â¢âÏ")); /* 9B DB E3: fwait */
  ASSERT_EQ(2, ild(u"âÏ"));  /* DB E3:    fninit */
}

BENCH(x86ild, bench) {
  uint8_t *x = gc(unbingx86op(u"bâmâPÃ®Â±Â Â Â âº"));
  struct XedDecodedInst xedd;
  EZBENCH2("x86ild", donothing,
           xed_instruction_length_decode(
               xed_decoded_inst_zero_set_mode(&xedd, XED_MACHINE_MODE_LONG_64),
               x, XED_MAX_INSTRUCTION_BYTES));
}
