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