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