1 /* { dg-do compile { target x86_64-*-* } } */
2 
3 #include "libgccjit++.h"
4 
5 #include "harness.h"
6 
7 /**********************************************************************
8  Support fns for creating code.
9  **********************************************************************/
10 
11 /* Make a "void FUNC_NAME (void)" function with a single block, returning
12    that block.  */
13 
14 static gccjit::block
make_single_block_func(gccjit::context ctxt,const char * func_name)15 make_single_block_func (gccjit::context ctxt, const char *func_name)
16 {
17   gccjit::type void_type = ctxt.get_type (GCC_JIT_TYPE_VOID);
18   std::vector<gccjit::param> params;
19   gccjit::function func
20     = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
21 			 void_type,
22 			 func_name, params, 0);
23   return func.new_block ("initial");
24 }
25 
26 /**********************************************************************
27  Support fns for verifying code.
28  **********************************************************************/
29 
30 typedef void (*void_void_fn) (void);
31 
32 static void_void_fn
get_test_fn(gcc_jit_result * result,const char * func_name)33 get_test_fn (gcc_jit_result *result, const char *func_name)
34 {
35   return (void_void_fn)gcc_jit_result_get_code (result, func_name);
36 }
37 
38 /**********************************************************************
39  test_i386_basic_asm_1: simple example of asm
40  **********************************************************************/
41 
42 /* Create the equivalent of:
43 
44      int src;
45      int dst;
46 
47      void test_i386_basic_asm_1 (void)
48      {
49        // Quote from here in docs/cp/topics/asm.rst: example 1: C
50        asm ("mov %1, %0\n\t"
51             "add $1, %0"
52             : "=r" (dst)
53             : "r" (src));
54        // Quote up to here in docs/cp/topics/asm.rst: example 1: C
55      }
56 
57      i.e. copy src to dst and add 1 to dst.  */
58 
59 static void
create_test_i386_basic_asm_1(gcc_jit_context * c_ctxt)60 create_test_i386_basic_asm_1 (gcc_jit_context *c_ctxt)
61 {
62   gccjit::context ctxt (c_ctxt);
63   gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT);
64   gccjit::lvalue dst
65     = ctxt.new_global (GCC_JIT_GLOBAL_EXPORTED, int_type, "dst");
66   gccjit::lvalue src
67     = ctxt.new_global (GCC_JIT_GLOBAL_EXPORTED, int_type, "src");
68 
69   gccjit::block block
70     = make_single_block_func (ctxt, "test_i386_basic_asm_1");
71 
72   gccjit::extended_asm ext_asm =
73   /* Quote from here in docs/cp/topics/asm.rst: example 1: jit.  */
74     block.add_extended_asm ("mov %1, %0\n\t"
75                             "add $1, %0")
76     .add_output_operand ("=r", dst)
77     .add_input_operand ("r", src);
78   /* Quote up to here in docs/cp/topics/asm.rst: example 1: jit.  */
79 
80   std::string desc = ext_asm.get_debug_string ();
81   CHECK_STRING_VALUE
82     (desc.c_str (),
83      "asm (\"mov %1, %0\\n\\tadd $1, %0\" : \"=r\" (dst) : \"r\" (src) : )");
84 
85   block.end_with_return ();
86 }
87 
88 static void
verify_code_1(gcc_jit_context * ctxt,gcc_jit_result * result)89 verify_code_1 (gcc_jit_context *ctxt, gcc_jit_result *result)
90 {
91   void_void_fn test_i386_basic_asm_1
92     = get_test_fn (result, "test_i386_basic_asm_1");
93   CHECK_NON_NULL (test_i386_basic_asm_1);
94 
95   int *dst_ptr = (int *)gcc_jit_result_get_global (result, "dst");
96   CHECK_NON_NULL (dst_ptr);
97   int *src_ptr = (int *)gcc_jit_result_get_global (result, "src");
98   CHECK_NON_NULL (src_ptr);
99 
100   *src_ptr = 42;
101   *dst_ptr = 0;
102   test_i386_basic_asm_1 ();
103   CHECK_VALUE (*src_ptr, 42);
104   CHECK_VALUE (*dst_ptr, 43);
105 }
106 
107 /**********************************************************************
108  test_i386_basic_asm_2: test of symbolic names and clobbers
109  **********************************************************************/
110 
111 /* Create the equivalent of:
112      uint32_t test_i386_basic_asm_2 (uint32_t Mask)
113      {
114        uint32_t Index;
115        // Quote from here in docs/cp/topics/asm.rst: example 2: C
116        asm ("bsfl %[aMask], %[aIndex]"
117             : [aIndex] "=r" (Index)
118             : [aMask] "r" (Mask)
119             : "cc");
120        // Quote up to here in docs/cp/topics/asm.rst: example 2: C
121        return Index;
122      }
123    i.e. return the first bit set in "Mask"
124 
125    This exercises symbolic names and clobbers.  */
126 
127 static void
create_test_i386_basic_asm_2(gcc_jit_context * c_ctxt)128 create_test_i386_basic_asm_2 (gcc_jit_context *c_ctxt)
129 {
130   gccjit::context ctxt (c_ctxt);
131   gccjit::type uint32_type = ctxt.get_int_type (4, 0);
132   gccjit::param mask = ctxt.new_param (uint32_type, "Mask");
133   std::vector<gccjit::param> params {mask};
134   gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
135 					     uint32_type,
136 					     "test_i386_basic_asm_2",
137 					     params, 0);
138   gccjit::lvalue index = func.new_local (uint32_type, "Index");
139   gccjit::block block = func.new_block ("initial");
140   gccjit::extended_asm ext_asm =
141   /* Quote from here in docs/cp/topics/asm.rst: example 2: jit.  */
142     block.add_extended_asm ("bsfl %[aMask], %[aIndex]")
143     .add_output_operand ("aIndex", "=r", index)
144     .add_input_operand ("aMask", "r", mask)
145     .add_clobber ("cc");
146   /* Quote up to here in docs/cp/topics/asm.rst: example 2: jit.  */
147 
148   std::string desc = ext_asm.get_debug_string ();
149   CHECK_STRING_VALUE
150     (desc.c_str (),
151      "asm (\"bsfl %[aMask], %[aIndex]\""
152      " : [aIndex] \"=r\" (Index) : [aMask] \"r\" (Mask) : \"cc\")");
153 
154   block.end_with_return (index);
155 }
156 
157 static void
verify_code_2(gcc_jit_context * ctxt,gcc_jit_result * result)158 verify_code_2 (gcc_jit_context *ctxt, gcc_jit_result *result)
159 {
160   typedef uint32_t (*fntype) (uint32_t);
161   fntype test_i386_basic_asm_2
162     = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_2");
163   CHECK_NON_NULL (test_i386_basic_asm_2);
164 
165   CHECK_VALUE (test_i386_basic_asm_2 (1), 0);
166   CHECK_VALUE (test_i386_basic_asm_2 (2), 1);
167   CHECK_VALUE (test_i386_basic_asm_2 (4), 2);
168   CHECK_VALUE (test_i386_basic_asm_2 (8), 3);
169 }
170 
171 /**********************************************************************
172  test_i386_basic_asm_3a/b: test of control flow: "asm goto"
173  **********************************************************************/
174 
175 /* Create the equivalent of:
176 
177      int test_i386_basic_asm_3a (int p1, int p2)
178      {
179        asm goto ("btl %1, %0\n\t"
180 		 "jc %l2"
181 		 : // No outputs
182 		 : "r" (p1), "r" (p2)
183 		 : "cc"
184 		 : carry);
185 
186        return 0;
187 
188       carry:
189        return 1;
190      }
191 
192     or (the "_3b" variant) using a name rather than a number for the goto
193     label:
194 
195        // Quote from here in docs/cp/topics/asm.rst: example 3b: C
196        asm goto ("btl %1, %0\n\t"
197                  "jc %l[carry]"
198                  : // No outputs
199                  : "r" (p1), "r" (p2)
200                  : "cc"
201                  : carry);
202        // Quote up to here in docs/cp/topics/asm.rst: example 3b: C
203 
204     This exercises control flow with an asm.  */
205 
206 static void
create_test_i386_basic_asm_3(gcc_jit_context * c_ctxt,const char * funcname,int use_name)207 create_test_i386_basic_asm_3 (gcc_jit_context *c_ctxt,
208 			      const char *funcname,
209 			      int use_name)
210 {
211   gccjit::context ctxt (c_ctxt);
212   gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT);
213   gccjit::param p1 = ctxt.new_param (int_type, "p1");
214   gccjit::param p2 = ctxt.new_param (int_type, "p2");
215   std::vector<gccjit::param> params ({p1, p2});
216   gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
217 					     int_type,
218 					     funcname,
219 					     params, 0);
220   gccjit::block b_start = func.new_block ("start");
221   gccjit::block b_fallthru = func.new_block ("fallthru");
222   gccjit::block b_carry = func.new_block ("carry");
223 
224   gccjit::rvalue zero = ctxt.new_rvalue (int_type, 0);
225   gccjit::rvalue one = ctxt.new_rvalue (int_type, 1);
226 
227   /* Quote from here in docs/cp/topics/asm.rst: example 3: jit.  */
228   const char *asm_template =
229     (use_name
230      ? /* Label referred to by name: "%l[carry]".  */
231        ("btl %1, %0\n\t"
232         "jc %l[carry]")
233      : /* Label referred to numerically: "%l2".  */
234        ("btl %1, %0\n\t"
235         "jc %l2"));
236 
237   std::vector<gccjit::block> goto_blocks ({b_carry});
238   gccjit::extended_asm ext_asm
239     = (b_start.end_with_extended_asm_goto (asm_template,
240 					  goto_blocks,
241 					  &b_fallthru)
242        .add_input_operand ("r", p1)
243        .add_input_operand ("r", p2)
244        .add_clobber ("cc"));
245   /* Quote up to here in docs/cp/topics/asm.rst: example 3: jit.  */
246 
247   std::string desc = ext_asm.get_debug_string ();
248   CHECK_STRING_VALUE
249     (desc.c_str (),
250      (use_name
251       ? ("asm goto (\"btl %1, %0\\n\\tjc %l[carry]\" "
252 	 ":  : \"r\" (p1), \"r\" (p2) : \"cc\" "
253 	 ": carry [fallthrough: fallthru])")
254       : ("asm goto (\"btl %1, %0\\n\\tjc %l2\" "
255 	 ":  : \"r\" (p1), \"r\" (p2) : \"cc\" "
256 	 ": carry [fallthrough: fallthru])")));
257 
258   b_fallthru.end_with_return (zero);
259   b_carry.end_with_return (one);
260 }
261 
262 static void
verify_code_3(gcc_jit_context * ctxt,gcc_jit_result * result,const char * funcname)263 verify_code_3 (gcc_jit_context *ctxt, gcc_jit_result *result,
264 	       const char *funcname)
265 {
266   typedef int (*test_i386_basic_asm_3_type) (int, int);
267 
268   test_i386_basic_asm_3_type test_i386_basic_asm_3
269     = (test_i386_basic_asm_3_type) gcc_jit_result_get_code (result, funcname);
270   CHECK_NON_NULL (test_i386_basic_asm_3);
271 
272   /* The fn should test bits, returning 0 or 1.  */
273   /* Bit 0.  */
274   CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 0), 0);
275   CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 0), 1);
276   CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 0), 0);
277   CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 0), 1);
278   CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 0), 0);
279   /* Bit 1.  */
280   CHECK_VALUE (test_i386_basic_asm_3 (0x0000, 1), 0);
281   CHECK_VALUE (test_i386_basic_asm_3 (0x0001, 1), 0);
282   CHECK_VALUE (test_i386_basic_asm_3 (0x0002, 1), 1);
283   CHECK_VALUE (test_i386_basic_asm_3 (0x0003, 1), 1);
284   CHECK_VALUE (test_i386_basic_asm_3 (0x0004, 1), 0);
285 
286   for (int i = 0; i < 15; i++)
287     {
288       CHECK_VALUE (test_i386_basic_asm_3 (0x0000, i), 0);
289       CHECK_VALUE (test_i386_basic_asm_3 (0xffff, i), 1);
290     }
291 }
292 
293 /**********************************************************************
294  test_i386_basic_asm_4: test of "volatile"
295  **********************************************************************/
296 
297 /* Create the equivalent of:
298      uint64_t test_i386_basic_asm_4 (void)
299      {
300        uint64_t start_time, end_time;
301 
302        // Get start time
303        asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
304                      "shl $32, %%rdx\n\t"  // Shift the upper bits left.
305                      "or %%rdx, %0"        // 'Or' in the lower bits.
306                      : "=a" (start_time)
307                      :
308                      : "rdx");
309 
310        // could do other work here
311 
312        // Get end time
313        asm volatile ("rdtsc\n\t"    // Returns the time in EDX:EAX.
314                      "shl $32, %%rdx\n\t"  // Shift the upper bits left.
315                      "or %%rdx, %0"        // 'Or' in the lower bits.
316                      : "=a" (start_time)
317                      :
318                      : "rdx");
319 
320        // Get elapsed time
321        return end_time - start_time;
322      }
323 
324    This exercises "volatile"; without it, the optimizer can assume that
325    both asm generate the same value and thus the time difference is zero.  */
326 
327 static void
add_rdtsc(gccjit::block block,gccjit::lvalue msr)328 add_rdtsc (gccjit::block block, gccjit::lvalue msr)
329 {
330   /* Quote from here in docs/cp/topics/asm.rst: example 4: jit.  */
331   gccjit::extended_asm ext_asm
332     = block.add_extended_asm
333 	("rdtsc\n\t"  /* Returns the time in EDX:EAX.  */
334 	 "shl $32, %%rdx\n\t"  /* Shift the upper bits left.  */
335 	 "or %%rdx, %0")  /* 'Or' in the lower bits.  */
336     .set_volatile_flag (true)
337     .add_output_operand ("=a", msr)
338     .add_clobber ("rdx");
339   /* Quote up to here in docs/cp/topics/asm.rst: example 4: jit.  */
340 
341   std::string desc = ext_asm.get_debug_string ();
342   CHECK_STRING_STARTS_WITH (desc.c_str (), "asm volatile (");
343 }
344 
345 static void
create_test_i386_basic_asm_4(gcc_jit_context * c_ctxt)346 create_test_i386_basic_asm_4 (gcc_jit_context *c_ctxt)
347 {
348   gccjit::context ctxt (c_ctxt);
349   gccjit::type uint64_type = ctxt.get_int_type (8, 0);
350   std::vector<gccjit::param> params;
351   gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
352 					      uint64_type,
353 					      "test_i386_basic_asm_4",
354 					     params, 0);
355   gccjit::block block = func.new_block ();
356 
357   gccjit::lvalue start_time = func.new_local (uint64_type, "start_time");
358   add_rdtsc (block, start_time);
359 
360   block.add_comment ("other work here");
361 
362   gccjit::lvalue end_time = func.new_local (uint64_type, "end_time");
363   add_rdtsc (block, end_time);
364 
365   block.end_with_return (end_time - start_time);
366 }
367 
368 static void
verify_code_4(gcc_jit_context * ctxt,gcc_jit_result * result)369 verify_code_4 (gcc_jit_context *ctxt, gcc_jit_result *result)
370 {
371   typedef uint64_t (*fntype) (void);
372   fntype test_i386_basic_asm_4
373     = (fntype)gcc_jit_result_get_code (result, "test_i386_basic_asm_4");
374 
375   CHECK_NON_NULL (test_i386_basic_asm_4);
376 
377   test_i386_basic_asm_4 ();
378 }
379 
380 /**********************************************************************
381  test_i386_basic_asm_5: test of top-level asm
382  **********************************************************************/
383 
384 /* Create the equivalent of:
385 
386    // Quote from here in docs/cp/topics/asm.rst: example 5: C
387      asm ("\t.pushsection .text\n"
388           "\t.globl add_asm\n"
389           "\t.type add_asm, @function\n"
390           "add_asm:\n"
391           "\tmovq %rdi, %rax\n"
392           "\tadd %rsi, %rax\n"
393           "\tret\n"
394           "\t.popsection\n");
395    // Quote up to here in docs/cp/topics/asm.rst: example 5: C
396 
397    to add a simple function ("add_asm") directly in assembly language.  */
398 
399 static void
create_test_i386_basic_asm_5(gcc_jit_context * c_ctxt)400 create_test_i386_basic_asm_5 (gcc_jit_context *c_ctxt)
401 {
402   gccjit::context ctxt (c_ctxt);
403   /* Quote from here in docs/cp/topics/asm.rst: example 5: jit.  */
404   ctxt.add_top_level_asm ("\t.pushsection .text\n"
405                           "\t.globl add_asm\n"
406                           "\t.type add_asm, @function\n"
407                           "add_asm:\n"
408                           "\tmovq %rdi, %rax\n"
409                           "\tadd %rsi, %rax\n"
410                           "\tret\n"
411                           "\t# some asm here\n"
412                           "\t.popsection\n");
413   /* Quote up to here in docs/cp/topics/asm.rst: example 5: jit.  */
414 }
415 
416 static void
verify_code_5(gcc_jit_context * ctxt,gcc_jit_result * result)417 verify_code_5 (gcc_jit_context *ctxt, gcc_jit_result *result)
418 {
419   typedef int (*test_i386_basic_asm_5_type) (int, int);
420   test_i386_basic_asm_5_type test_i386_basic_asm_5
421     = (test_i386_basic_asm_5_type) gcc_jit_result_get_code (result, "add_asm");
422   CHECK_NON_NULL (test_i386_basic_asm_5);
423 
424   CHECK_VALUE (test_i386_basic_asm_5 (2, 2), 4);
425   CHECK_VALUE (test_i386_basic_asm_5 (20, 7), 27);
426 }
427 
428 /**********************************************************************
429  Code for harness
430  **********************************************************************/
431 
432 void
create_code(gcc_jit_context * ctxt,void * user_data)433 create_code (gcc_jit_context *ctxt, void *user_data)
434 {
435   create_test_i386_basic_asm_1 (ctxt);
436   create_test_i386_basic_asm_2 (ctxt);
437   create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3a", 0);
438   create_test_i386_basic_asm_3 (ctxt, "test_i386_basic_asm_3b", 1);
439   create_test_i386_basic_asm_4 (ctxt);
440   create_test_i386_basic_asm_5 (ctxt);
441 }
442 
443 void
verify_code(gcc_jit_context * ctxt,gcc_jit_result * result)444 verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
445 {
446   CHECK_NON_NULL (result);
447   verify_code_1 (ctxt, result);
448   verify_code_2 (ctxt, result);
449   verify_code_3 (ctxt, result, "test_i386_basic_asm_3a");
450   verify_code_3 (ctxt, result, "test_i386_basic_asm_3b");
451   verify_code_4 (ctxt, result);
452   verify_code_5 (ctxt, result);
453 }
454