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