6f7585deed
PR jit/95296 reports an ICE when using libgccjit to create a local of void type. This patch adds checking to various API entrypoints in libgccjit.c so that they fail gracefully with an error if the client code attempts to create various kinds of rvalues or types involving void types. The patch documents these and various pre-existing restrictions on types in the API. gcc/jit/ChangeLog: PR jit/95296 * docs/topics/expressions.rst (Unary Operations): Document that result_type of gcc_jit_context_new_unary_op must be a numeric type. (Binary Operations): Likewise for gcc_jit_context_new_binary_op. (Global variables): Document that "type" of gcc_jit_context_new_global must be non-`void`. * docs/topics/function-pointers.rst (gcc_jit_context_new_function_ptr_type): Document that the param_types must be non-void, but that return_type may be. * docs/topics/functions.rst (Params): Document that gcc_jit_context_new_param's type must be non-void. (Functions): Likewise for gcc_jit_function_new_local. * docs/topics/types.rst (gcc_jit_context_new_array_type): Document that the type must be non-void. (gcc_jit_context_new_field): Likewise. * docs/_build/texinfo/Makefile: Regenerate. * docs/_build/texinfo/libgccjit.texi: Regenerate. * libgccjit.c (gcc_jit_context_new_array_type): Fail if element_type is void. (gcc_jit_context_new_field): Likewise for "type". (gcc_jit_context_new_function_ptr_type): Likewise for each element of param_types. (gcc_jit_context_new_param): Likewise for "type". (gcc_jit_context_new_global): Likewise. (gcc_jit_function_new_local): Likewise. (gcc_jit_type_get_aligned): Likewise. gcc/testsuite/ChangeLog: PR jit/95296 * jit.dg/test-error-gcc_jit_context_new_global-void-type.c: New test. * jit.dg/test-error-gcc_jit_function_new_local-void-type.c: New test. * jit.dg/test-fuzzer.c (fuzzer_init): Allow for make_random_type to return NULL. (get_random_type): Allow for elements in f->types to be NULL.
3250 lines
96 KiB
C
3250 lines
96 KiB
C
/* Implementation of the C API; all wrappers into the internal C++ API
|
|
Copyright (C) 2013-2020 Free Software Foundation, Inc.
|
|
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it
|
|
under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3, or (at your option)
|
|
any later version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "timevar.h"
|
|
#include "typed-splay-tree.h"
|
|
#include "cppbuiltin.h"
|
|
#include <pthread.h>
|
|
|
|
#include "libgccjit.h"
|
|
#include "jit-recording.h"
|
|
#include "jit-result.h"
|
|
|
|
/* The opaque types used by the public API are actually subclasses
|
|
of the gcc::jit::recording classes. */
|
|
|
|
struct gcc_jit_context : public gcc::jit::recording::context
|
|
{
|
|
gcc_jit_context (gcc_jit_context *parent_ctxt) :
|
|
context (parent_ctxt)
|
|
{}
|
|
};
|
|
|
|
struct gcc_jit_result : public gcc::jit::result
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_object : public gcc::jit::recording::memento
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_location : public gcc::jit::recording::location
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_type : public gcc::jit::recording::type
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_struct : public gcc::jit::recording::struct_
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_field : public gcc::jit::recording::field
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_bitfield : public gcc::jit::recording::bitfield
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_function : public gcc::jit::recording::function
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_block : public gcc::jit::recording::block
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_rvalue : public gcc::jit::recording::rvalue
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_lvalue : public gcc::jit::recording::lvalue
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_param : public gcc::jit::recording::param
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_case : public gcc::jit::recording::case_
|
|
{
|
|
};
|
|
|
|
struct gcc_jit_timer : public timer
|
|
{
|
|
};
|
|
|
|
/**********************************************************************
|
|
Error-handling.
|
|
|
|
We try to gracefully handle API usage errors by being defensive
|
|
at the API boundary.
|
|
**********************************************************************/
|
|
|
|
#define JIT_BEGIN_STMT do {
|
|
#define JIT_END_STMT } while(0)
|
|
|
|
/* Each of these error-handling macros determines if TEST_EXPR holds.
|
|
|
|
If TEXT_EXPR fails to hold we return from the enclosing function and
|
|
print an error, either via adding an error on the given context CTXT
|
|
if CTXT is non-NULL, falling back to simply printing to stderr if CTXT
|
|
is NULL.
|
|
|
|
They have to be macros since they inject their "return" into the
|
|
function they are placed in.
|
|
|
|
The variant macros express:
|
|
|
|
(A) whether or not we need to return a value:
|
|
RETURN_VAL_IF_FAIL* vs
|
|
RETURN_IF_FAIL*,
|
|
with the former returning RETURN_EXPR, and
|
|
RETURN_NULL_IF_FAIL*
|
|
for the common case where a NULL value is to be returned on
|
|
error, and
|
|
|
|
(B) whether the error message is to be directly printed:
|
|
RETURN_*IF_FAIL
|
|
or is a format string with some number of arguments:
|
|
RETURN_*IF_FAIL_PRINTF*
|
|
|
|
They all use JIT_BEGIN_STMT/JIT_END_STMT so they can be written with
|
|
trailing semicolons.
|
|
*/
|
|
|
|
#define RETURN_VAL_IF_FAIL(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_MSG) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: %s", __func__, (ERR_MSG)); \
|
|
return (RETURN_EXPR); \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_VAL_IF_FAIL_PRINTF1(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0)); \
|
|
return (RETURN_EXPR); \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_VAL_IF_FAIL_PRINTF2(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0), (A1)); \
|
|
return (RETURN_EXPR); \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_VAL_IF_FAIL_PRINTF3(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0), (A1), (A2)); \
|
|
return (RETURN_EXPR); \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_VAL_IF_FAIL_PRINTF4(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0), (A1), (A2), (A3)); \
|
|
return (RETURN_EXPR); \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_VAL_IF_FAIL_PRINTF5(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0), (A1), (A2), (A3), (A4)); \
|
|
return (RETURN_EXPR); \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_VAL_IF_FAIL_PRINTF6(TEST_EXPR, RETURN_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0), (A1), (A2), (A3), (A4), (A5)); \
|
|
return (RETURN_EXPR); \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_NULL_IF_FAIL(TEST_EXPR, CTXT, LOC, ERR_MSG) \
|
|
RETURN_VAL_IF_FAIL ((TEST_EXPR), NULL, (CTXT), (LOC), (ERR_MSG))
|
|
|
|
#define RETURN_NULL_IF_FAIL_PRINTF1(TEST_EXPR, CTXT, LOC, ERR_FMT, A0) \
|
|
RETURN_VAL_IF_FAIL_PRINTF1 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0)
|
|
|
|
#define RETURN_NULL_IF_FAIL_PRINTF2(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1) \
|
|
RETURN_VAL_IF_FAIL_PRINTF2 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1)
|
|
|
|
#define RETURN_NULL_IF_FAIL_PRINTF3(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2) \
|
|
RETURN_VAL_IF_FAIL_PRINTF3 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2)
|
|
|
|
#define RETURN_NULL_IF_FAIL_PRINTF4(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) \
|
|
RETURN_VAL_IF_FAIL_PRINTF4 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3)
|
|
|
|
#define RETURN_NULL_IF_FAIL_PRINTF5(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4) \
|
|
RETURN_VAL_IF_FAIL_PRINTF5 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4)
|
|
|
|
#define RETURN_NULL_IF_FAIL_PRINTF6(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5) \
|
|
RETURN_VAL_IF_FAIL_PRINTF6 (TEST_EXPR, NULL, CTXT, LOC, ERR_FMT, A0, A1, A2, A3, A4, A5)
|
|
|
|
#define RETURN_IF_FAIL(TEST_EXPR, CTXT, LOC, ERR_MSG) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: %s", __func__, (ERR_MSG)); \
|
|
return; \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_IF_FAIL_PRINTF1(TEST_EXPR, CTXT, LOC, ERR_FMT, A0) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0)); \
|
|
return; \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_IF_FAIL_PRINTF2(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0), (A1)); \
|
|
return; \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
#define RETURN_IF_FAIL_PRINTF4(TEST_EXPR, CTXT, LOC, ERR_FMT, A0, A1, A2, A3) \
|
|
JIT_BEGIN_STMT \
|
|
if (!(TEST_EXPR)) \
|
|
{ \
|
|
jit_error ((CTXT), (LOC), "%s: " ERR_FMT, \
|
|
__func__, (A0), (A1), (A2), (A3)); \
|
|
return; \
|
|
} \
|
|
JIT_END_STMT
|
|
|
|
/* Check that BLOCK is non-NULL, and that it's OK to add statements to
|
|
it. This will fail if BLOCK has already been terminated by some
|
|
kind of jump or a return. */
|
|
#define RETURN_IF_NOT_VALID_BLOCK(BLOCK, LOC) \
|
|
JIT_BEGIN_STMT \
|
|
RETURN_IF_FAIL ((BLOCK), NULL, (LOC), "NULL block"); \
|
|
RETURN_IF_FAIL_PRINTF2 ( \
|
|
!(BLOCK)->has_been_terminated (), \
|
|
(BLOCK)->get_context (), \
|
|
(LOC), \
|
|
"adding to terminated block: %s (already terminated by: %s)", \
|
|
(BLOCK)->get_debug_string (), \
|
|
(BLOCK)->get_last_statement ()->get_debug_string ()); \
|
|
JIT_END_STMT
|
|
|
|
/* As RETURN_IF_NOT_VALID_BLOCK, but injecting a "return NULL;" if it
|
|
fails. */
|
|
#define RETURN_NULL_IF_NOT_VALID_BLOCK(BLOCK, LOC) \
|
|
JIT_BEGIN_STMT \
|
|
RETURN_NULL_IF_FAIL ((BLOCK), NULL, (LOC), "NULL block"); \
|
|
RETURN_NULL_IF_FAIL_PRINTF2 ( \
|
|
!(BLOCK)->has_been_terminated (), \
|
|
(BLOCK)->get_context (), \
|
|
(LOC), \
|
|
"adding to terminated block: %s (already terminated by: %s)", \
|
|
(BLOCK)->get_debug_string (), \
|
|
(BLOCK)->get_last_statement ()->get_debug_string ()); \
|
|
JIT_END_STMT
|
|
|
|
/* Format the given string, and report it as an error, either on CTXT
|
|
if non-NULL, or by printing to stderr if we have a NULL context.
|
|
LOC gives the source location where the error occcurred, and can be
|
|
NULL. */
|
|
|
|
static void
|
|
jit_error (gcc::jit::recording::context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *fmt, ...)
|
|
GNU_PRINTF(3, 4);
|
|
|
|
static void
|
|
jit_error (gcc::jit::recording::context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
va_start (ap, fmt);
|
|
|
|
if (ctxt)
|
|
ctxt->add_error_va (loc, fmt, ap);
|
|
else
|
|
{
|
|
/* No context? Send to stderr. */
|
|
vfprintf (stderr, fmt, ap);
|
|
fprintf (stderr, "\n");
|
|
}
|
|
|
|
va_end (ap);
|
|
}
|
|
|
|
/* Determine whether or not we can write to lvalues of type LTYPE from
|
|
rvalues of type RTYPE, detecting type errors such as attempting to
|
|
write to an int with a string literal (without an explicit cast).
|
|
|
|
This is implemented by calling the
|
|
gcc::jit::recording::type::accepts_writes_from virtual function on
|
|
LTYPE. */
|
|
|
|
static bool
|
|
compatible_types (gcc::jit::recording::type *ltype,
|
|
gcc::jit::recording::type *rtype)
|
|
{
|
|
return ltype->accepts_writes_from (rtype);
|
|
}
|
|
|
|
/* Public entrypoint for acquiring a gcc_jit_context.
|
|
Note that this creates a new top-level context; contrast with
|
|
gcc_jit_context_new_child_context below.
|
|
|
|
The real work is done in the constructor for
|
|
gcc::jit::recording::context in jit-recording.c. */
|
|
|
|
gcc_jit_context *
|
|
gcc_jit_context_acquire (void)
|
|
{
|
|
gcc_jit_context *ctxt = new gcc_jit_context (NULL);
|
|
ctxt->log ("new top-level ctxt: %p", (void *)ctxt);
|
|
return ctxt;
|
|
}
|
|
|
|
/* Public entrypoint for releasing a gcc_jit_context.
|
|
The real work is done in the destructor for
|
|
gcc::jit::recording::context in jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_release (gcc_jit_context *ctxt)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL ctxt");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
ctxt->log ("deleting ctxt: %p", (void *)ctxt);
|
|
delete ctxt;
|
|
}
|
|
|
|
/* Public entrypoint for creating a child context within
|
|
PARENT_CTXT. See description in libgccjit.h.
|
|
|
|
The real work is done in the constructor for
|
|
gcc::jit::recording::context in jit-recording.c. */
|
|
|
|
gcc_jit_context *
|
|
gcc_jit_context_new_child_context (gcc_jit_context *parent_ctxt)
|
|
{
|
|
RETURN_NULL_IF_FAIL (parent_ctxt, NULL, NULL, "NULL parent ctxt");
|
|
JIT_LOG_FUNC (parent_ctxt->get_logger ());
|
|
parent_ctxt->log ("parent_ctxt: %p", (void *)parent_ctxt);
|
|
gcc_jit_context *child_ctxt = new gcc_jit_context (parent_ctxt);
|
|
child_ctxt->log ("new child_ctxt: %p", (void *)child_ctxt);
|
|
return child_ctxt;
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_location
|
|
method in jit-recording.c. */
|
|
|
|
gcc_jit_location *
|
|
gcc_jit_context_new_location (gcc_jit_context *ctxt,
|
|
const char *filename,
|
|
int line,
|
|
int column)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
return (gcc_jit_location *)ctxt->new_location (filename, line, column, true);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (a location is a
|
|
memento), in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_location_as_object (gcc_jit_location *loc)
|
|
{
|
|
RETURN_NULL_IF_FAIL (loc, NULL, NULL, "NULL location");
|
|
|
|
return static_cast <gcc_jit_object *> (loc->as_object ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (a type is a
|
|
memento), in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_type_as_object (gcc_jit_type *type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
|
|
|
|
return static_cast <gcc_jit_object *> (type->as_object ());
|
|
}
|
|
|
|
/* Public entrypoint for getting a specific type from a context.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::get_type method, in
|
|
jit-recording.c */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_context_get_type (gcc_jit_context *ctxt,
|
|
enum gcc_jit_types type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
(type >= GCC_JIT_TYPE_VOID
|
|
&& type <= GCC_JIT_TYPE_FILE_PTR),
|
|
ctxt, NULL,
|
|
"unrecognized value for enum gcc_jit_types: %i", type);
|
|
|
|
return (gcc_jit_type *)ctxt->get_type (type);
|
|
}
|
|
|
|
/* Public entrypoint for getting the integer type of the given size and
|
|
signedness.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::get_int_type method,
|
|
in jit-recording.c. */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_context_get_int_type (gcc_jit_context *ctxt,
|
|
int num_bytes, int is_signed)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL (num_bytes >= 0, ctxt, NULL, "negative size");
|
|
|
|
return (gcc_jit_type *)ctxt->get_int_type (num_bytes, is_signed);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::type::get_pointer method, in
|
|
jit-recording.c */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_type_get_pointer (gcc_jit_type *type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
|
|
|
|
return (gcc_jit_type *)type->get_pointer ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::type::get_const method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_type_get_const (gcc_jit_type *type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
|
|
|
|
return (gcc_jit_type *)type->get_const ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::type::get_volatile method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_type_get_volatile (gcc_jit_type *type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
|
|
|
|
return (gcc_jit_type *)type->get_volatile ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_array_type method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_context_new_array_type (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_type *element_type,
|
|
int num_elements)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (element_type, ctxt, loc, "NULL type");
|
|
RETURN_NULL_IF_FAIL (num_elements >= 0, ctxt, NULL, "negative size");
|
|
RETURN_NULL_IF_FAIL (!element_type->is_void (), ctxt, loc,
|
|
"void type for elements");
|
|
|
|
return (gcc_jit_type *)ctxt->new_array_type (loc,
|
|
element_type,
|
|
num_elements);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_field method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_field *
|
|
gcc_jit_context_new_field (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_type *type,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
type->has_known_size (),
|
|
ctxt, loc,
|
|
"unknown size for field \"%s\" (type: %s)",
|
|
name,
|
|
type->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
!type->is_void (),
|
|
ctxt, loc,
|
|
"void type for field \"%s\"",
|
|
name);
|
|
|
|
return (gcc_jit_field *)ctxt->new_field (loc, type, name);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_bitfield method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_field *
|
|
gcc_jit_context_new_bitfield (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_type *type,
|
|
int width,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (type->is_int () || type->is_bool (),
|
|
ctxt, loc,
|
|
"bit-field %s has non integral type %s",
|
|
name, type->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
width > 0, ctxt, loc,
|
|
"invalid width %d for bitfield \"%s\" (must be > 0)",
|
|
width, name);
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
type->has_known_size (),
|
|
ctxt, loc,
|
|
"unknown size for field \"%s\" (type: %s)",
|
|
name,
|
|
type->get_debug_string ());
|
|
|
|
return (gcc_jit_field *)ctxt->new_bitfield (loc, type, width, name);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (a field is a
|
|
memento), in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_field_as_object (gcc_jit_field *field)
|
|
{
|
|
RETURN_NULL_IF_FAIL (field, NULL, NULL, "NULL field");
|
|
|
|
return static_cast <gcc_jit_object *> (field->as_object ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_struct_type method,
|
|
immediately followed by a "set_fields" call on the resulting
|
|
gcc::jit::recording::compound_type *, both in jit-recording.c */
|
|
|
|
gcc_jit_struct *
|
|
gcc_jit_context_new_struct_type (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *name,
|
|
int num_fields,
|
|
gcc_jit_field **fields)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
if (num_fields)
|
|
RETURN_NULL_IF_FAIL (fields, ctxt, loc, "NULL fields ptr");
|
|
for (int i = 0; i < num_fields; i++)
|
|
{
|
|
RETURN_NULL_IF_FAIL (fields[i], ctxt, loc, "NULL field ptr");
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
fields[i]->get_container () == NULL,
|
|
ctxt, loc,
|
|
"%s is already a field of %s",
|
|
fields[i]->get_debug_string (),
|
|
fields[i]->get_container ()->get_debug_string ());
|
|
}
|
|
|
|
gcc::jit::recording::struct_ *result =
|
|
ctxt->new_struct_type (loc, name);
|
|
result->set_fields (loc,
|
|
num_fields,
|
|
(gcc::jit::recording::field **)fields);
|
|
return static_cast<gcc_jit_struct *> (result);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_struct_type method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_struct *
|
|
gcc_jit_context_new_opaque_struct (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
|
|
return (gcc_jit_struct *)ctxt->new_struct_type (loc, name);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::struct_::as_object method in
|
|
jit-recording.h. */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_struct_as_type (gcc_jit_struct *struct_type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (struct_type, NULL, NULL, "NULL struct_type");
|
|
|
|
return static_cast <gcc_jit_type *> (struct_type->as_type ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::compound_type::set_fields method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_struct_set_fields (gcc_jit_struct *struct_type,
|
|
gcc_jit_location *loc,
|
|
int num_fields,
|
|
gcc_jit_field **fields)
|
|
{
|
|
RETURN_IF_FAIL (struct_type, NULL, loc, "NULL struct_type");
|
|
gcc::jit::recording::context *ctxt = struct_type->m_ctxt;
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_IF_FAIL_PRINTF1 (
|
|
struct_type->get_fields () == NULL, ctxt, loc,
|
|
"%s already has had fields set",
|
|
struct_type->get_debug_string ());
|
|
if (num_fields)
|
|
RETURN_IF_FAIL (fields, ctxt, loc, "NULL fields ptr");
|
|
for (int i = 0; i < num_fields; i++)
|
|
{
|
|
RETURN_IF_FAIL_PRINTF2 (
|
|
fields[i],
|
|
ctxt, loc,
|
|
"%s: NULL field ptr at index %i",
|
|
struct_type->get_debug_string (),
|
|
i);
|
|
RETURN_IF_FAIL_PRINTF2 (
|
|
fields[i]->get_container () == NULL,
|
|
ctxt, loc,
|
|
"%s is already a field of %s",
|
|
fields[i]->get_debug_string (),
|
|
fields[i]->get_container ()->get_debug_string ());
|
|
}
|
|
|
|
struct_type->set_fields (loc, num_fields,
|
|
(gcc::jit::recording::field **)fields);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_union_type method,
|
|
immediately followed by a "set_fields" call on the resulting
|
|
gcc::jit::recording::compound_type *, both in jit-recording.c */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_context_new_union_type (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *name,
|
|
int num_fields,
|
|
gcc_jit_field **fields)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
if (num_fields)
|
|
RETURN_NULL_IF_FAIL (fields, ctxt, loc, "NULL fields ptr");
|
|
for (int i = 0; i < num_fields; i++)
|
|
{
|
|
RETURN_NULL_IF_FAIL (fields[i], ctxt, loc, "NULL field ptr");
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
fields[i]->get_container () == NULL,
|
|
ctxt, loc,
|
|
"%s is already a field of %s",
|
|
fields[i]->get_debug_string (),
|
|
fields[i]->get_container ()->get_debug_string ());
|
|
}
|
|
|
|
gcc::jit::recording::union_ *result =
|
|
ctxt->new_union_type (loc, name);
|
|
result->set_fields (loc,
|
|
num_fields,
|
|
(gcc::jit::recording::field **)fields);
|
|
return (gcc_jit_type *) (result);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_function_ptr_type method,
|
|
in jit-recording.c */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_context_new_function_ptr_type (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_type *return_type,
|
|
int num_params,
|
|
gcc_jit_type **param_types,
|
|
int is_variadic)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (return_type, ctxt, loc, "NULL return_type");
|
|
RETURN_NULL_IF_FAIL (
|
|
(num_params == 0) || param_types,
|
|
ctxt, loc,
|
|
"NULL param_types creating function pointer type");
|
|
for (int i = 0; i < num_params; i++)
|
|
{
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (param_types[i],
|
|
ctxt, loc,
|
|
"NULL parameter type %i"
|
|
" creating function pointer type", i);
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (!param_types[i]->is_void (),
|
|
ctxt, loc,
|
|
"void type for param %i", i);
|
|
}
|
|
|
|
return (gcc_jit_type*)
|
|
ctxt->new_function_ptr_type (loc, return_type,
|
|
num_params,
|
|
(gcc::jit::recording::type **)param_types,
|
|
is_variadic);
|
|
}
|
|
|
|
/* Constructing functions. */
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_param method, in jit-recording.c */
|
|
|
|
gcc_jit_param *
|
|
gcc_jit_context_new_param (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_type *type,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (!type->is_void (),
|
|
ctxt, loc,
|
|
"void type for param \"%s\"", name);
|
|
|
|
return (gcc_jit_param *)ctxt->new_param (loc, type, name);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (a param is a memento),
|
|
in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_param_as_object (gcc_jit_param *param)
|
|
{
|
|
RETURN_NULL_IF_FAIL (param, NULL, NULL, "NULL param");
|
|
|
|
return static_cast <gcc_jit_object *> (param->as_object ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::param::as_lvalue method in jit-recording.h. */
|
|
|
|
gcc_jit_lvalue *
|
|
gcc_jit_param_as_lvalue (gcc_jit_param *param)
|
|
{
|
|
RETURN_NULL_IF_FAIL (param, NULL, NULL, "NULL param");
|
|
|
|
return (gcc_jit_lvalue *)param->as_lvalue ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::lvalue::as_rvalue method (a param is an rvalue),
|
|
in jit-recording.h. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_param_as_rvalue (gcc_jit_param *param)
|
|
{
|
|
RETURN_NULL_IF_FAIL (param, NULL, NULL, "NULL param");
|
|
|
|
return (gcc_jit_rvalue *)param->as_rvalue ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_function method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_function *
|
|
gcc_jit_context_new_function (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
enum gcc_jit_function_kind kind,
|
|
gcc_jit_type *return_type,
|
|
const char *name,
|
|
int num_params,
|
|
gcc_jit_param **params,
|
|
int is_variadic)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
((kind >= GCC_JIT_FUNCTION_EXPORTED)
|
|
&& (kind <= GCC_JIT_FUNCTION_ALWAYS_INLINE)),
|
|
ctxt, loc,
|
|
"unrecognized value for enum gcc_jit_function_kind: %i",
|
|
kind);
|
|
RETURN_NULL_IF_FAIL (return_type, ctxt, loc, "NULL return_type");
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
/* The assembler can only handle certain names, so for now, enforce
|
|
C's rules for identiers upon the name, using ISALPHA and ISALNUM
|
|
from safe-ctype.h to ignore the current locale.
|
|
Eventually we'll need some way to interact with e.g. C++ name
|
|
mangling. */
|
|
{
|
|
/* Leading char: */
|
|
char ch = *name;
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
ISALPHA (ch) || ch == '_',
|
|
ctxt, loc,
|
|
"name \"%s\" contains invalid character: '%c'",
|
|
name, ch);
|
|
/* Subsequent chars: */
|
|
for (const char *ptr = name + 1; (ch = *ptr); ptr++)
|
|
{
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
ISALNUM (ch) || ch == '_',
|
|
ctxt, loc,
|
|
"name \"%s\" contains invalid character: '%c'",
|
|
name, ch);
|
|
}
|
|
}
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
(num_params == 0) || params,
|
|
ctxt, loc,
|
|
"NULL params creating function %s", name);
|
|
for (int i = 0; i < num_params; i++)
|
|
{
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
params[i],
|
|
ctxt, loc,
|
|
"NULL parameter %i creating function %s", i, name);
|
|
RETURN_NULL_IF_FAIL_PRINTF5 (
|
|
params[i]->get_scope () == NULL,
|
|
ctxt, loc,
|
|
"parameter %i \"%s\""
|
|
" (type: %s)"
|
|
" for function %s"
|
|
" was already used for function %s",
|
|
i, params[i]->get_debug_string (),
|
|
params[i]->get_type ()->get_debug_string (),
|
|
name,
|
|
params[i]->get_scope ()->get_debug_string ());
|
|
}
|
|
|
|
return (gcc_jit_function*)
|
|
ctxt->new_function (loc, kind, return_type, name,
|
|
num_params,
|
|
(gcc::jit::recording::param **)params,
|
|
is_variadic,
|
|
BUILT_IN_NONE);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::get_builtin_function method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_function *
|
|
gcc_jit_context_get_builtin_function (gcc_jit_context *ctxt,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL (name, ctxt, NULL, "NULL name");
|
|
|
|
return static_cast <gcc_jit_function *> (ctxt->get_builtin_function (name));
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (a function is a
|
|
memento), in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_function_as_object (gcc_jit_function *func)
|
|
{
|
|
RETURN_NULL_IF_FAIL (func, NULL, NULL, "NULL function");
|
|
|
|
return static_cast <gcc_jit_object *> (func->as_object ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::function::get_param method, in
|
|
jit-recording.h. */
|
|
|
|
gcc_jit_param *
|
|
gcc_jit_function_get_param (gcc_jit_function *func, int index)
|
|
{
|
|
RETURN_NULL_IF_FAIL (func, NULL, NULL, "NULL function");
|
|
gcc::jit::recording::context *ctxt = func->m_ctxt;
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL (index >= 0, ctxt, NULL, "negative index");
|
|
int num_params = func->get_params ().length ();
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (index < num_params,
|
|
ctxt, NULL,
|
|
"index of %d is too large (%s has %d params)",
|
|
index,
|
|
func->get_debug_string (),
|
|
num_params);
|
|
|
|
return static_cast <gcc_jit_param *> (func->get_param (index));
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::function::dump_to_dot method, in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_function_dump_to_dot (gcc_jit_function *func,
|
|
const char *path)
|
|
{
|
|
RETURN_IF_FAIL (func, NULL, NULL, "NULL function");
|
|
gcc::jit::recording::context *ctxt = func->m_ctxt;
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_IF_FAIL (path, ctxt, NULL, "NULL path");
|
|
|
|
func->dump_to_dot (path);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::function::new_block method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_block*
|
|
gcc_jit_function_new_block (gcc_jit_function *func,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (func, NULL, NULL, "NULL function");
|
|
JIT_LOG_FUNC (func->get_context ()->get_logger ());
|
|
RETURN_NULL_IF_FAIL (func->get_kind () != GCC_JIT_FUNCTION_IMPORTED,
|
|
func->get_context (), NULL,
|
|
"cannot add block to an imported function");
|
|
/* name can be NULL. */
|
|
|
|
return (gcc_jit_block *)func->new_block (name);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (a block is a
|
|
memento), in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_block_as_object (gcc_jit_block *block)
|
|
{
|
|
RETURN_NULL_IF_FAIL (block, NULL, NULL, "NULL block");
|
|
|
|
return static_cast <gcc_jit_object *> (block->as_object ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::get_function method, in
|
|
jit-recording.h. */
|
|
|
|
gcc_jit_function *
|
|
gcc_jit_block_get_function (gcc_jit_block *block)
|
|
{
|
|
RETURN_NULL_IF_FAIL (block, NULL, NULL, "NULL block");
|
|
|
|
return static_cast <gcc_jit_function *> (block->get_function ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_global method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_lvalue *
|
|
gcc_jit_context_new_global (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
enum gcc_jit_global_kind kind,
|
|
gcc_jit_type *type,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
((kind >= GCC_JIT_GLOBAL_EXPORTED)
|
|
&& (kind <= GCC_JIT_GLOBAL_IMPORTED)),
|
|
ctxt, loc,
|
|
"unrecognized value for enum gcc_jit_global_kind: %i",
|
|
kind);
|
|
RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
type->has_known_size (),
|
|
ctxt, loc,
|
|
"unknown size for global \"%s\" (type: %s)",
|
|
name,
|
|
type->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
!type->is_void (),
|
|
ctxt, loc,
|
|
"void type for global \"%s\"",
|
|
name);
|
|
|
|
return (gcc_jit_lvalue *)ctxt->new_global (loc, kind, type, name);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (an lvalue is a
|
|
memento), in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_lvalue_as_object (gcc_jit_lvalue *lvalue)
|
|
{
|
|
RETURN_NULL_IF_FAIL (lvalue, NULL, NULL, "NULL lvalue");
|
|
|
|
return static_cast <gcc_jit_object *> (lvalue->as_object ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::lvalue::as_rvalue method in jit-recording.h. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_lvalue_as_rvalue (gcc_jit_lvalue *lvalue)
|
|
{
|
|
RETURN_NULL_IF_FAIL (lvalue, NULL, NULL, "NULL lvalue");
|
|
|
|
return (gcc_jit_rvalue *)lvalue->as_rvalue ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (an rvalue is a
|
|
memento), in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_rvalue_as_object (gcc_jit_rvalue *rvalue)
|
|
{
|
|
RETURN_NULL_IF_FAIL (rvalue, NULL, NULL, "NULL rvalue");
|
|
|
|
return static_cast <gcc_jit_object *> (rvalue->as_object ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::rvalue::get_type method, in
|
|
jit-recording.h. */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_rvalue_get_type (gcc_jit_rvalue *rvalue)
|
|
{
|
|
RETURN_NULL_IF_FAIL (rvalue, NULL, NULL, "NULL rvalue");
|
|
|
|
return static_cast <gcc_jit_type *> (rvalue->get_type ());
|
|
}
|
|
|
|
/* Verify that NUMERIC_TYPE is non-NULL, and that it is a "numeric"
|
|
type i.e. it satisfies gcc::jit::type::is_numeric (), such as the
|
|
result of gcc_jit_context_get_type (GCC_JIT_TYPE_INT). */
|
|
|
|
#define RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE(CTXT, NUMERIC_TYPE) \
|
|
JIT_BEGIN_STMT \
|
|
RETURN_NULL_IF_FAIL (NUMERIC_TYPE, CTXT, NULL, "NULL type"); \
|
|
RETURN_NULL_IF_FAIL_PRINTF1 ( \
|
|
NUMERIC_TYPE->is_numeric (), ctxt, NULL, \
|
|
"not a numeric type: %s", \
|
|
NUMERIC_TYPE->get_debug_string ()); \
|
|
JIT_END_STMT
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_rvalue_from_int method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_rvalue_from_int (gcc_jit_context *ctxt,
|
|
gcc_jit_type *numeric_type,
|
|
int value)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
|
|
|
|
return ((gcc_jit_rvalue *)ctxt
|
|
->new_rvalue_from_const <int> (numeric_type, value));
|
|
}
|
|
|
|
/* FIXME. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_rvalue_from_long (gcc_jit_context *ctxt,
|
|
gcc_jit_type *numeric_type,
|
|
long value)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
|
|
|
|
return ((gcc_jit_rvalue *)ctxt
|
|
->new_rvalue_from_const <long> (numeric_type, value));
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
This is essentially equivalent to:
|
|
gcc_jit_context_new_rvalue_from_int (ctxt, numeric_type, 0);
|
|
albeit with slightly different error messages if an error occurs. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_zero (gcc_jit_context *ctxt,
|
|
gcc_jit_type *numeric_type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
|
|
|
|
return gcc_jit_context_new_rvalue_from_int (ctxt, numeric_type, 0);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
This is essentially equivalent to:
|
|
gcc_jit_context_new_rvalue_from_int (ctxt, numeric_type, 1);
|
|
albeit with slightly different error messages if an error occurs. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_one (gcc_jit_context *ctxt,
|
|
gcc_jit_type *numeric_type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
|
|
|
|
return gcc_jit_context_new_rvalue_from_int (ctxt, numeric_type, 1);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_rvalue_from_double method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_rvalue_from_double (gcc_jit_context *ctxt,
|
|
gcc_jit_type *numeric_type,
|
|
double value)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL_NONNULL_NUMERIC_TYPE (ctxt, numeric_type);
|
|
|
|
return ((gcc_jit_rvalue *)ctxt
|
|
->new_rvalue_from_const <double> (numeric_type, value));
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_rvalue_from_ptr method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_rvalue_from_ptr (gcc_jit_context *ctxt,
|
|
gcc_jit_type *pointer_type,
|
|
void *value)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL (pointer_type, ctxt, NULL, "NULL type");
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
pointer_type->is_pointer (),
|
|
ctxt, NULL,
|
|
"not a pointer type (type: %s)",
|
|
pointer_type->get_debug_string ());
|
|
|
|
return ((gcc_jit_rvalue *)ctxt
|
|
->new_rvalue_from_const <void *> (pointer_type, value));
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
This is essentially equivalent to:
|
|
gcc_jit_context_new_rvalue_from_ptr (ctxt, pointer_type, NULL);
|
|
albeit with slightly different error messages if an error occurs. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_null (gcc_jit_context *ctxt,
|
|
gcc_jit_type *pointer_type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL (pointer_type, ctxt, NULL, "NULL type");
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
pointer_type->is_pointer (),
|
|
ctxt, NULL,
|
|
"not a pointer type (type: %s)",
|
|
pointer_type->get_debug_string ());
|
|
|
|
return gcc_jit_context_new_rvalue_from_ptr (ctxt, pointer_type, NULL);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_string_literal method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_string_literal (gcc_jit_context *ctxt,
|
|
const char *value)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL (value, ctxt, NULL, "NULL value");
|
|
|
|
return (gcc_jit_rvalue *)ctxt->new_string_literal (value);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_unary_op method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_unary_op (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
enum gcc_jit_unary_op op,
|
|
gcc_jit_type *result_type,
|
|
gcc_jit_rvalue *rvalue)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
(op >= GCC_JIT_UNARY_OP_MINUS
|
|
&& op <= GCC_JIT_UNARY_OP_ABS),
|
|
ctxt, loc,
|
|
"unrecognized value for enum gcc_jit_unary_op: %i",
|
|
op);
|
|
RETURN_NULL_IF_FAIL (result_type, ctxt, loc, "NULL result_type");
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (
|
|
result_type->is_numeric (), ctxt, loc,
|
|
"gcc_jit_unary_op %s with operand %s "
|
|
"has non-numeric result_type: %s",
|
|
gcc::jit::unary_op_reproducer_strings[op],
|
|
rvalue->get_debug_string (),
|
|
result_type->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
|
|
|
|
return (gcc_jit_rvalue *)ctxt->new_unary_op (loc, op, result_type, rvalue);
|
|
}
|
|
|
|
/* Determine if OP is a valid value for enum gcc_jit_binary_op.
|
|
For use by both gcc_jit_context_new_binary_op and
|
|
gcc_jit_block_add_assignment_op. */
|
|
|
|
static bool
|
|
valid_binary_op_p (enum gcc_jit_binary_op op)
|
|
{
|
|
return (op >= GCC_JIT_BINARY_OP_PLUS
|
|
&& op <= GCC_JIT_BINARY_OP_RSHIFT);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_binary_op method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_binary_op (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
enum gcc_jit_binary_op op,
|
|
gcc_jit_type *result_type,
|
|
gcc_jit_rvalue *a, gcc_jit_rvalue *b)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
valid_binary_op_p (op),
|
|
ctxt, loc,
|
|
"unrecognized value for enum gcc_jit_binary_op: %i",
|
|
op);
|
|
RETURN_NULL_IF_FAIL (result_type, ctxt, loc, "NULL result_type");
|
|
RETURN_NULL_IF_FAIL (a, ctxt, loc, "NULL a");
|
|
RETURN_NULL_IF_FAIL (b, ctxt, loc, "NULL b");
|
|
RETURN_NULL_IF_FAIL_PRINTF4 (
|
|
a->get_type ()->unqualified () == b->get_type ()->unqualified (),
|
|
ctxt, loc,
|
|
"mismatching types for binary op:"
|
|
" a: %s (type: %s) b: %s (type: %s)",
|
|
a->get_debug_string (),
|
|
a->get_type ()->get_debug_string (),
|
|
b->get_debug_string (),
|
|
b->get_type ()->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF4 (
|
|
result_type->is_numeric (), ctxt, loc,
|
|
"gcc_jit_binary_op %s with operands a: %s b: %s "
|
|
"has non-numeric result_type: %s",
|
|
gcc::jit::binary_op_reproducer_strings[op],
|
|
a->get_debug_string (), b->get_debug_string (),
|
|
result_type->get_debug_string ());
|
|
|
|
return (gcc_jit_rvalue *)ctxt->new_binary_op (loc, op, result_type, a, b);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_comparison method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_comparison (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
enum gcc_jit_comparison op,
|
|
gcc_jit_rvalue *a, gcc_jit_rvalue *b)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
(op >= GCC_JIT_COMPARISON_EQ
|
|
&& op <= GCC_JIT_COMPARISON_GE),
|
|
ctxt, loc,
|
|
"unrecognized value for enum gcc_jit_comparison: %i",
|
|
op);
|
|
RETURN_NULL_IF_FAIL (a, ctxt, loc, "NULL a");
|
|
RETURN_NULL_IF_FAIL (b, ctxt, loc, "NULL b");
|
|
RETURN_NULL_IF_FAIL_PRINTF4 (
|
|
a->get_type ()->unqualified () == b->get_type ()->unqualified (),
|
|
ctxt, loc,
|
|
"mismatching types for comparison:"
|
|
" a: %s (type: %s) b: %s (type: %s)",
|
|
a->get_debug_string (),
|
|
a->get_type ()->get_debug_string (),
|
|
b->get_debug_string (),
|
|
b->get_type ()->get_debug_string ());
|
|
|
|
return (gcc_jit_rvalue *)ctxt->new_comparison (loc, op, a, b);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_call method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_call (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_function *func,
|
|
int numargs , gcc_jit_rvalue **args)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (func, ctxt, loc, "NULL function");
|
|
if (numargs)
|
|
RETURN_NULL_IF_FAIL (args, ctxt, loc, "NULL args");
|
|
|
|
int min_num_params = func->get_params ().length ();
|
|
bool is_variadic = func->is_variadic ();
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (
|
|
numargs >= min_num_params,
|
|
ctxt, loc,
|
|
"not enough arguments to function \"%s\""
|
|
" (got %i args, expected %i)",
|
|
func->get_name ()->c_str (),
|
|
numargs, min_num_params);
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (
|
|
(numargs == min_num_params || is_variadic),
|
|
ctxt, loc,
|
|
"too many arguments to function \"%s\""
|
|
" (got %i args, expected %i)",
|
|
func->get_name ()->c_str (),
|
|
numargs, min_num_params);
|
|
|
|
for (int i = 0; i < min_num_params; i++)
|
|
{
|
|
gcc::jit::recording::param *param = func->get_param (i);
|
|
gcc_jit_rvalue *arg = args[i];
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF4 (
|
|
arg,
|
|
ctxt, loc,
|
|
"NULL argument %i to function \"%s\":"
|
|
" param %s (type: %s)",
|
|
i + 1,
|
|
func->get_name ()->c_str (),
|
|
param->get_debug_string (),
|
|
param->get_type ()->get_debug_string ());
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF6 (
|
|
compatible_types (param->get_type (),
|
|
arg->get_type ()),
|
|
ctxt, loc,
|
|
"mismatching types for argument %d of function \"%s\":"
|
|
" assignment to param %s (type: %s) from %s (type: %s)",
|
|
i + 1,
|
|
func->get_name ()->c_str (),
|
|
param->get_debug_string (),
|
|
param->get_type ()->get_debug_string (),
|
|
arg->get_debug_string (),
|
|
arg->get_type ()->get_debug_string ());
|
|
}
|
|
|
|
return (gcc_jit_rvalue *)ctxt->new_call (loc,
|
|
func,
|
|
numargs,
|
|
(gcc::jit::recording::rvalue **)args);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_call_through_ptr method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_call_through_ptr (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_rvalue *fn_ptr,
|
|
int numargs, gcc_jit_rvalue **args)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (fn_ptr, ctxt, loc, "NULL fn_ptr");
|
|
if (numargs)
|
|
RETURN_NULL_IF_FAIL (args, ctxt, loc, "NULL args");
|
|
|
|
gcc::jit::recording::type *ptr_type = fn_ptr->get_type ()->dereference ();
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
ptr_type, ctxt, loc,
|
|
"fn_ptr is not a ptr: %s"
|
|
" type: %s",
|
|
fn_ptr->get_debug_string (),
|
|
fn_ptr->get_type ()->get_debug_string ());
|
|
|
|
gcc::jit::recording::function_type *fn_type =
|
|
ptr_type->dyn_cast_function_type();
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
fn_type, ctxt, loc,
|
|
"fn_ptr is not a function ptr: %s"
|
|
" type: %s",
|
|
fn_ptr->get_debug_string (),
|
|
fn_ptr->get_type ()->get_debug_string ());
|
|
|
|
int min_num_params = fn_type->get_param_types ().length ();
|
|
bool is_variadic = fn_type->is_variadic ();
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (
|
|
numargs >= min_num_params,
|
|
ctxt, loc,
|
|
"not enough arguments to fn_ptr: %s"
|
|
" (got %i args, expected %i)",
|
|
fn_ptr->get_debug_string (),
|
|
numargs, min_num_params);
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (
|
|
(numargs == min_num_params || is_variadic),
|
|
ctxt, loc,
|
|
"too many arguments to fn_ptr: %s"
|
|
" (got %i args, expected %i)",
|
|
fn_ptr->get_debug_string (),
|
|
numargs, min_num_params);
|
|
|
|
for (int i = 0; i < min_num_params; i++)
|
|
{
|
|
gcc::jit::recording::type *param_type = fn_type->get_param_types ()[i];
|
|
gcc_jit_rvalue *arg = args[i];
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (
|
|
arg,
|
|
ctxt, loc,
|
|
"NULL argument %i to fn_ptr: %s"
|
|
" (type: %s)",
|
|
i + 1,
|
|
fn_ptr->get_debug_string (),
|
|
param_type->get_debug_string ());
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF6 (
|
|
compatible_types (param_type,
|
|
arg->get_type ()),
|
|
ctxt, loc,
|
|
"mismatching types for argument %d of fn_ptr: %s:"
|
|
" assignment to param %d (type: %s) from %s (type: %s)",
|
|
i + 1,
|
|
fn_ptr->get_debug_string (),
|
|
i + 1,
|
|
param_type->get_debug_string (),
|
|
arg->get_debug_string (),
|
|
arg->get_type ()->get_debug_string ());
|
|
}
|
|
|
|
return (gcc_jit_rvalue *)(
|
|
ctxt->new_call_through_ptr (loc,
|
|
fn_ptr,
|
|
numargs,
|
|
(gcc::jit::recording::rvalue **)args));
|
|
}
|
|
|
|
/* Helper function for determining if we can cast an rvalue from SRC_TYPE
|
|
to DST_TYPE, for use by gcc_jit_context_new_cast.
|
|
|
|
We only permit these kinds of cast:
|
|
|
|
int <-> float
|
|
int <-> bool
|
|
P* <-> Q* for pointer types P and Q. */
|
|
|
|
static bool
|
|
is_valid_cast (gcc::jit::recording::type *src_type,
|
|
gcc_jit_type *dst_type)
|
|
{
|
|
bool src_is_int = src_type->is_int ();
|
|
bool dst_is_int = dst_type->is_int ();
|
|
bool src_is_float = src_type->is_float ();
|
|
bool dst_is_float = dst_type->is_float ();
|
|
bool src_is_bool = src_type->is_bool ();
|
|
bool dst_is_bool = dst_type->is_bool ();
|
|
|
|
if (src_is_int)
|
|
if (dst_is_int || dst_is_float || dst_is_bool)
|
|
return true;
|
|
|
|
if (src_is_float)
|
|
if (dst_is_int || dst_is_float)
|
|
return true;
|
|
|
|
if (src_is_bool)
|
|
if (dst_is_int || dst_is_bool)
|
|
return true;
|
|
|
|
/* Permit casts between pointer types. */
|
|
gcc::jit::recording::type *deref_src_type = src_type->is_pointer ();
|
|
gcc::jit::recording::type *deref_dst_type = dst_type->is_pointer ();
|
|
if (deref_src_type && deref_dst_type)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_cast method in jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_context_new_cast (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_rvalue *rvalue,
|
|
gcc_jit_type *type)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
|
|
RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (
|
|
is_valid_cast (rvalue->get_type (), type),
|
|
ctxt, loc,
|
|
"cannot cast %s from type: %s to type: %s",
|
|
rvalue->get_debug_string (),
|
|
rvalue->get_type ()->get_debug_string (),
|
|
type->get_debug_string ());
|
|
|
|
return static_cast <gcc_jit_rvalue *> (ctxt->new_cast (loc, rvalue, type));
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_array_access method in
|
|
jit-recording.c. */
|
|
|
|
extern gcc_jit_lvalue *
|
|
gcc_jit_context_new_array_access (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_rvalue *ptr,
|
|
gcc_jit_rvalue *index)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (ptr, ctxt, loc, "NULL ptr");
|
|
RETURN_NULL_IF_FAIL (index, ctxt, loc, "NULL index");
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
ptr->get_type ()->dereference (),
|
|
ctxt, loc,
|
|
"ptr: %s (type: %s) is not a pointer or array",
|
|
ptr->get_debug_string (),
|
|
ptr->get_type ()->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
index->get_type ()->is_numeric (),
|
|
ctxt, loc,
|
|
"index: %s (type: %s) is not of numeric type",
|
|
index->get_debug_string (),
|
|
index->get_type ()->get_debug_string ());
|
|
|
|
return (gcc_jit_lvalue *)ctxt->new_array_access (loc, ptr, index);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::memento::get_context method in
|
|
jit-recording.h. */
|
|
|
|
gcc_jit_context *
|
|
gcc_jit_object_get_context (gcc_jit_object *obj)
|
|
{
|
|
RETURN_NULL_IF_FAIL (obj, NULL, NULL, "NULL object");
|
|
|
|
return static_cast <gcc_jit_context *> (obj->get_context ());
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::memento::get_debug_string method in
|
|
jit-recording.c. */
|
|
|
|
const char *
|
|
gcc_jit_object_get_debug_string (gcc_jit_object *obj)
|
|
{
|
|
RETURN_NULL_IF_FAIL (obj, NULL, NULL, "NULL object");
|
|
|
|
return obj->get_debug_string ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::lvalue::access_field method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_lvalue *
|
|
gcc_jit_lvalue_access_field (gcc_jit_lvalue *struct_,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_field *field)
|
|
{
|
|
RETURN_NULL_IF_FAIL (struct_, NULL, loc, "NULL struct");
|
|
gcc::jit::recording::context *ctxt = struct_->m_ctxt;
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (field, ctxt, loc, "NULL field");
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (field->get_container (), field->m_ctxt, loc,
|
|
"field %s has not been placed in a struct",
|
|
field->get_debug_string ());
|
|
gcc::jit::recording::type *underlying_type =
|
|
struct_->get_type ();
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
(field->get_container ()->unqualified ()
|
|
== underlying_type->unqualified ()),
|
|
struct_->m_ctxt, loc,
|
|
"%s is not a field of %s",
|
|
field->get_debug_string (),
|
|
underlying_type->get_debug_string ());
|
|
|
|
return (gcc_jit_lvalue *)struct_->access_field (loc, field);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::rvalue::access_field method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_rvalue_access_field (gcc_jit_rvalue *struct_,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_field *field)
|
|
{
|
|
RETURN_NULL_IF_FAIL (struct_, NULL, loc, "NULL struct");
|
|
gcc::jit::recording::context *ctxt = struct_->m_ctxt;
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (field, ctxt, loc, "NULL field");
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (field->get_container (), field->m_ctxt, loc,
|
|
"field %s has not been placed in a struct",
|
|
field->get_debug_string ());
|
|
gcc::jit::recording::type *underlying_type =
|
|
struct_->get_type ();
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
(field->get_container ()->unqualified ()
|
|
== underlying_type->unqualified ()),
|
|
struct_->m_ctxt, loc,
|
|
"%s is not a field of %s",
|
|
field->get_debug_string (),
|
|
underlying_type->get_debug_string ());
|
|
|
|
return (gcc_jit_rvalue *)struct_->access_field (loc, field);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::rvalue::deference_field method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_lvalue *
|
|
gcc_jit_rvalue_dereference_field (gcc_jit_rvalue *ptr,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_field *field)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ptr, NULL, loc, "NULL ptr");
|
|
JIT_LOG_FUNC (ptr->get_context ()->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (field, NULL, loc, "NULL field");
|
|
gcc::jit::recording::type *underlying_type =
|
|
ptr->get_type ()->is_pointer ();
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (field->get_container (), field->m_ctxt, loc,
|
|
"field %s has not been placed in a struct",
|
|
field->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF3 (
|
|
underlying_type,
|
|
ptr->m_ctxt, loc,
|
|
"dereference of non-pointer %s (type: %s) when accessing ->%s",
|
|
ptr->get_debug_string (),
|
|
ptr->get_type ()->get_debug_string (),
|
|
field->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
(field->get_container ()->unqualified ()
|
|
== underlying_type->unqualified ()),
|
|
ptr->m_ctxt, loc,
|
|
"%s is not a field of %s",
|
|
field->get_debug_string (),
|
|
underlying_type->get_debug_string ());
|
|
|
|
return (gcc_jit_lvalue *)ptr->dereference_field (loc, field);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::rvalue::deference method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_lvalue *
|
|
gcc_jit_rvalue_dereference (gcc_jit_rvalue *rvalue,
|
|
gcc_jit_location *loc)
|
|
{
|
|
RETURN_NULL_IF_FAIL (rvalue, NULL, loc, "NULL rvalue");
|
|
JIT_LOG_FUNC (rvalue->get_context ()->get_logger ());
|
|
/* LOC can be NULL. */
|
|
|
|
gcc::jit::recording::type *underlying_type =
|
|
rvalue->get_type ()->is_pointer ();
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
underlying_type,
|
|
rvalue->m_ctxt, loc,
|
|
"dereference of non-pointer %s (type: %s)",
|
|
rvalue->get_debug_string (),
|
|
rvalue->get_type ()->get_debug_string ());
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
!underlying_type->is_void (),
|
|
rvalue->m_ctxt, loc,
|
|
"dereference of void pointer %s (type: %s)",
|
|
rvalue->get_debug_string (),
|
|
rvalue->get_type ()->get_debug_string ());
|
|
|
|
return (gcc_jit_lvalue *)rvalue->dereference (loc);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::lvalue::get_address method in jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_lvalue_get_address (gcc_jit_lvalue *lvalue,
|
|
gcc_jit_location *loc)
|
|
{
|
|
RETURN_NULL_IF_FAIL (lvalue, NULL, loc, "NULL lvalue");
|
|
JIT_LOG_FUNC (lvalue->get_context ()->get_logger ());
|
|
/* LOC can be NULL. */
|
|
|
|
return (gcc_jit_rvalue *)lvalue->get_address (loc);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::function::new_local method in jit-recording.c. */
|
|
|
|
gcc_jit_lvalue *
|
|
gcc_jit_function_new_local (gcc_jit_function *func,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_type *type,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (func, NULL, loc, "NULL function");
|
|
gcc::jit::recording::context *ctxt = func->m_ctxt;
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (func->get_kind () != GCC_JIT_FUNCTION_IMPORTED,
|
|
ctxt, loc,
|
|
"Cannot add locals to an imported function");
|
|
RETURN_NULL_IF_FAIL (type, ctxt, loc, "NULL type");
|
|
RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name");
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
type->has_known_size (),
|
|
ctxt, loc,
|
|
"unknown size for local \"%s\" (type: %s)",
|
|
name,
|
|
type->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
!type->is_void (),
|
|
ctxt, loc,
|
|
"void type for local \"%s\"",
|
|
name);
|
|
|
|
return (gcc_jit_lvalue *)func->new_local (loc, type, name);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::add_eval method in jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_add_eval (gcc_jit_block *block,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_rvalue *rvalue)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
|
|
|
|
gcc::jit::recording::statement *stmt = block->add_eval (loc, rvalue);
|
|
|
|
/* "stmt" should be good enough to be usable in error-messages,
|
|
but might still not be compilable; perform some more
|
|
error-checking here. We do this here so that the error messages
|
|
can contain a stringified version of "stmt", whilst appearing
|
|
as close as possible to the point of failure. */
|
|
rvalue->verify_valid_within_stmt (__func__, stmt);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::add_assignment method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_add_assignment (gcc_jit_block *block,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_lvalue *lvalue,
|
|
gcc_jit_rvalue *rvalue)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_IF_FAIL (lvalue, ctxt, loc, "NULL lvalue");
|
|
RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
|
|
RETURN_IF_FAIL_PRINTF4 (
|
|
compatible_types (lvalue->get_type (),
|
|
rvalue->get_type ()),
|
|
ctxt, loc,
|
|
"mismatching types:"
|
|
" assignment to %s (type: %s) from %s (type: %s)",
|
|
lvalue->get_debug_string (),
|
|
lvalue->get_type ()->get_debug_string (),
|
|
rvalue->get_debug_string (),
|
|
rvalue->get_type ()->get_debug_string ());
|
|
|
|
gcc::jit::recording::statement *stmt = block->add_assignment (loc, lvalue, rvalue);
|
|
|
|
/* "stmt" should be good enough to be usable in error-messages,
|
|
but might still not be compilable; perform some more
|
|
error-checking here. We do this here so that the error messages
|
|
can contain a stringified version of "stmt", whilst appearing
|
|
as close as possible to the point of failure. */
|
|
lvalue->verify_valid_within_stmt (__func__, stmt);
|
|
rvalue->verify_valid_within_stmt (__func__, stmt);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::add_assignment_op method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_add_assignment_op (gcc_jit_block *block,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_lvalue *lvalue,
|
|
enum gcc_jit_binary_op op,
|
|
gcc_jit_rvalue *rvalue)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_IF_FAIL (lvalue, ctxt, loc, "NULL lvalue");
|
|
RETURN_IF_FAIL_PRINTF1 (
|
|
valid_binary_op_p (op),
|
|
ctxt, loc,
|
|
"unrecognized value for enum gcc_jit_binary_op: %i",
|
|
op);
|
|
RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
|
|
RETURN_IF_FAIL_PRINTF4 (
|
|
compatible_types (lvalue->get_type (),
|
|
rvalue->get_type ()),
|
|
ctxt, loc,
|
|
"mismatching types:"
|
|
" assignment to %s (type: %s) involving %s (type: %s)",
|
|
lvalue->get_debug_string (),
|
|
lvalue->get_type ()->get_debug_string (),
|
|
rvalue->get_debug_string (),
|
|
rvalue->get_type ()->get_debug_string ());
|
|
|
|
gcc::jit::recording::statement *stmt = block->add_assignment_op (loc, lvalue, op, rvalue);
|
|
|
|
/* "stmt" should be good enough to be usable in error-messages,
|
|
but might still not be compilable; perform some more
|
|
error-checking here. We do this here so that the error messages
|
|
can contain a stringified version of "stmt", whilst appearing
|
|
as close as possible to the point of failure. */
|
|
lvalue->verify_valid_within_stmt (__func__, stmt);
|
|
rvalue->verify_valid_within_stmt (__func__, stmt);
|
|
}
|
|
|
|
/* Internal helper function for determining if rvalue BOOLVAL is of
|
|
boolean type. For use by gcc_jit_block_end_with_conditional. */
|
|
|
|
static bool
|
|
is_bool (gcc_jit_rvalue *boolval)
|
|
{
|
|
gcc::jit::recording::type *actual_type = boolval->get_type ();
|
|
gcc::jit::recording::type *bool_type =
|
|
boolval->m_ctxt->get_type (GCC_JIT_TYPE_BOOL);
|
|
return actual_type == bool_type;
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::end_with_conditional method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_end_with_conditional (gcc_jit_block *block,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_rvalue *boolval,
|
|
gcc_jit_block *on_true,
|
|
gcc_jit_block *on_false)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_IF_FAIL (boolval, ctxt, loc, "NULL boolval");
|
|
RETURN_IF_FAIL_PRINTF2 (
|
|
is_bool (boolval), ctxt, loc,
|
|
"%s (type: %s) is not of boolean type ",
|
|
boolval->get_debug_string (),
|
|
boolval->get_type ()->get_debug_string ());
|
|
RETURN_IF_FAIL (on_true, ctxt, loc, "NULL on_true");
|
|
RETURN_IF_FAIL (on_true, ctxt, loc, "NULL on_false");
|
|
RETURN_IF_FAIL_PRINTF4 (
|
|
block->get_function () == on_true->get_function (),
|
|
ctxt, loc,
|
|
"\"on_true\" block is not in same function:"
|
|
" source block %s is in function %s"
|
|
" whereas target block %s is in function %s",
|
|
block->get_debug_string (),
|
|
block->get_function ()->get_debug_string (),
|
|
on_true->get_debug_string (),
|
|
on_true->get_function ()->get_debug_string ());
|
|
RETURN_IF_FAIL_PRINTF4 (
|
|
block->get_function () == on_false->get_function (),
|
|
ctxt, loc,
|
|
"\"on_false\" block is not in same function:"
|
|
" source block %s is in function %s"
|
|
" whereas target block %s is in function %s",
|
|
block->get_debug_string (),
|
|
block->get_function ()->get_debug_string (),
|
|
on_false->get_debug_string (),
|
|
on_false->get_function ()->get_debug_string ());
|
|
|
|
gcc::jit::recording::statement *stmt = block->end_with_conditional (loc, boolval, on_true, on_false);
|
|
|
|
/* "stmt" should be good enough to be usable in error-messages,
|
|
but might still not be compilable; perform some more
|
|
error-checking here. We do this here so that the error messages
|
|
can contain a stringified version of "stmt", whilst appearing
|
|
as close as possible to the point of failure. */
|
|
boolval->verify_valid_within_stmt (__func__, stmt);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::add_comment method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_add_comment (gcc_jit_block *block,
|
|
gcc_jit_location *loc,
|
|
const char *text)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_IF_FAIL (text, ctxt, loc, "NULL text");
|
|
|
|
block->add_comment (loc, text);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::end_with_jump method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_end_with_jump (gcc_jit_block *block,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_block *target)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_IF_FAIL (target, ctxt, loc, "NULL target");
|
|
RETURN_IF_FAIL_PRINTF4 (
|
|
block->get_function () == target->get_function (),
|
|
ctxt, loc,
|
|
"target block is not in same function:"
|
|
" source block %s is in function %s"
|
|
" whereas target block %s is in function %s",
|
|
block->get_debug_string (),
|
|
block->get_function ()->get_debug_string (),
|
|
target->get_debug_string (),
|
|
target->get_function ()->get_debug_string ());
|
|
|
|
block->end_with_jump (loc, target);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::end_with_return method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_end_with_return (gcc_jit_block *block,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_rvalue *rvalue)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
gcc::jit::recording::function *func = block->get_function ();
|
|
RETURN_IF_FAIL (rvalue, ctxt, loc, "NULL rvalue");
|
|
RETURN_IF_FAIL_PRINTF4 (
|
|
compatible_types (
|
|
func->get_return_type (),
|
|
rvalue->get_type ()),
|
|
ctxt, loc,
|
|
"mismatching types:"
|
|
" return of %s (type: %s) in function %s (return type: %s)",
|
|
rvalue->get_debug_string (),
|
|
rvalue->get_type ()->get_debug_string (),
|
|
func->get_debug_string (),
|
|
func->get_return_type ()->get_debug_string ());
|
|
|
|
gcc::jit::recording::statement *stmt = block->end_with_return (loc, rvalue);
|
|
|
|
/* "stmt" should be good enough to be usable in error-messages,
|
|
but might still not be compilable; perform some more
|
|
error-checking here. We do this here so that the error messages
|
|
can contain a stringified version of "stmt", whilst appearing
|
|
as close as possible to the point of failure. */
|
|
rvalue->verify_valid_within_stmt (__func__, stmt);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::end_with_return method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_end_with_void_return (gcc_jit_block *block,
|
|
gcc_jit_location *loc)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
gcc::jit::recording::function *func = block->get_function ();
|
|
RETURN_IF_FAIL_PRINTF2 (
|
|
func->get_return_type () == ctxt->get_type (GCC_JIT_TYPE_VOID),
|
|
ctxt, loc,
|
|
"mismatching types:"
|
|
" void return in function %s (return type: %s)",
|
|
func->get_debug_string (),
|
|
func->get_return_type ()->get_debug_string ());
|
|
|
|
block->end_with_return (loc, NULL);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_case method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_case *
|
|
gcc_jit_context_new_case (gcc_jit_context *ctxt,
|
|
gcc_jit_rvalue *min_value,
|
|
gcc_jit_rvalue *max_value,
|
|
gcc_jit_block *block)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_NULL_IF_FAIL (min_value, ctxt, NULL, "NULL min_value");
|
|
RETURN_NULL_IF_FAIL (max_value, ctxt, NULL, "NULL max_value");
|
|
RETURN_NULL_IF_FAIL (block, ctxt, NULL, "NULL block");
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (min_value->is_constant (), ctxt, NULL,
|
|
"min_value is not a constant: %s",
|
|
min_value->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (max_value->is_constant (), ctxt, NULL,
|
|
"max_value is not a constant: %s",
|
|
max_value->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
min_value->get_type ()->is_int (),
|
|
ctxt, NULL,
|
|
"min_value: %s (type: %s) is not of integer type",
|
|
min_value->get_debug_string (),
|
|
min_value->get_type ()->get_debug_string ());
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
max_value->get_type ()->is_int (),
|
|
ctxt, NULL,
|
|
"max_value: %s (type: %s) is not of integer type",
|
|
max_value->get_debug_string (),
|
|
max_value->get_type ()->get_debug_string ());
|
|
|
|
wide_int wi_min, wi_max;
|
|
if (!min_value->get_wide_int (&wi_min))
|
|
gcc_unreachable ();
|
|
if (!max_value->get_wide_int (&wi_max))
|
|
gcc_unreachable ();
|
|
RETURN_NULL_IF_FAIL_PRINTF2 (
|
|
wi::les_p (wi_min, wi_max),
|
|
ctxt, NULL,
|
|
"min_value: %s > max_value: %s",
|
|
min_value->get_debug_string (),
|
|
max_value->get_debug_string ());
|
|
return (gcc_jit_case *)ctxt->new_case (min_value,
|
|
max_value,
|
|
block);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this calls the trivial
|
|
gcc::jit::recording::memento::as_object method (a case is a
|
|
memento), in jit-recording.h. */
|
|
|
|
gcc_jit_object *
|
|
gcc_jit_case_as_object (gcc_jit_case *case_)
|
|
{
|
|
RETURN_NULL_IF_FAIL (case_, NULL, NULL, "NULL case");
|
|
|
|
return static_cast <gcc_jit_object *> (case_->as_object ());
|
|
}
|
|
|
|
/* Helper function for gcc_jit_block_end_with_switch and
|
|
valid_case_for_switch. */
|
|
|
|
static bool
|
|
valid_dest_for_switch (gcc::jit::recording::context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *api_funcname,
|
|
gcc::jit::recording::block *switch_block,
|
|
gcc::jit::recording::block *dest_block,
|
|
const char *dest_block_desc)
|
|
{
|
|
if (!dest_block)
|
|
{
|
|
jit_error (ctxt, loc, "%s: NULL %s", api_funcname, dest_block_desc);
|
|
return false;
|
|
}
|
|
gcc::jit::recording::function *switch_fn = switch_block->get_function ();
|
|
gcc::jit::recording::function *dest_fn = dest_block->get_function ();
|
|
if (switch_fn != dest_fn)
|
|
{
|
|
jit_error (ctxt, loc,
|
|
"%s: %s is not in same function:"
|
|
" switch block %s is in function %s"
|
|
" whereas %s %s is in function %s",
|
|
api_funcname,
|
|
dest_block_desc,
|
|
switch_block->get_debug_string (),
|
|
switch_fn->get_debug_string (),
|
|
dest_block_desc,
|
|
dest_block->get_debug_string (),
|
|
dest_fn->get_debug_string ());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Helper function for gcc_jit_block_end_with_switch. */
|
|
|
|
static bool
|
|
valid_case_for_switch (gcc::jit::recording::context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *api_funcname,
|
|
gcc_jit_block *switch_block,
|
|
gcc_jit_rvalue *expr,
|
|
gcc_jit_case *case_,
|
|
const char *case_desc,
|
|
int case_idx)
|
|
{
|
|
if (!case_)
|
|
{
|
|
jit_error (ctxt, loc,
|
|
"%s:"
|
|
" NULL case %i",
|
|
api_funcname,
|
|
case_idx);
|
|
return false;
|
|
}
|
|
if (!valid_dest_for_switch (ctxt, loc,
|
|
api_funcname,
|
|
switch_block,
|
|
case_->get_dest_block (),
|
|
case_desc))
|
|
return false;
|
|
gcc::jit::recording::type *expr_type = expr->get_type ();
|
|
if (expr_type != case_->get_min_value ()->get_type ())
|
|
{
|
|
jit_error (ctxt, loc,
|
|
"%s:"
|
|
" mismatching types between case and expression:"
|
|
" cases[%i]->min_value: %s (type: %s)"
|
|
" expr: %s (type: %s)",
|
|
api_funcname,
|
|
case_idx,
|
|
case_->get_min_value ()->get_debug_string (),
|
|
case_->get_min_value ()->get_type ()->get_debug_string (),
|
|
expr->get_debug_string (),
|
|
expr_type->get_debug_string ());
|
|
return false;
|
|
}
|
|
if (expr_type != case_->get_max_value ()->get_type ())
|
|
{
|
|
jit_error (ctxt, loc,
|
|
"%s:"
|
|
" mismatching types between case and expression:"
|
|
" cases[%i]->max_value: %s (type: %s)"
|
|
" expr: %s (type: %s)",
|
|
api_funcname,
|
|
case_idx,
|
|
case_->get_max_value ()->get_debug_string (),
|
|
case_->get_max_value ()->get_type ()->get_debug_string (),
|
|
expr->get_debug_string (),
|
|
expr_type->get_debug_string ());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* A class for holding the data we need to perform error-checking
|
|
on a libgccjit API call. */
|
|
|
|
class api_call_validator
|
|
{
|
|
public:
|
|
api_call_validator (gcc::jit::recording::context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *funcname)
|
|
: m_ctxt (ctxt),
|
|
m_loc (loc),
|
|
m_funcname (funcname)
|
|
{}
|
|
|
|
protected:
|
|
gcc::jit::recording::context *m_ctxt;
|
|
gcc_jit_location *m_loc;
|
|
const char *m_funcname;
|
|
};
|
|
|
|
/* A class for verifying that the ranges of cases within
|
|
gcc_jit_block_end_with_switch don't overlap. */
|
|
|
|
class case_range_validator : public api_call_validator
|
|
{
|
|
public:
|
|
case_range_validator (gcc::jit::recording::context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *funcname);
|
|
|
|
bool
|
|
validate (gcc_jit_case *case_, int idx);
|
|
|
|
private:
|
|
static int
|
|
case_compare (gcc::jit::recording::rvalue *k1,
|
|
gcc::jit::recording::rvalue *k2);
|
|
|
|
static wide_int
|
|
get_wide_int (gcc::jit::recording::rvalue *k);
|
|
|
|
private:
|
|
typed_splay_tree <gcc::jit::recording::rvalue *, gcc_jit_case *> m_cases;
|
|
};
|
|
|
|
/* case_range_validator's ctor. */
|
|
|
|
case_range_validator::case_range_validator (gcc::jit::recording::context *ctxt,
|
|
gcc_jit_location *loc,
|
|
const char *funcname)
|
|
: api_call_validator (ctxt, loc, funcname),
|
|
m_cases (case_compare, NULL, NULL)
|
|
{
|
|
}
|
|
|
|
/* Ensure that the range of CASE_ does not overlap with any of the
|
|
ranges of cases we've already seen.
|
|
Return true if everything is OK.
|
|
Return false and emit an error if there is an overlap.
|
|
Compare with c-family/c-common.c:c_add_case_label. */
|
|
|
|
bool
|
|
case_range_validator::validate (gcc_jit_case *case_,
|
|
int case_idx)
|
|
{
|
|
/* Look up the LOW_VALUE in the table of case labels we already
|
|
have. */
|
|
gcc_jit_case *other = m_cases.lookup (case_->get_min_value ());
|
|
|
|
/* If there was not an exact match, check for overlapping ranges. */
|
|
if (!other)
|
|
{
|
|
gcc_jit_case *pred;
|
|
gcc_jit_case *succ;
|
|
|
|
/* Even though there wasn't an exact match, there might be an
|
|
overlap between this case range and another case range.
|
|
Since we've (inductively) not allowed any overlapping case
|
|
ranges, we simply need to find the greatest low case label
|
|
that is smaller that CASE_MIN_VALUE, and the smallest low case
|
|
label that is greater than CASE_MAX_VALUE. If there is an overlap
|
|
it will occur in one of these two ranges. */
|
|
pred = m_cases.predecessor (case_->get_min_value ());
|
|
succ = m_cases.successor (case_->get_max_value ());
|
|
|
|
/* Check to see if the PRED overlaps. It is smaller than
|
|
the LOW_VALUE, so we only need to check its max value. */
|
|
if (pred)
|
|
{
|
|
wide_int wi_case_min = get_wide_int (case_->get_min_value ());
|
|
wide_int wi_pred_max = get_wide_int (pred->get_max_value ());
|
|
if (wi::ges_p (wi_pred_max, wi_case_min))
|
|
other = pred;
|
|
}
|
|
|
|
if (!other && succ)
|
|
{
|
|
/* Check to see if the SUCC overlaps. The low end of that
|
|
range is bigger than the low end of the current range. */
|
|
wide_int wi_case_max = get_wide_int (case_->get_max_value ());
|
|
wide_int wi_succ_min = get_wide_int (succ->get_min_value ());
|
|
if (wi::les_p (wi_succ_min, wi_case_max))
|
|
other = succ;
|
|
}
|
|
}
|
|
|
|
/* If there was an overlap, issue an error. */
|
|
if (other)
|
|
{
|
|
jit_error (m_ctxt, m_loc,
|
|
"%s: duplicate (or overlapping) cases values:"
|
|
" case %i: %s overlaps %s",
|
|
m_funcname,
|
|
case_idx,
|
|
case_->get_debug_string (),
|
|
other->get_debug_string ());
|
|
return false;
|
|
}
|
|
|
|
/* Register this case label in the splay tree. */
|
|
m_cases.insert (case_->get_min_value (),
|
|
case_);
|
|
return true;
|
|
}
|
|
|
|
/* Compare with c-family/c-common.c:case_compare, which acts on tree
|
|
nodes, rather than rvalue *.
|
|
|
|
Comparator for case label values. K1 and K2 must be constant integer
|
|
values (anything else should have been rejected by
|
|
gcc_jit_context_new_case.
|
|
|
|
Returns -1 if K1 is ordered before K2, -1 if K1 is ordered after
|
|
K2, and 0 if K1 and K2 are equal. */
|
|
|
|
int
|
|
case_range_validator::case_compare (gcc::jit::recording::rvalue * k1,
|
|
gcc::jit::recording::rvalue * k2)
|
|
{
|
|
wide_int wi1 = get_wide_int (k1);
|
|
wide_int wi2 = get_wide_int (k2);
|
|
return wi::cmps(wi1, wi2);
|
|
}
|
|
|
|
/* Given a const int rvalue K, get the underlying value as a wide_int. */
|
|
|
|
wide_int
|
|
case_range_validator::get_wide_int (gcc::jit::recording::rvalue *k)
|
|
{
|
|
wide_int wi;
|
|
bool got_wi = k->get_wide_int (&wi);
|
|
gcc_assert (got_wi);
|
|
return wi;
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::block::end_with_switch method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_block_end_with_switch (gcc_jit_block *block,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_rvalue *expr,
|
|
gcc_jit_block *default_block,
|
|
int num_cases,
|
|
gcc_jit_case **cases)
|
|
{
|
|
RETURN_IF_NOT_VALID_BLOCK (block, loc);
|
|
gcc::jit::recording::context *ctxt = block->get_context ();
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
RETURN_IF_FAIL (expr, ctxt, loc,
|
|
"NULL expr");
|
|
gcc::jit::recording::type *expr_type = expr->get_type ();
|
|
RETURN_IF_FAIL_PRINTF2 (
|
|
expr_type->is_int (),
|
|
ctxt, loc,
|
|
"expr: %s (type: %s) is not of integer type",
|
|
expr->get_debug_string (),
|
|
expr_type->get_debug_string ());
|
|
if (!valid_dest_for_switch (ctxt, loc,
|
|
__func__,
|
|
block,
|
|
default_block,
|
|
"default_block"))
|
|
return;
|
|
RETURN_IF_FAIL (num_cases >= 0, ctxt, loc, "num_cases < 0");
|
|
case_range_validator crv (ctxt, loc, __func__);
|
|
for (int i = 0; i < num_cases; i++)
|
|
{
|
|
char case_desc[32];
|
|
snprintf (case_desc, sizeof (case_desc),
|
|
"cases[%i]", i);
|
|
if (!valid_case_for_switch (ctxt, loc,
|
|
__func__,
|
|
block,
|
|
expr,
|
|
cases[i],
|
|
case_desc,
|
|
i))
|
|
return;
|
|
if (!crv.validate (cases[i], i))
|
|
return;
|
|
}
|
|
|
|
block->end_with_switch (loc, expr, default_block,
|
|
num_cases,
|
|
(gcc::jit::recording::case_ **)cases);
|
|
}
|
|
|
|
/**********************************************************************
|
|
Option-management
|
|
**********************************************************************/
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::set_str_option method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_set_str_option (gcc_jit_context *ctxt,
|
|
enum gcc_jit_str_option opt,
|
|
const char *value)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* opt is checked by the inner function.
|
|
value can be NULL. */
|
|
|
|
ctxt->set_str_option (opt, value);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::set_int_option method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_set_int_option (gcc_jit_context *ctxt,
|
|
enum gcc_jit_int_option opt,
|
|
int value)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* opt is checked by the inner function. */
|
|
|
|
ctxt->set_int_option (opt, value);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::set_bool_option method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_set_bool_option (gcc_jit_context *ctxt,
|
|
enum gcc_jit_bool_option opt,
|
|
int value)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* opt is checked by the inner function. */
|
|
|
|
ctxt->set_bool_option (opt, value);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::set_inner_bool_option method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_set_bool_allow_unreachable_blocks (gcc_jit_context *ctxt,
|
|
int bool_value)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
ctxt->set_inner_bool_option (
|
|
gcc::jit::INNER_BOOL_OPTION_ALLOW_UNREACHABLE_BLOCKS,
|
|
bool_value);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::set_inner_bool_option method in
|
|
jit-recording.c. */
|
|
|
|
extern void
|
|
gcc_jit_context_set_bool_use_external_driver (gcc_jit_context *ctxt,
|
|
int bool_value)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
ctxt->set_inner_bool_option (
|
|
gcc::jit::INNER_BOOL_OPTION_USE_EXTERNAL_DRIVER,
|
|
bool_value);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::add_command_line_option method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_add_command_line_option (gcc_jit_context *ctxt,
|
|
const char *optname)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_IF_FAIL (optname, ctxt, NULL, "NULL optname");
|
|
if (ctxt->get_logger ())
|
|
ctxt->get_logger ()->log ("optname: %s", optname);
|
|
|
|
ctxt->add_command_line_option (optname);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
The real work is done by the
|
|
gcc::jit::recording::context::add_driver_option method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_add_driver_option (gcc_jit_context *ctxt,
|
|
const char *optname)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_IF_FAIL (optname, ctxt, NULL, "NULL optname");
|
|
if (ctxt->get_logger ())
|
|
ctxt->get_logger ()->log ("optname: %s", optname);
|
|
|
|
ctxt->add_driver_option (optname);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::enable_dump method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_enable_dump (gcc_jit_context *ctxt,
|
|
const char *dumpname,
|
|
char **out_ptr)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_IF_FAIL (dumpname, ctxt, NULL, "NULL dumpname");
|
|
RETURN_IF_FAIL (out_ptr, ctxt, NULL, "NULL out_ptr");
|
|
|
|
ctxt->enable_dump (dumpname, out_ptr);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::compile method in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_result *
|
|
gcc_jit_context_compile (gcc_jit_context *ctxt)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
|
|
ctxt->log ("in-memory compile of ctxt: %p", (void *)ctxt);
|
|
|
|
gcc_jit_result *result = (gcc_jit_result *)ctxt->compile ();
|
|
|
|
ctxt->log ("%s: returning (gcc_jit_result *)%p",
|
|
__func__, (void *)result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::compile_to_file method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_compile_to_file (gcc_jit_context *ctxt,
|
|
enum gcc_jit_output_kind output_kind,
|
|
const char *output_path)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_IF_FAIL_PRINTF1 (
|
|
((output_kind >= GCC_JIT_OUTPUT_KIND_ASSEMBLER)
|
|
&& (output_kind <= GCC_JIT_OUTPUT_KIND_EXECUTABLE)),
|
|
ctxt, NULL,
|
|
"unrecognized output_kind: %i",
|
|
output_kind);
|
|
RETURN_IF_FAIL (output_path, ctxt, NULL, "NULL output_path");
|
|
|
|
ctxt->log ("compile_to_file of ctxt: %p", (void *)ctxt);
|
|
ctxt->log ("output_kind: %i", output_kind);
|
|
ctxt->log ("output_path: %s", output_path);
|
|
|
|
ctxt->compile_to_file (output_kind, output_path);
|
|
}
|
|
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::dump_to_file method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_dump_to_file (gcc_jit_context *ctxt,
|
|
const char *path,
|
|
int update_locations)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_IF_FAIL (path, ctxt, NULL, "NULL path");
|
|
ctxt->dump_to_file (path, update_locations);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h. */
|
|
|
|
void
|
|
gcc_jit_context_set_logfile (gcc_jit_context *ctxt,
|
|
FILE *logfile,
|
|
int flags,
|
|
int verbosity)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_IF_FAIL ((flags == 0), ctxt, NULL, "flags must be 0 for now");
|
|
RETURN_IF_FAIL ((verbosity == 0), ctxt, NULL, "verbosity must be 0 for now");
|
|
|
|
gcc::jit::logger *logger;
|
|
if (logfile)
|
|
logger = new gcc::jit::logger (logfile, flags, verbosity);
|
|
else
|
|
logger = NULL;
|
|
ctxt->set_logger (logger);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::dump_reproducer_to_file method in
|
|
jit-recording.c. */
|
|
|
|
void
|
|
gcc_jit_context_dump_reproducer_to_file (gcc_jit_context *ctxt,
|
|
const char *path)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
RETURN_IF_FAIL (path, ctxt, NULL, "NULL path");
|
|
ctxt->dump_reproducer_to_file (path);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::get_first_error method in
|
|
jit-recording.c. */
|
|
|
|
const char *
|
|
gcc_jit_context_get_first_error (gcc_jit_context *ctxt)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
|
|
return ctxt->get_first_error ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::get_last_error method in
|
|
jit-recording.c. */
|
|
|
|
const char *
|
|
gcc_jit_context_get_last_error (gcc_jit_context *ctxt)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL context");
|
|
|
|
return ctxt->get_last_error ();
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::result::get_code method in jit-result.c. */
|
|
|
|
void *
|
|
gcc_jit_result_get_code (gcc_jit_result *result,
|
|
const char *fnname)
|
|
{
|
|
RETURN_NULL_IF_FAIL (result, NULL, NULL, "NULL result");
|
|
JIT_LOG_FUNC (result->get_logger ());
|
|
RETURN_NULL_IF_FAIL (fnname, NULL, NULL, "NULL fnname");
|
|
|
|
result->log ("locating fnname: %s", fnname);
|
|
void *code = result->get_code (fnname);
|
|
result->log ("%s: returning (void *)%p", __func__, code);
|
|
|
|
return code;
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::result::get_global method in jit-result.c. */
|
|
|
|
void *
|
|
gcc_jit_result_get_global (gcc_jit_result *result,
|
|
const char *name)
|
|
{
|
|
RETURN_NULL_IF_FAIL (result, NULL, NULL, "NULL result");
|
|
JIT_LOG_FUNC (result->get_logger ());
|
|
RETURN_NULL_IF_FAIL (name, NULL, NULL, "NULL name");
|
|
|
|
void *global = result->get_global (name);
|
|
result->log ("%s: returning (void *)%p", __func__, global);
|
|
|
|
return global;
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, this is essentially a wrapper around the
|
|
destructor for gcc::jit::result in jit-result.c. */
|
|
|
|
void
|
|
gcc_jit_result_release (gcc_jit_result *result)
|
|
{
|
|
RETURN_IF_FAIL (result, NULL, NULL, "NULL result");
|
|
JIT_LOG_FUNC (result->get_logger ());
|
|
result->log ("deleting result: %p", (void *)result);
|
|
delete result;
|
|
}
|
|
|
|
/**********************************************************************
|
|
Timing support.
|
|
**********************************************************************/
|
|
|
|
/* Create a gcc_jit_timer instance, and start timing. */
|
|
|
|
gcc_jit_timer *
|
|
gcc_jit_timer_new (void)
|
|
{
|
|
gcc_jit_timer *timer = new gcc_jit_timer ();
|
|
timer->start (TV_TOTAL);
|
|
timer->push (TV_JIT_CLIENT_CODE);
|
|
return timer;
|
|
}
|
|
|
|
/* Release a gcc_jit_timer instance. */
|
|
|
|
void
|
|
gcc_jit_timer_release (gcc_jit_timer *timer)
|
|
{
|
|
RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer");
|
|
|
|
delete timer;
|
|
}
|
|
|
|
/* Associate a gcc_jit_timer instance with a context. */
|
|
|
|
void
|
|
gcc_jit_context_set_timer (gcc_jit_context *ctxt,
|
|
gcc_jit_timer *timer)
|
|
{
|
|
RETURN_IF_FAIL (ctxt, NULL, NULL, "NULL ctxt");
|
|
RETURN_IF_FAIL (timer, ctxt, NULL, "NULL timer");
|
|
|
|
ctxt->set_timer (timer);
|
|
}
|
|
|
|
/* Get the timer associated with a context (if any). */
|
|
|
|
gcc_jit_timer *
|
|
gcc_jit_context_get_timer (gcc_jit_context *ctxt)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, NULL, "NULL ctxt");
|
|
|
|
return (gcc_jit_timer *)ctxt->get_timer ();
|
|
}
|
|
|
|
/* Push the given item onto the timing stack. */
|
|
|
|
void
|
|
gcc_jit_timer_push (gcc_jit_timer *timer,
|
|
const char *item_name)
|
|
{
|
|
RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer");
|
|
RETURN_IF_FAIL (item_name, NULL, NULL, "NULL item_name");
|
|
timer->push_client_item (item_name);
|
|
}
|
|
|
|
/* Pop the top item from the timing stack. */
|
|
|
|
void
|
|
gcc_jit_timer_pop (gcc_jit_timer *timer,
|
|
const char *item_name)
|
|
{
|
|
RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer");
|
|
|
|
if (item_name)
|
|
{
|
|
const char *top_item_name = timer->get_topmost_item_name ();
|
|
|
|
RETURN_IF_FAIL_PRINTF1
|
|
(top_item_name, NULL, NULL,
|
|
"pop of empty timing stack (attempting to pop: \"%s\")",
|
|
item_name);
|
|
|
|
RETURN_IF_FAIL_PRINTF2
|
|
(strcmp (item_name, top_item_name) == 0, NULL, NULL,
|
|
"mismatching item_name:"
|
|
" top of timing stack: \"%s\","
|
|
" attempting to pop: \"%s\"",
|
|
top_item_name,
|
|
item_name);
|
|
}
|
|
|
|
timer->pop_client_item ();
|
|
}
|
|
|
|
/* Print timing information to the given stream about activity since
|
|
the timer was started. */
|
|
|
|
void
|
|
gcc_jit_timer_print (gcc_jit_timer *timer,
|
|
FILE *f_out)
|
|
{
|
|
RETURN_IF_FAIL (timer, NULL, NULL, "NULL timer");
|
|
RETURN_IF_FAIL (f_out, NULL, NULL, "NULL f_out");
|
|
|
|
timer->pop (TV_JIT_CLIENT_CODE);
|
|
timer->stop (TV_TOTAL);
|
|
timer->print (f_out);
|
|
timer->start (TV_TOTAL);
|
|
timer->push (TV_JIT_CLIENT_CODE);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is effectively done by the
|
|
gcc::jit::base_call::set_require_tail_call setter in jit-recording.h. */
|
|
|
|
void
|
|
gcc_jit_rvalue_set_bool_require_tail_call (gcc_jit_rvalue *rvalue,
|
|
int require_tail_call)
|
|
{
|
|
RETURN_IF_FAIL (rvalue, NULL, NULL, "NULL call");
|
|
JIT_LOG_FUNC (rvalue->get_context ()->get_logger ());
|
|
|
|
/* Verify that it's a call. */
|
|
gcc::jit::recording::base_call *call = rvalue->dyn_cast_base_call ();
|
|
RETURN_IF_FAIL_PRINTF1 (call, NULL, NULL, "not a call: %s",
|
|
rvalue->get_debug_string ());
|
|
|
|
call->set_require_tail_call (require_tail_call);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::type::get_aligned method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_type_get_aligned (gcc_jit_type *type,
|
|
size_t alignment_in_bytes)
|
|
{
|
|
RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
|
|
|
|
gcc::jit::recording::context *ctxt = type->m_ctxt;
|
|
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF1
|
|
(pow2_or_zerop (alignment_in_bytes), ctxt, NULL,
|
|
"alignment not a power of two: %zi",
|
|
alignment_in_bytes);
|
|
RETURN_NULL_IF_FAIL (!type->is_void (), ctxt, NULL, "void type");
|
|
|
|
return (gcc_jit_type *)type->get_aligned (alignment_in_bytes);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::type::get_vector method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_type *
|
|
gcc_jit_type_get_vector (gcc_jit_type *type, size_t num_units)
|
|
{
|
|
RETURN_NULL_IF_FAIL (type, NULL, NULL, "NULL type");
|
|
|
|
gcc::jit::recording::context *ctxt = type->m_ctxt;
|
|
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF1
|
|
(type->is_int () || type->is_float (), ctxt, NULL,
|
|
"type is not integral or floating point: %s",
|
|
type->get_debug_string ());
|
|
|
|
RETURN_NULL_IF_FAIL_PRINTF1
|
|
(pow2_or_zerop (num_units), ctxt, NULL,
|
|
"num_units not a power of two: %zi",
|
|
num_units);
|
|
|
|
return (gcc_jit_type *)type->get_vector (num_units);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::function::get_address method, in
|
|
jit-recording.c. */
|
|
|
|
gcc_jit_rvalue *
|
|
gcc_jit_function_get_address (gcc_jit_function *fn,
|
|
gcc_jit_location *loc)
|
|
{
|
|
RETURN_NULL_IF_FAIL (fn, NULL, NULL, "NULL function");
|
|
|
|
gcc::jit::recording::context *ctxt = fn->m_ctxt;
|
|
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
/* LOC can be NULL. */
|
|
|
|
return (gcc_jit_rvalue *)fn->get_address (loc);
|
|
}
|
|
|
|
/* Public entrypoint. See description in libgccjit.h.
|
|
|
|
After error-checking, the real work is done by the
|
|
gcc::jit::recording::context::new_rvalue_from_vector method, in
|
|
jit-recording.c. */
|
|
|
|
extern gcc_jit_rvalue *
|
|
gcc_jit_context_new_rvalue_from_vector (gcc_jit_context *ctxt,
|
|
gcc_jit_location *loc,
|
|
gcc_jit_type *vec_type,
|
|
size_t num_elements,
|
|
gcc_jit_rvalue **elements)
|
|
{
|
|
RETURN_NULL_IF_FAIL (ctxt, NULL, loc, "NULL ctxt");
|
|
JIT_LOG_FUNC (ctxt->get_logger ());
|
|
|
|
/* LOC can be NULL. */
|
|
RETURN_NULL_IF_FAIL (vec_type, ctxt, loc, "NULL vec_type");
|
|
|
|
/* "vec_type" must be a vector type. */
|
|
gcc::jit::recording::vector_type *as_vec_type
|
|
= vec_type->dyn_cast_vector_type ();
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (as_vec_type, ctxt, loc,
|
|
"%s is not a vector type",
|
|
vec_type->get_debug_string ());
|
|
|
|
/* "num_elements" must match. */
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
num_elements == as_vec_type->get_num_units (), ctxt, loc,
|
|
"num_elements != %zi", as_vec_type->get_num_units ());
|
|
|
|
/* "elements must be non-NULL. */
|
|
RETURN_NULL_IF_FAIL (elements, ctxt, loc, "NULL elements");
|
|
|
|
/* Each of "elements" must be non-NULL and of the correct type. */
|
|
gcc::jit::recording::type *element_type
|
|
= as_vec_type->get_element_type ();
|
|
for (size_t i = 0; i < num_elements; i++)
|
|
{
|
|
RETURN_NULL_IF_FAIL_PRINTF1 (
|
|
elements[i], ctxt, loc, "NULL elements[%zi]", i);
|
|
RETURN_NULL_IF_FAIL_PRINTF4 (
|
|
compatible_types (element_type,
|
|
elements[i]->get_type ()),
|
|
ctxt, loc,
|
|
"mismatching type for element[%zi] (expected type: %s): %s (type: %s)",
|
|
i,
|
|
element_type->get_debug_string (),
|
|
elements[i]->get_debug_string (),
|
|
elements[i]->get_type ()->get_debug_string ());
|
|
}
|
|
|
|
return (gcc_jit_rvalue *)ctxt->new_rvalue_from_vector
|
|
(loc,
|
|
as_vec_type,
|
|
(gcc::jit::recording::rvalue **)elements);
|
|
}
|
|
|
|
/* A mutex around the cached state in parse_basever.
|
|
Ideally this would be within parse_basever, but the mutex is only needed
|
|
by libgccjit. */
|
|
|
|
static pthread_mutex_t version_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
|
|
struct version_info
|
|
{
|
|
/* Default constructor. Populate via parse_basever,
|
|
guarded by version_mutex. */
|
|
version_info ()
|
|
{
|
|
pthread_mutex_lock (&version_mutex);
|
|
parse_basever (&major, &minor, &patchlevel);
|
|
pthread_mutex_unlock (&version_mutex);
|
|
}
|
|
|
|
int major;
|
|
int minor;
|
|
int patchlevel;
|
|
};
|
|
|
|
|
|
extern int
|
|
gcc_jit_version_major (void)
|
|
{
|
|
version_info vi;
|
|
return vi.major;
|
|
}
|
|
|
|
extern int
|
|
gcc_jit_version_minor (void)
|
|
{
|
|
version_info vi;
|
|
return vi.minor;
|
|
}
|
|
|
|
extern int
|
|
gcc_jit_version_patchlevel (void)
|
|
{
|
|
version_info vi;
|
|
return vi.patchlevel;
|
|
}
|