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