(root)/
gcc-13.2.0/
gcc/
testsuite/
jit.dg/
test-asm.cc
/* { dg-do compile { target x86_64-*-* } } */

#include <stdint.h>
#include "libgccjit++.h"

#include "harness.h"

/**********************************************************************
 Support fns for creating code.
 **********************************************************************/

/* Make a "void FUNC_NAME (void)" function with a single block, returning
   that block.  */

static gccjit::block
make_single_block_func (gccjit::context ctxt, const char *func_name)
{
  gccjit::type void_type = ctxt.get_type (GCC_JIT_TYPE_VOID);
  std::vector<gccjit::param> params;
  gccjit::function func
    = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
			 void_type,
			 func_name, params, 0);
  return func.new_block ("initial");
}

/**********************************************************************
 Support fns for verifying code.
 **********************************************************************/

typedef void (*void_void_fn) (void);

static void_void_fn
get_test_fn (gcc_jit_result *result, const char *func_name)
{
  return (void_void_fn)gcc_jit_result_get_code (result, func_name);
}

/**********************************************************************
 test_i386_basic_asm_1: simple example of asm
 **********************************************************************/

/* Create the equivalent of:

     int src;
     int dst;

     void test_i386_basic_asm_1 (void)
     {
       // Quote from here in docs/cp/topics/asm.rst: example 1: C
       asm ("mov %1, %0\n\t"
            "add $1, %0"
            : "=r" (dst)
            : "r" (src));
       // Quote up to here in docs/cp/topics/asm.rst: example 1: C
     }

     i.e. copy src to dst and add 1 to dst.  */

static void
create_test_i386_basic_asm_1 (gcc_jit_context *c_ctxt)
{
  gccjit::context ctxt (c_ctxt);
  gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT);
  gccjit::lvalue dst
    = ctxt.new_global (GCC_JIT_GLOBAL_EXPORTED, int_type, "dst");
  gccjit::lvalue src
    = ctxt.new_global (GCC_JIT_GLOBAL_EXPORTED, int_type, "src");

  gccjit::block block
    = make_single_block_func (ctxt, "test_i386_basic_asm_1");

  gccjit::extended_asm ext_asm =
  /* Quote from here in docs/cp/topics/asm.rst: example 1: jit.  */
    block.add_extended_asm ("mov %1, %0\n\t"
                            "add $1, %0")
    .add_output_operand ("=r", dst)
    .add_input_operand ("r", src);
  /* Quote up to here in docs/cp/topics/asm.rst: example 1: jit.  */

  std::string desc = ext_asm.get_debug_string ();
  CHECK_STRING_VALUE
    (desc.c_str (),
     "asm (\"mov %1, %0\\n\\tadd $1, %0\" : \"=r\" (dst) : \"r\" (src) : )");

  block.end_with_return ();
}

static void
verify_code_1 (gcc_jit_context *ctxt, gcc_jit_result *result)
{
  void_void_fn test_i386_basic_asm_1
    = get_test_fn (result, "test_i386_basic_asm_1");
  CHECK_NON_NULL (test_i386_basic_asm_1);

  int *dst_ptr = (int *)gcc_jit_result_get_global (result, "dst");
  CHECK_NON_NULL (dst_ptr);
  int *src_ptr = (int *)gcc_jit_result_get_global (result, "src");
  CHECK_NON_NULL (src_ptr);

  *src_ptr = 42;
  *dst_ptr = 0;
  test_i386_basic_asm_1 ();
  CHECK_VALUE (*src_ptr, 42);
  CHECK_VALUE (*dst_ptr, 43);
}

/**********************************************************************
 test_i386_basic_asm_2: test of symbolic names and clobbers
 **********************************************************************/

/* Create the equivalent of:
     uint32_t test_i386_basic_asm_2 (uint32_t Mask)
     {
       uint32_t Index;
       // Quote from here in docs/cp/topics/asm.rst: example 2: C
       asm ("bsfl %[aMask], %[aIndex]"
            : [aIndex] "=r" (Index)
            : [aMask] "r" (Mask)
            : "cc");
       // Quote up to here in docs/cp/topics/asm.rst: example 2: C
       return Index;
     }
   i.e. return the first bit set in "Mask"

   This exercises symbolic names and clobbers.  */

