384 lines
12 KiB
C
384 lines
12 KiB
C
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
#include "libgccjit.h"
|
|
|
|
#include "harness.h"
|
|
|
|
/**********************************************************************
|
|
GCC_JIT_FUNCTION_ALWAYS_INLINE and GCC_JIT_FUNCTION_INTERNAL
|
|
**********************************************************************/
|
|
static void
|
|
create_test_of_hidden_function (gcc_jit_context *ctxt,
|
|
enum gcc_jit_function_kind hidden_kind,
|
|
const char *hidden_func_name,
|
|
const char *visible_func_name)
|
|
{
|
|
/* Let's try to inject the equivalent of:
|
|
static double hidden_mult (double a, double b)
|
|
{
|
|
return x * x;
|
|
}
|
|
double my_square (double x)
|
|
{
|
|
return my_mult (x, x);
|
|
}
|
|
|
|
where hidden_mult can potentially be
|
|
inline __attribute__((always_inline)). */
|
|
gcc_jit_type *double_type =
|
|
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
|
|
|
|
/* Create "my_mult" */
|
|
gcc_jit_param *param_a =
|
|
gcc_jit_context_new_param (ctxt, NULL, double_type, "a");
|
|
gcc_jit_param *param_b =
|
|
gcc_jit_context_new_param (ctxt, NULL, double_type, "b");
|
|
gcc_jit_param *params[2] = {param_a, param_b};
|
|
gcc_jit_function *my_mult =
|
|
gcc_jit_context_new_function (ctxt, NULL,
|
|
hidden_kind,
|
|
double_type,
|
|
hidden_func_name,
|
|
2, params,
|
|
0);
|
|
gcc_jit_block *body_of_my_mult =
|
|
gcc_jit_function_new_block (my_mult, NULL);
|
|
gcc_jit_block_end_with_return (
|
|
body_of_my_mult, NULL,
|
|
gcc_jit_context_new_binary_op (
|
|
ctxt, NULL,
|
|
GCC_JIT_BINARY_OP_MULT,
|
|
double_type,
|
|
gcc_jit_param_as_rvalue (param_a),
|
|
gcc_jit_param_as_rvalue (param_b)));
|
|
|
|
/* Create "my_square" */
|
|
gcc_jit_param *param_x =
|
|
gcc_jit_context_new_param (ctxt, NULL, double_type, "x");
|
|
gcc_jit_function *my_square =
|
|
gcc_jit_context_new_function (ctxt, NULL,
|
|
GCC_JIT_FUNCTION_EXPORTED,
|
|
double_type,
|
|
visible_func_name,
|
|
1, ¶m_x,
|
|
0);
|
|
gcc_jit_block *body_of_my_square =
|
|
gcc_jit_function_new_block (my_square, NULL);
|
|
gcc_jit_rvalue *args[2] = {gcc_jit_param_as_rvalue (param_x),
|
|
gcc_jit_param_as_rvalue (param_x)};
|
|
gcc_jit_block_end_with_return (
|
|
body_of_my_square, NULL,
|
|
gcc_jit_context_new_call (
|
|
ctxt, NULL,
|
|
my_mult,
|
|
2, args));
|
|
}
|
|
|
|
static void
|
|
create_tests_of_hidden_functions (gcc_jit_context *ctxt)
|
|
{
|
|
create_test_of_hidden_function (ctxt,
|
|
GCC_JIT_FUNCTION_INTERNAL,
|
|
"my_internal_mult",
|
|
"my_square_with_internal");
|
|
create_test_of_hidden_function (ctxt,
|
|
GCC_JIT_FUNCTION_ALWAYS_INLINE,
|
|
"my_always_inline_mult",
|
|
"my_square_with_always_inline");
|
|
}
|
|
|
|
static void
|
|
verify_hidden_functions (gcc_jit_context *ctxt, gcc_jit_result *result)
|
|
{
|
|
CHECK_NON_NULL (result);
|
|
|
|
/* GCC_JIT_FUNCTION_INTERNAL and GCC_JIT_FUNCTION_ALWAYS_INLINE
|
|
functions should not be accessible in the result. */
|
|
CHECK_VALUE (NULL, gcc_jit_result_get_code (result, "my_internal_mult"));
|
|
CHECK_VALUE (NULL, gcc_jit_result_get_code (result, "my_always_inline_mult"));
|
|
|
|
typedef double (*fn_type) (double);
|
|
fn_type my_square_with_internal =
|
|
(fn_type)gcc_jit_result_get_code (result, "my_square_with_internal");
|
|
CHECK_NON_NULL (my_square_with_internal);
|
|
CHECK_VALUE (my_square_with_internal (5.0), 25.0);
|
|
|
|
fn_type my_square_with_always_inline =
|
|
(fn_type)gcc_jit_result_get_code (result, "my_square_with_always_inline");
|
|
CHECK_NON_NULL (my_square_with_always_inline);
|
|
CHECK_VALUE (my_square_with_always_inline (5.0), 25.0);
|
|
}
|
|
|
|
/**********************************************************************
|
|
Builtin functions
|
|
**********************************************************************/
|
|
|
|
static void
|
|
create_test_of_builtin_strcmp (gcc_jit_context *ctxt)
|
|
{
|
|
/* Let's try to inject the equivalent of:
|
|
int
|
|
test_of_builtin_strcmp (const char *a, const char *b)
|
|
{
|
|
return __builtin_strcmp (a, b);
|
|
}
|
|
*/
|
|
gcc_jit_type *int_type =
|
|
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
|
|
gcc_jit_type *const_char_ptr_type =
|
|
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CONST_CHAR_PTR);
|
|
|
|
/* Get the built-in function. */
|
|
gcc_jit_function *builtin_fn =
|
|
gcc_jit_context_get_builtin_function (ctxt, "strcmp");
|
|
|
|
CHECK_STRING_VALUE (
|
|
gcc_jit_object_get_debug_string (gcc_jit_function_as_object (builtin_fn)),
|
|
"strcmp");
|
|
|
|
/* Build the test_fn. */
|
|
gcc_jit_param *param_a =
|
|
gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "a");
|
|
gcc_jit_param *param_b =
|
|
gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "b");
|
|
gcc_jit_param *params[2] = {param_a, param_b};
|
|
gcc_jit_function *test_fn =
|
|
gcc_jit_context_new_function (ctxt, NULL,
|
|
GCC_JIT_FUNCTION_EXPORTED,
|
|
int_type,
|
|
"test_of_builtin_strcmp",
|
|
2, params,
|
|
0);
|
|
gcc_jit_rvalue *args[2] = {gcc_jit_param_as_rvalue (param_a),
|
|
gcc_jit_param_as_rvalue (param_b)};
|
|
gcc_jit_rvalue *call =
|
|
gcc_jit_context_new_call (ctxt,
|
|
NULL,
|
|
builtin_fn,
|
|
2, args);
|
|
CHECK_STRING_VALUE (
|
|
gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (call)),
|
|
"strcmp (a, b)");
|
|
|
|
gcc_jit_block *initial =
|
|
gcc_jit_function_new_block (test_fn, "initial");
|
|
gcc_jit_block_end_with_return (initial, NULL, call);
|
|
}
|
|
|
|
static char *trig_sincos_dump;
|
|
static char *trig_statistics_dump;
|
|
|
|
static void
|
|
create_test_of_builtin_trig (gcc_jit_context *ctxt)
|
|
{
|
|
/* Let's try to inject the equivalent of:
|
|
int
|
|
test_of_builtin_trig (double theta)
|
|
{
|
|
return 2 * sin (theta) * cos (theta);
|
|
}
|
|
(in theory, optimizable to sin (2 * theta))
|
|
*/
|
|
|
|
gcc_jit_context_enable_dump (ctxt,
|
|
"tree-sincos",
|
|
&trig_sincos_dump);
|
|
gcc_jit_context_enable_dump (ctxt,
|
|
"statistics",
|
|
&trig_statistics_dump);
|
|
|
|
gcc_jit_type *double_t =
|
|
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE);
|
|
|
|
/* Get the built-in functions. */
|
|
gcc_jit_function *builtin_sin =
|
|
gcc_jit_context_get_builtin_function (ctxt, "sin");
|
|
gcc_jit_function *builtin_cos =
|
|
gcc_jit_context_get_builtin_function (ctxt, "cos");
|
|
|
|
/* Build the test_fn. */
|
|
gcc_jit_param *param_theta =
|
|
gcc_jit_context_new_param (ctxt, NULL, double_t, "theta");
|
|
gcc_jit_function *test_fn =
|
|
gcc_jit_context_new_function (ctxt, NULL,
|
|
GCC_JIT_FUNCTION_EXPORTED,
|
|
double_t,
|
|
"test_of_builtin_trig",
|
|
1, ¶m_theta,
|
|
0);
|
|
gcc_jit_rvalue *args[1] = {gcc_jit_param_as_rvalue (param_theta)};
|
|
gcc_jit_rvalue *two =
|
|
gcc_jit_context_new_rvalue_from_int (ctxt, double_t, 2);
|
|
gcc_jit_rvalue *ret =
|
|
gcc_jit_context_new_binary_op (
|
|
ctxt, NULL,
|
|
GCC_JIT_BINARY_OP_MULT,
|
|
double_t,
|
|
two,
|
|
gcc_jit_context_new_binary_op (
|
|
ctxt, NULL,
|
|
GCC_JIT_BINARY_OP_MULT,
|
|
double_t,
|
|
gcc_jit_context_new_call (ctxt, NULL,
|
|
builtin_sin,
|
|
1, args),
|
|
gcc_jit_context_new_call (ctxt, NULL,
|
|
builtin_cos,
|
|
1, args)));
|
|
CHECK_STRING_VALUE (
|
|
gcc_jit_object_get_debug_string (gcc_jit_rvalue_as_object (ret)),
|
|
"(double)2 * sin (theta) * cos (theta)");
|
|
|
|
gcc_jit_block *initial =
|
|
gcc_jit_function_new_block (test_fn, "initial");
|
|
gcc_jit_block_end_with_return (initial, NULL, ret);
|
|
}
|
|
|
|
static void
|
|
create_use_of_builtins (gcc_jit_context *ctxt)
|
|
{
|
|
create_test_of_builtin_strcmp (ctxt);
|
|
create_test_of_builtin_trig (ctxt);
|
|
}
|
|
|
|
static void
|
|
verify_test_of_builtin_strcmp (gcc_jit_context *ctxt, gcc_jit_result *result)
|
|
{
|
|
typedef int (*fn_type) (const char *, const char *);
|
|
CHECK_NON_NULL (result);
|
|
|
|
fn_type test_of_builtin_strcmp =
|
|
(fn_type)gcc_jit_result_get_code (result, "test_of_builtin_strcmp");
|
|
CHECK_NON_NULL (test_of_builtin_strcmp);
|
|
|
|
/* Verify that it correctly called strcmp. */
|
|
CHECK_VALUE (test_of_builtin_strcmp ("foo", "foo"), 0);
|
|
CHECK (test_of_builtin_strcmp ("foo", "bar") > 0);
|
|
CHECK (test_of_builtin_strcmp ("bar", "foo") < 0);
|
|
}
|
|
|
|
static void
|
|
verify_test_of_builtin_trig (gcc_jit_context *ctxt, gcc_jit_result *result)
|
|
{
|
|
typedef double (*fn_type) (double);
|
|
CHECK_NON_NULL (result);
|
|
|
|
fn_type test_of_builtin_trig =
|
|
(fn_type)gcc_jit_result_get_code (result, "test_of_builtin_trig");
|
|
CHECK_NON_NULL (test_of_builtin_trig);
|
|
|
|
/* Verify that it correctly computes
|
|
sin (2 * theta)
|
|
(perhaps calling sin and cos). */
|
|
CHECK_DOUBLE_VALUE (test_of_builtin_trig (0.0 ), 0.0);
|
|
CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_4 ), 1.0);
|
|
CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_2 ), 0.0);
|
|
CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI_4 * 3.0), -1.0);
|
|
CHECK_DOUBLE_VALUE (test_of_builtin_trig (M_PI ), 0.0);
|
|
|
|
/* PR jit/64020:
|
|
The "sincos" pass merges sin/cos calls into the cexpi builtin.
|
|
Verify that a dump of the "sincos" pass was provided, and that it
|
|
shows a call to the cexpi builtin on a SSA name of "theta". */
|
|
CHECK_NON_NULL (trig_sincos_dump);
|
|
CHECK_STRING_CONTAINS (trig_sincos_dump, " = __builtin_cexpi (theta_");
|
|
free (trig_sincos_dump);
|
|
|
|
/* Similarly, verify that the statistics dump was provided, and that
|
|
it shows the sincos optimization. */
|
|
CHECK_NON_NULL (trig_statistics_dump);
|
|
CHECK_STRING_CONTAINS (
|
|
trig_statistics_dump,
|
|
"sincos \"sincos statements inserted\" \"test_of_builtin_trig\" 1");
|
|
free (trig_statistics_dump);
|
|
}
|
|
|
|
static void
|
|
verify_use_of_builtins (gcc_jit_context *ctxt, gcc_jit_result *result)
|
|
{
|
|
verify_test_of_builtin_strcmp (ctxt, result);
|
|
verify_test_of_builtin_trig (ctxt, result);
|
|
}
|
|
|
|
/**********************************************************************
|
|
"void" return
|
|
**********************************************************************/
|
|
|
|
static void
|
|
create_use_of_void_return (gcc_jit_context *ctxt)
|
|
{
|
|
/* Let's try to inject the equivalent of:
|
|
void
|
|
test_of_void_return (int *out)
|
|
{
|
|
*out = 1;
|
|
return;
|
|
}
|
|
*/
|
|
gcc_jit_type *void_t =
|
|
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
|
|
gcc_jit_type *int_t =
|
|
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
|
|
gcc_jit_type *int_ptr_t =
|
|
gcc_jit_type_get_pointer (int_t);
|
|
|
|
/* Build the test_fn. */
|
|
gcc_jit_param *param_out =
|
|
gcc_jit_context_new_param (ctxt, NULL, int_ptr_t, "out");
|
|
gcc_jit_function *test_fn =
|
|
gcc_jit_context_new_function (ctxt, NULL,
|
|
GCC_JIT_FUNCTION_EXPORTED,
|
|
void_t,
|
|
"test_of_void_return",
|
|
1, ¶m_out,
|
|
0);
|
|
gcc_jit_block *initial =
|
|
gcc_jit_function_new_block (test_fn, "initial");
|
|
|
|
gcc_jit_block_add_assignment (
|
|
initial, NULL,
|
|
/* "*out = ..." */
|
|
gcc_jit_rvalue_dereference (gcc_jit_param_as_rvalue (param_out),
|
|
NULL),
|
|
gcc_jit_context_one (ctxt, int_t));
|
|
gcc_jit_block_end_with_void_return (initial, NULL);
|
|
}
|
|
|
|
static void
|
|
verify_void_return (gcc_jit_context *ctxt, gcc_jit_result *result)
|
|
{
|
|
typedef void (*fn_type) (int *);
|
|
CHECK_NON_NULL (result);
|
|
|
|
fn_type test_of_void_return =
|
|
(fn_type)gcc_jit_result_get_code (result, "test_of_void_return");
|
|
CHECK_NON_NULL (test_of_void_return);
|
|
|
|
int i;
|
|
test_of_void_return (&i);
|
|
CHECK_VALUE (i, 1); /* ensure correct value was written back */
|
|
}
|
|
|
|
/**********************************************************************
|
|
Code for harness
|
|
**********************************************************************/
|
|
|
|
void
|
|
create_code (gcc_jit_context *ctxt, void *user_data)
|
|
{
|
|
create_tests_of_hidden_functions (ctxt);
|
|
create_use_of_builtins (ctxt);
|
|
create_use_of_void_return (ctxt);
|
|
}
|
|
|
|
|
|
void
|
|
verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
|
|
{
|
|
verify_hidden_functions (ctxt, result);
|
|
verify_use_of_builtins (ctxt, result);
|
|
verify_void_return (ctxt, result);
|
|
}
|