1 /* Test of C++ API.  */
2 #include <stdlib.h>
3 #include <stdio.h>
4 
5 #include "libgccjit++.h"
6 
7 #include <sstream>
8 
9 #include "harness.h"
10 
11 struct quadratic
12 {
13   double a;
14   double b;
15   double c;
16   double discriminant;
17 };
18 
19 /* As per test-quadratic.c, let's try to inject the equivalent of:
20 
21      extern double sqrt (double);
22 
23      void
24      calc_discriminant (struct quadratic *q)
25      {
26        // (b^2 - 4ac)
27        q->discriminant = (q->b * q->b) - (4 * q->a * q->c);
28      }
29 
30      int
31      test_quadratic (double a, double b, double c, double *r1, double *r2)
32      {
33        struct quadratic q;
34        q.a = a;
35        q.b = b;
36        q.c = c;
37        calc_discriminant (&q);
38        if (q.discriminant > 0)
39 	 {
40 	    double s = sqrt (q.discriminant);
41 	    *r1 = (-b + s) / (2 * a);
42 	    *r2 = (-b - s) / (2 * a);
43 	    return 2;
44 	 }
45        else if (q.discriminant == 0)
46 	 {
47 	    *r1 = -b / (2 * a);
48 	    return 1;
49 	 }
50 	 else return 0;
51      }
52 
53   However, we'll use the C++ bindings.
54 */
55 
56 /****************************************************************************
57  Test case
58  ****************************************************************************/
59 
60 struct quadratic_test
61 {
62   gccjit::context ctxt;
63 
64   /* "double" and "(double *)".  */
65   gccjit::type numeric_type;
66   gccjit::type numeric_type_ptr;
67 
68   /* The value (double)0.  */
69   gccjit::rvalue zero;
70 
71   gccjit::type int_type;
72   gccjit::type void_type;
73 
74   /* "struct quadratic" */
75   gccjit::type quadratic;
76   gccjit::field a;
77   gccjit::field b;
78   gccjit::field c;
79   gccjit::field discriminant;
80 
81   /* "(struct quadratic *)" */
82   gccjit::type quadratic_ptr;
83 
84   gccjit::function calc_discriminant;
85 
86   gccjit::function sqrt;
87 
88 };
89 
90 static void
make_types(quadratic_test & testcase)91 make_types (quadratic_test &testcase)
92 {
93   testcase.numeric_type = testcase.ctxt.get_type (GCC_JIT_TYPE_DOUBLE);
94   testcase.numeric_type_ptr = testcase.numeric_type.get_pointer ();
95   testcase.zero = testcase.ctxt.zero (testcase.numeric_type);
96 
97   testcase.int_type = testcase.ctxt.get_int_type <int> ();
98   testcase.void_type = testcase.ctxt.get_type (GCC_JIT_TYPE_VOID);
99 
100   testcase.a = testcase.ctxt.new_field (testcase.numeric_type, "a");
101   testcase.b = testcase.ctxt.new_field (testcase.numeric_type, "b");
102   testcase.c = testcase.ctxt.new_field (testcase.numeric_type, "c");
103   testcase.discriminant =
104     testcase.ctxt.new_field (testcase.numeric_type, "discriminant");
105   CHECK_STRING_VALUE (testcase.discriminant.get_debug_string ().c_str (),
106                       "discriminant");
107   std::vector<gccjit::field> fields (4);
108   fields[0] = testcase.a;
109   fields[1] = testcase.b;
110   fields[2] = testcase.c;
111   fields[3] = testcase.discriminant;
112   testcase.quadratic =
113     testcase.ctxt.new_struct_type (
114       "quadratic",
115       fields);
116   testcase.quadratic_ptr = testcase.quadratic.get_pointer ();
117 }
118 
119 static void
make_sqrt(quadratic_test & testcase)120 make_sqrt (quadratic_test &testcase)
121 {
122   std::vector<gccjit::param> params (1);
123   params[0] =
124     testcase.ctxt.new_param (testcase.numeric_type, "x");
125   testcase.sqrt =
126     testcase.ctxt.new_function (GCC_JIT_FUNCTION_IMPORTED,
127 				testcase.numeric_type,
128 				"sqrt",
129 				params,
130 				0);
131 }
132 
133 static void
make_calc_discriminant(quadratic_test & testcase)134 make_calc_discriminant (quadratic_test &testcase)
135 {
136   /* Build "calc_discriminant".  */
137   gccjit::param param_q =
138     testcase.ctxt.new_param (testcase.quadratic_ptr, "q");
139   std::vector <gccjit::param> params (1);
140   params[0] = param_q;
141   testcase.calc_discriminant =
142     testcase.ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
143 				testcase.void_type,
144 				"calc_discriminant",
145 				params,
146 				0);
147   gccjit::block block = testcase.calc_discriminant.new_block ();
148   block.add_comment ("(b^2 - 4ac)");
149 
150   gccjit::rvalue q_a = param_q.dereference_field (testcase.a);
151   gccjit::rvalue q_b = param_q.dereference_field (testcase.b);
152   gccjit::rvalue q_c = param_q.dereference_field (testcase.c);
153 
154   block.add_assignment (
155     /* q->discriminant =...  */
156     param_q.dereference_field (testcase.discriminant),
157     /* (q->b * q->b) - (4 * q->a * q->c) */
158     testcase.ctxt.new_minus (
159       testcase.numeric_type,
160 
161       /* (q->b * q->b) */
162       testcase.ctxt.new_mult (
163 	testcase.numeric_type,
164 	q_b, q_b),
165 
166       /* (4 * (q->a * q->c)) */
167       testcase.ctxt.new_mult (
168 	testcase.numeric_type,
169 	/* 4.0 */
170 	testcase.ctxt.new_rvalue (
171 	  testcase.numeric_type,
172 	  4),
173 	/* (q->a * q->c) */
174 	testcase.ctxt.new_mult (
175 	  testcase.numeric_type,
176 	  q_a, q_c)))); /* end of add_assignment call.	*/
177   block.end_with_return ();
178 }
179 
180 static void
make_test_quadratic(quadratic_test & testcase)181 make_test_quadratic (quadratic_test &testcase)
182 {
183   gccjit::param a = testcase.ctxt.new_param (testcase.numeric_type, "a");
184   gccjit::param b = testcase.ctxt.new_param (testcase.numeric_type, "b");
185   gccjit::param c = testcase.ctxt.new_param (testcase.numeric_type, "c");
186   gccjit::param r1 =
187     testcase.ctxt.new_param (testcase.numeric_type_ptr, "r1");
188   gccjit::param r2 =
189     testcase.ctxt.new_param (testcase.numeric_type_ptr, "r2");
190 
191   std::vector<gccjit::param> params (5);
192   params[0] = a;
193   params[1] = b;
194   params[2] = c;
195   params[3] = r1;
196   params[4] = r2;
197 
198   gccjit::function test_quadratic =
199     testcase.ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
200 				testcase.int_type,
201 				"test_quadratic",
202 				params,
203 				0);
204 
205   /* struct quadratic q; */
206   gccjit::lvalue q = test_quadratic.new_local (testcase.quadratic, "q");
207 
208   gccjit::block initial = test_quadratic.new_block ("initial");
209   gccjit::block on_positive_discriminant
210     = test_quadratic.new_block ("positive_discriminant");
211   gccjit::block on_nonpositive_discriminant
212     = test_quadratic.new_block ("nonpositive_discriminant");
213   gccjit::block on_zero_discriminant
214     = test_quadratic.new_block ("zero_discriminant");
215   gccjit::block on_negative_discriminant
216     = test_quadratic.new_block ("negative_discriminant");
217 
218   CHECK_STRING_VALUE (on_zero_discriminant.get_debug_string ().c_str (),
219                       "zero_discriminant");
220 
221   /* q.a = a; */
222   initial.add_assignment (q.access_field (testcase.a), a);
223   /* q.b = b; */
224   initial.add_assignment (q.access_field (testcase.b), b);
225   /* q.c = c; */
226   initial.add_assignment (q.access_field (testcase.c), c);
227   /* calc_discriminant (&q); */
228   gccjit::rvalue address_of_q = q.get_address ();
229   initial.add_call (testcase.calc_discriminant, address_of_q);
230 
231   initial.add_comment ("if (q.discriminant > 0)");
232   initial.end_with_conditional (
233     testcase.ctxt.new_gt (
234       q.access_field (testcase.discriminant),
235       testcase.zero),
236     on_positive_discriminant,
237     on_nonpositive_discriminant);
238 
239   /* Block: "on_positive_discriminant" */
240   /* double s = sqrt (q.discriminant); */
241   gccjit::lvalue s = test_quadratic.new_local (testcase.numeric_type, "s");
242   gccjit::rvalue discriminant_of_q = q.access_field (testcase.discriminant);
243   on_positive_discriminant.add_assignment (
244     s,
245     testcase.ctxt.new_call (testcase.sqrt, discriminant_of_q));
246 
247   gccjit::rvalue minus_b =
248     testcase.ctxt.new_minus (
249       testcase.numeric_type,
250       b);
251   gccjit::rvalue two_a =
252     testcase.ctxt.new_mult (
253       testcase.numeric_type,
254       testcase.ctxt.new_rvalue (testcase.numeric_type, 2),
255       a);
256   CHECK_STRING_VALUE (two_a.get_debug_string ().c_str (),
257                       "(double)2 * a");
258 
259   on_positive_discriminant.add_comment ("*r1 = (-b + s) / (2 * a);");
260   on_positive_discriminant.add_assignment (
261     /* "*r1 = ..." */
262     r1.dereference (),
263 
264     /* (-b + s) / (2 * a) */
265     testcase.ctxt.new_divide (
266       testcase.numeric_type,
267       testcase.ctxt.new_plus (
268 	testcase.numeric_type,
269 	minus_b,
270 	s),
271       two_a));
272 
273   on_positive_discriminant.add_comment ("*r2 = (-b - s) / (2 * a)");
274   on_positive_discriminant.add_assignment (
275     /* "*r2 = ..." */
276     r2.dereference (),
277 
278     /* (-b - s) / (2 * a) */
279     testcase.ctxt.new_divide (
280       testcase.numeric_type,
281       testcase.ctxt.new_minus (
282 	testcase.numeric_type,
283 	minus_b,
284 	s),
285       two_a));
286 
287   /* "return 2;" */
288   on_positive_discriminant.end_with_return (
289     testcase.ctxt.new_rvalue (testcase.int_type, 2));
290 
291   /* Block: "on_nonpositive_discriminant" */
292   on_nonpositive_discriminant.add_comment ("else if (q.discriminant == 0)");
293   on_nonpositive_discriminant.end_with_conditional (
294     testcase.ctxt.new_eq (
295       q.access_field (testcase.discriminant),
296       testcase.zero),
297     on_zero_discriminant,
298     on_negative_discriminant);
299 
300   /* Block: "on_zero_discriminant" */
301   /* if (q.discriminant == 0) */
302   on_zero_discriminant.add_comment ("*r1 = -b / (2 * a);");
303   on_zero_discriminant.add_assignment (
304     /* "*r1 = ..." */
305     r1.dereference (),
306 
307     /* -b / (2 * a) */
308     testcase.ctxt.new_divide (
309       testcase.numeric_type,
310       minus_b,
311       two_a));
312 
313   /* "return 1;" */
314   on_zero_discriminant.end_with_return (
315     testcase.ctxt.one (testcase.int_type));
316 
317   /* Block: "on_negative_discriminant" */
318   /* else return 0; */
319   on_negative_discriminant.end_with_return (
320     testcase.ctxt.zero (testcase.int_type));
321 
322   /* Verify that output stream operator << works.  */
323   std::ostringstream os;
324   os << "streamed output: " << address_of_q;
325   CHECK_STRING_VALUE (os.str ().c_str (), "streamed output: &q");
326 }
327 
328 void
create_code(gcc_jit_context * ctxt,void * user_data)329 create_code (gcc_jit_context *ctxt, void *user_data)
330 {
331   struct quadratic_test testcase = {};
332   testcase.ctxt = ctxt;
333   make_types (testcase);
334   make_sqrt (testcase);
335   make_calc_discriminant (testcase);
336   make_test_quadratic (testcase);
337 }
338 
339 void
verify_code(gcc_jit_context * ctxt,gcc_jit_result * result)340 verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
341 {
342   typedef int (*fn_type) (double a, double b, double c,
343 			  double *r1, double *r2);
344 
345   CHECK_NON_NULL (result);
346 
347   fn_type test_quadratic =
348     (fn_type)gcc_jit_result_get_code (result, "test_quadratic");
349   CHECK_NON_NULL (test_quadratic);
350 
351   /* Verify that the code correctly solves quadratic equations.  */
352   double r1, r2;
353 
354   /* This one has two solutions: */
355   CHECK_VALUE (test_quadratic (1, 3, -4, &r1, &r2), 2);
356   CHECK_VALUE (r1, 1);
357   CHECK_VALUE (r2, -4);
358 
359   /* This one has one solution: */
360   CHECK_VALUE (test_quadratic (4, 4, 1, &r1, &r2), 1);
361   CHECK_VALUE (r1, -0.5);
362 
363   /* This one has no real solutions: */
364   CHECK_VALUE (test_quadratic (4, 1, 1, &r1, &r2), 0);
365 }
366