static void
create_test_i386_basic_asm_2 (gcc_jit_context *c_ctxt)
{
  gccjit::context ctxt (c_ctxt);
  gccjit::type uint32_type = ctxt.get_int_type (4, 0);
  gccjit::param mask = ctxt.new_param (uint32_type, "Mask");
  std::vector<gccjit::param> params {mask};
  gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
					     uint32_type,
					     "test_i386_basic_asm_2",
					     params, 0);
  gccjit::lvalue index = func.new_local (uint32_type, "Index");
  gccjit::block block = func.new_block ("initial");
  gccjit::extended_asm ext_asm =
  /* Quote from here in docs/cp/topics/asm.rst: example 2: jit.  */
    block.add_extended_asm ("bsfl %[aMask], %[aIndex]")
    .add_output_operand ("aIndex", "=r", index)
    .add_input_operand ("aMask", "r", mask)
    .add_clobber ("cc");
  /* Quote up to here in docs/cp/topics/asm.rst: example 2: jit.  */

  std::string desc = ext_asm.get_debug_string ();
  CHECK_STRING_VALUE
    (desc.c_str (),
     "asm (\"bsfl %[aMask], %[aIndex]\""
     " : [aIndex] \"=r\" (Index) : [aMask] \"r\" (Mask) : \"cc\")");

  block.end_with_return (index);
}

static void
verify_code_2 (gcc_jit_context *ctxt, gcc_jit_result *result)
{
  typedef uint32_t (*fntype) (uint32_t);
  fntype test_i386_basic_asm_2
    = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_2");
  CHECK_NON_NULL (test_i386_basic_asm_2);

  CHECK_VALUE (test_i386_basic_asm_2 (1), 0);
  CHECK_VALUE (test_i386_basic_asm_2 (2), 1);
  CHECK_VALUE (test_i386_basic_asm_2 (4), 2);
  CHECK_VALUE (test_i386_basic_asm_2 (8), 3);
}

/**********************************************************************
 test_i386_basic_asm_3a/b: test of control flow: "asm goto"
 **********************************************************************/

/* Create the equivalent of:

     int test_i386_basic_asm_3a (int p1, int p2)
     {
       asm goto ("btl %1, %0\n\t"
		 "jc %l2"
		 : // No outputs
		 : "r" (p1), "r" (p2)
		 : "cc"
		 : carry);

       return 0;

      carry:
       return 1;
     }

    or (the "_3b" variant) using a name rather than a number for the goto
    label:

       // Quote from here in docs/cp/topics/asm.rst: example 3b: C
       asm goto ("btl %1, %0\n\t"
                 "jc %l[carry]"
                 : // No outputs
                 : "r" (p1), "r" (p2)
                 : "cc"
                 : carry);
       // Quote up to here in docs/cp/topics/asm.rst: example 3b: C

    This exercises control flow with an asm.  */

