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.cc, 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 operator overloading for maxium brevity, at the
54   risk of perhaps being too "magical".
55 */
56 
57 /****************************************************************************
58  Test case
59  ****************************************************************************/
60 
61 struct quadratic_test
62 {
63   gccjit::context ctxt;
64 
65   /* "double" and "(double *)".  */
66   gccjit::type numeric_type;
67   gccjit::type numeric_type_ptr;
68 
69   /* The value (double)0.  */
70   gccjit::rvalue zero;
71 
72   gccjit::type int_type;
73   gccjit::type void_type;
74 
75   /* "struct quadratic" */
76   gccjit::type quadratic;
77   gccjit::field a;
78   gccjit::field b;
79   gccjit::field c;
80   gccjit::field discriminant;
81 
82   /* "(struct quadratic *)" */
83   gccjit::type quadratic_ptr;
84 
85   gccjit::function calc_discriminant;
86 
87   gccjit::function sqrt;
88 
89 };
90 
91 static void
make_types(quadratic_test & testcase)92 make_types (quadratic_test &testcase)
93 {
94   testcase.numeric_type = testcase.ctxt.get_type (GCC_JIT_TYPE_DOUBLE);
95   testcase.numeric_type_ptr = testcase.numeric_type.get_pointer ();
96   testcase.zero = testcase.ctxt.zero (testcase.numeric_type);
97 
98   testcase.int_type = testcase.ctxt.get_int_type <int> ();
99   testcase.void_type = testcase.ctxt.get_type (GCC_JIT_TYPE_VOID);
100 
101   testcase.a = testcase.ctxt.new_field (testcase.numeric_type, "a");
102   testcase.b = testcase.ctxt.new_field (testcase.numeric_type, "b");
103   testcase.c = testcase.ctxt.new_field (testcase.numeric_type, "c");
104   testcase.discriminant =
105     testcase.ctxt.new_field (testcase.numeric_type, "discriminant");
106   CHECK_STRING_VALUE (testcase.discriminant.get_debug_string ().c_str (),
107                       "discriminant");
108   std::vector<gccjit::field> fields (4);
109   fields[0] = testcase.a;
110   fields[1] = testcase.b;
111   fields[2] = testcase.c;
112   fields[3] = testcase.discriminant;
113   testcase.quadratic =
114     testcase.ctxt.new_struct_type (
115       "quadratic",
116       fields);
117   testcase.quadratic_ptr = testcase.quadratic.get_pointer ();
118 }
119 
120 static void
make_sqrt(quadratic_test & testcase)121 make_sqrt (quadratic_test &testcase)
122 {
123   std::vector<gccjit::param> params (1);
124   params[0] =
125     testcase.ctxt.new_param (testcase.numeric_type, "x");
126   testcase.sqrt =
127     testcase.ctxt.new_function (GCC_JIT_FUNCTION_IMPORTED,
128 				testcase.numeric_type,
129 				"sqrt",
130 				params,
131 				0);
132 }
133 
134 static void
make_calc_discriminant(quadratic_test & testcase)135 make_calc_discriminant (quadratic_test &testcase)
136 {
137   /* Build "calc_discriminant".  */
138   gccjit::param param_q =
139     testcase.ctxt.new_param (testcase.quadratic_ptr, "q");
140   std::vector <gccjit::param> params (1);
141   params[0] = param_q;
142   testcase.calc_discriminant =
143     testcase.ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
144 				testcase.void_type,
145 				"calc_discriminant",
146 				params,
147 				0);
148   gccjit::block block = testcase.calc_discriminant.new_block ();
149   block.add_comment ("(b^2 - 4ac)");
150 
151   gccjit::rvalue q_a = param_q.dereference_field (testcase.a);
152   gccjit::rvalue q_b = param_q.dereference_field (testcase.b);
153   gccjit::rvalue q_c = param_q.dereference_field (testcase.c);
154 
155   gccjit::rvalue four =
156     testcase.ctxt.new_rvalue (testcase.numeric_type, 4);
157 
158   block.add_assignment (
159     /* q->discriminant =...  */
160     param_q.dereference_field (testcase.discriminant),
161     /* (q->b * q->b) - (4 * q->a * q->c) */
162     (q_b * q_b) - (four * q_a * q_c));
163   block.end_with_return ();
164 }
165 
166 static void
make_test_quadratic(quadratic_test & testcase)167 make_test_quadratic (quadratic_test &testcase)
168 {
169   gccjit::param a = testcase.ctxt.new_param (testcase.numeric_type, "a");
170   gccjit::param b = testcase.ctxt.new_param (testcase.numeric_type, "b");
171   gccjit::param c = testcase.ctxt.new_param (testcase.numeric_type, "c");
172   gccjit::param r1 =
173     testcase.ctxt.new_param (testcase.numeric_type_ptr, "r1");
174   gccjit::param r2 =
175     testcase.ctxt.new_param (testcase.numeric_type_ptr, "r2");
176 
177   std::vector<gccjit::param> params (5);
178   params[0] = a;
179   params[1] = b;
180   params[2] = c;
181   params[3] = r1;
182   params[4] = r2;
183 
184   gccjit::function test_quadratic =
185     testcase.ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED,
186 				testcase.int_type,
187 				"test_quadratic",
188 				params,
189 				0);
190 
191   /* struct quadratic q; */
192   gccjit::lvalue q = test_quadratic.new_local (testcase.quadratic, "q");
193 
194   gccjit::block initial = test_quadratic.new_block ("initial");
195   gccjit::block on_positive_discriminant
196     = test_quadratic.new_block ("positive_discriminant");
197   gccjit::block on_nonpositive_discriminant
198     = test_quadratic.new_block ("nonpositive_discriminant");
199   gccjit::block on_zero_discriminant
200     = test_quadratic.new_block ("zero_discriminant");
201   gccjit::block on_negative_discriminant
202     = test_quadratic.new_block ("negative_discriminant");
203 
204   CHECK_STRING_VALUE (on_zero_discriminant.get_debug_string ().c_str (),
205                       "zero_discriminant");
206 
207   /* q.a = a; */
208   initial.add_assignment (q.access_field (testcase.a), a);
209   /* q.b = b; */
210   initial.add_assignment (q.access_field (testcase.b), b);
211   /* q.c = c; */
212   initial.add_assignment (q.access_field (testcase.c), c);
213   /* calc_discriminant (&q); */
214   gccjit::rvalue address_of_q = q.get_address ();
215   initial.add_eval (testcase.calc_discriminant (address_of_q));
216 
217   initial.add_comment ("if (q.discriminant > 0)");
218   initial.end_with_conditional (
219     q.access_field (testcase.discriminant) > testcase.zero,
220     on_positive_discriminant,
221     on_nonpositive_discriminant);
222 
223   /* Block: "on_positive_discriminant" */
224   /* double s = sqrt (q.discriminant); */
225   gccjit::lvalue s = test_quadratic.new_local (testcase.numeric_type, "s");
226   gccjit::rvalue discriminant_of_q = q.access_field (testcase.discriminant);
227   on_positive_discriminant.add_assignment (s, testcase.sqrt (discriminant_of_q));
228 
229   gccjit::rvalue minus_b = -b;
230   gccjit::rvalue two =
231     testcase.ctxt.new_rvalue (testcase.numeric_type, 2);
232   gccjit::rvalue two_a = two * a;
233   CHECK_STRING_VALUE (two_a.get_debug_string ().c_str (),
234                       "(double)2 * a");
235 
236   on_positive_discriminant.add_comment ("*r1 = (-b + s) / (2 * a);");
237   on_positive_discriminant.add_assignment (*r1, (minus_b + s) / two_a);
238 
239   on_positive_discriminant.add_comment ("*r2 = (-b - s) / (2 * a)");
240   on_positive_discriminant.add_assignment (*r2, (minus_b - s) / two_a);
241 
242   /* "return 2;" */
243   on_positive_discriminant.end_with_return (
244     testcase.ctxt.new_rvalue (testcase.int_type, 2));
245 
246   /* Block: "on_nonpositive_discriminant" */
247   /* "else if (q.discriminant == 0)" */
248   on_nonpositive_discriminant.add_comment ("else if (q.discriminant == 0)");
249   on_nonpositive_discriminant.end_with_conditional (
250     q.access_field (testcase.discriminant) == testcase.zero,
251     on_zero_discriminant,
252     on_negative_discriminant);
253 
254   /* Block: "on_zero_discriminant" */
255   /* if (q.discriminant == 0) */
256   on_zero_discriminant.add_comment ("*r1 = -b / (2 * a);");
257   on_zero_discriminant.add_assignment (*r1, minus_b / two_a);
258 
259   /* "return 1;" */
260   on_zero_discriminant.end_with_return (testcase.int_type.one ());
261 
262   /* Block: "on_negative_discriminant" */
263   /* else return 0; */
264   on_negative_discriminant.end_with_return (testcase.int_type.zero ());
265 
266   /* Verify that output stream operator << works.  */
267   std::ostringstream os;
268   os << "streamed output: " << address_of_q;
269   CHECK_STRING_VALUE (os.str ().c_str (), "streamed output: &q");
270 }
271 
272 void
create_code(gcc_jit_context * ctxt,void * user_data)273 create_code (gcc_jit_context *ctxt, void *user_data)
274 {
275   struct quadratic_test testcase = {};
276   testcase.ctxt = ctxt;
277   make_types (testcase);
278   make_sqrt (testcase);
279   make_calc_discriminant (testcase);
280   make_test_quadratic (testcase);
281 }
282 
283 void
verify_code(gcc_jit_context * ctxt,gcc_jit_result * result)284 verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
285 {
286   typedef int (*fn_type) (double a, double b, double c,
287 			  double *r1, double *r2);
288 
289   CHECK_NON_NULL (result);
290 
291   fn_type test_quadratic =
292     (fn_type)gcc_jit_result_get_code (result, "test_quadratic");
293   CHECK_NON_NULL (test_quadratic);
294 
295   /* Verify that the code correctly solves quadratic equations.  */
296   double r1, r2;
297 
298   /* This one has two solutions: */
299   CHECK_VALUE (test_quadratic (1, 3, -4, &r1, &r2), 2);
300   CHECK_VALUE (r1, 1);
301   CHECK_VALUE (r2, -4);
302 
303   /* This one has one solution: */
304   CHECK_VALUE (test_quadratic (4, 4, 1, &r1, &r2), 1);
305   CHECK_VALUE (r1, -0.5);
306 
307   /* This one has no real solutions: */
308   CHECK_VALUE (test_quadratic (4, 1, 1, &r1, &r2), 0);
309 }
310