static void
create_test_i386_basic_asm_3 (gcc_jit_context *c_ctxt,
			      const char *funcname,
			      int use_name)
{
  gccjit::context ctxt (c_ctxt);
  gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT);
  gccjit::param p1 = ctxt.new_param (int_type, "p1");
  gccjit::param p2 = ctxt.new_param (int_type, "p2");
  std::vector<gccjit::param> params ({p1, p2});
  gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
					     int_type,
					     funcname,
					     params, 0);
  gccjit::block b_start = func.new_block ("start");
  gccjit::block b_fallthru = func.new_block ("fallthru");
  gccjit::block b_carry = func.new_block ("carry");

  gccjit::rvalue zero = ctxt.new_rvalue (int_type, 0);
  gccjit::rvalue one = ctxt.new_rvalue (int_type, 1);

  /* Quote from here in docs/cp/topics/asm.rst: example 3: jit.  */
  const char *asm_template =
    (use_name
     ? /* Label referred to by name: "%l[carry]".  */
       ("btl %1, %0\n\t"
        "jc %l[carry]")
     : /* Label referred to numerically: "%l2".  */
       ("btl %1, %0\n\t"
        "jc %l2"));

  std::vector<gccjit::block> goto_blocks ({b_carry});
  gccjit::extended_asm ext_asm
    = (b_start.end_with_extended_asm_goto (asm_template,
					  goto_blocks,
					  &b_fallthru)
       .add_input_operand ("r", p1)
       .add_input_operand ("r", p2)
       .add_clobber ("cc"));
  /* Quote up to here in docs/cp/topics/asm.rst: example 3: jit.  */

  std::string desc = ext_asm.get_debug_string ();
  CHECK_STRING_VALUE
    (desc.c_str (),
     (use_name
      ? ("asm goto (\"btl %1, %0\\n\\tjc %l[carry]\" "
	 ":  : \"r\" (p1), \"r\" (p2) : \"cc\" "
	 ": carry [fallthrough: fallthru])")
      : ("asm goto (\"btl %1, %0\\n\\tjc %l2\" "
	 ":  : \"r\" (p1), \"r\" (p2) : \"cc\" "
	 ": carry [fallthrough: fallthru])")));

  b_fallthru.end_with_return (zero);
  b_carry.end_with_return (one);
}

static void
verify_code_3 (gcc_jit_context *ctxt, gcc_jit_result *result,
	       const char *funcname)
{
  typedef int (*test_i386_basic_asm_3_type) (int, int);

  test_i386_basic_asm_3_type test_i386_basic_asm_3
    = (test_i386_basic_asm_3_type) gcc_jit_result_get_code (result, funcname);
  CHECK_NON_NULL (test_i386_basic_asm_3);

  /* The fn should test bits, returning 0 or 1.  */
  /* Bit 0.  */
  CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 0), 0);
  CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 0), 1);
  CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 0), 0);
  CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 0), 1);
  CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 0), 0);
  /* Bit 1.  */
  CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 1), 0);
  CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 1), 0);
  CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 1), 1);
  CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 1), 1);
  CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 1), 0);

  for (int i = 0; i < 15; i++)
    {
      CHECK_VALUE (test_i386_basic_asm_3 (0x0000, i), 0);
      CHECK_VALUE (test_i386_basic_asm_3 (0xffff, i), 1);
    }
}

/**********************************************************************
 test_i386_basic_asm_4: test of "volatile"
 **********************************************************************/

/* Create the equivalent of:
     uint64_t test_i386_basic_asm_4 (void)
     {
       uint64_t start_time, end_time;

       // Get start time
       asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
                     "shl $32, %%rdx\n\t"  // Shift the upper bits left.
                     "or %%rdx, %0"        // 'Or' in the lower bits.
                     : "=a" (start_time)
                     :
                     : "rdx");

       // could do other work here

       // Get end time
       asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
                     "shl $32, %%rdx\n\t"  // Shift the upper bits left.
                     "or %%rdx, %0"        // 'Or' in the lower bits.
                     : "=a" (start_time)
                     :
                     : "rdx");

       // Get elapsed time
       return end_time - start_time;
     }

   This exercises "volatile"; without it, the optimizer can assume that
   both asm generate the same value and thus the time difference is zero.  */

static void
add_rdtsc (gccjit::block block, gccjit::lvalue msr)
{
  /* Quote from here in docs/cp/topics/asm.rst: example 4: jit.  */
  gccjit::extended_asm ext_asm
    = block.add_extended_asm
	("rdtsc\n\t"  /* Returns the time in EDX:EAX.  */
	 "shl $32, %%rdx\n\t"  /* Shift the upper bits left.  */
	 "or %%rdx, %0")  /* 'Or' in the lower bits.  */
    .set_volatile_flag (true)
    .add_output_operand ("=a", msr)
    .add_clobber ("rdx");
  /* Quote up to here in docs/cp/topics/asm.rst: example 4: jit.  */

  std::string desc = ext_asm.get_debug_string ();
  CHECK_STRING_STARTS_WITH (desc.c_str (), "asm volatile (");
}

static void
create_test_i386_basic_asm_4 (gcc_jit_context *c_ctxt)
{
  gccjit::context ctxt (c_ctxt);
  gccjit::type uint64_type = ctxt.get_int_type (8, 0);
  std::vector<gccjit::param> params;
  gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
					      uint64_type,
					      "test_i386_basic_asm_4",
					     params, 0);
  gccjit::block block = func.new_block ();

  gccjit::lvalue start_time = func.new_local (uint64_type, "start_time");
  add_rdtsc (block, start_time);

  block.add_comment ("other work here");

  gccjit::lvalue end_time = func.new_local (uint64_type, "end_time");
  add_rdtsc (block, end_time);

  block.end_with_return (end_time - start_time);
}

static void
verify_code_4 (gcc_jit_context *ctxt, gcc_jit_result *result)
{
  typedef uint64_t (*fntype) (void);
  fntype test_i386_basic_asm_4
    = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_4");

  CHECK_NON_NULL (test_i386_basic_asm_4);

  test_i386_basic_asm_4 ();
}

/**********************************************************************
 test_i386_basic_asm_5: test of top-level asm
 **********************************************************************/

/* Create the equivalent of:

   // Quote from here in docs/cp/topics/asm.rst: example 5: C
     asm ("\t.pushsection .text\n"
          "\t.globl add_asm\n"
          "\t.type add_asm, @function\n"
          "add_asm:\n"
          "\tmovq %rdi, %rax\n"
          "\tadd %rsi, %rax\n"
          "\tret\n"
          "\t.popsection\n");
   // Quote up to here in docs/cp/topics/asm.rst: example 5: C

   to add a simple function ("add_asm") directly in assembly language.  */

static void
create_test_i386_basic_asm_5 (gcc_jit_context *c_ctxt)
{
  gccjit::context ctxt (c_ctxt);
#if __APPLE__
  /* Darwin's assemblers do not support push/pop section, do not use .type
     and external symbols should use __USER_LABEL_PREFIX__.  */
  ctxt.add_top_level_asm ("\t.text\n"
                          "\t.globl _add_asm\n"
                          "_add_asm:\n"
                          "\tmovq %rdi, %rax\n"
                          "\tadd %rsi, %rax\n"
                          "\tret\n"
                          "\t# some asm here\n");
#else
  /* Quote from here in docs/cp/topics/asm.rst: example 5: jit.  */
  ctxt.add_top_level_asm ("\t.pushsection .text\n"
                          "\t.globl add_asm\n"
                          "\t.type add_asm, @function\n"
                          "add_asm:\n"
                          "\tmovq %rdi, %rax\n"
                          "\tadd %rsi, %rax\n"
                          "\tret\n"
                          "\t# some asm here\n"
                          "\t.popsection\n");
  /* Quote up to here in docs/cp/topics/asm.rst: example 5: jit.  */
#endif
}

static void
verify_code_5 (gcc_jit_context *ctxt, gcc_jit_result *result)
{
  typedef int (*test_i386_basic_asm_5_type) (int, int);
  test_i386_basic_asm_5_type test_i386_basic_asm_5
    = (test_i386_basic_asm_5_type) gcc_jit_result_get_code (result, "add_asm");
  CHECK_NON_NULL (test_i386_basic_asm_5);

  CHECK_VALUE (test_i386_basic_asm_5 (2, 2), 4);
  CHECK_VALUE (test_i386_basic_asm_5 (20, 7), 27);
}

/**********************************************************************
 Code for harness
 **********************************************************************/

void
create_code (gcc_jit_context *ctxt, void *user_data)
{
  create_test_i386_basic_asm_1 (ctxt);
  create_test_i386_basic_asm_2 (ctxt);
  create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3a", 0);
  create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3b", 1);
  create_test_i386_basic_asm_4 (ctxt);
  create_test_i386_basic_asm_5 (ctxt);
}

void
verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
{
  CHECK_NON_NULL (result);
  verify_code_1 (ctxt, result);
  verify_code_2 (ctxt, result);
  verify_code_3 (ctxt, result, "test_i386_basic_asm_3a");
  verify_code_3 (ctxt, result, "test_i386_basic_asm_3b");
  verify_code_4 (ctxt, result);
  verify_code_5 (ctxt, result);
}