From b861fbbfbcbf18cc1b69dc4b7f91c596af40c8e4 Mon Sep 17 00:00:00 2001 From: Andrea Corallo Date: Sat, 30 May 2020 10:33:08 +0100 Subject: [PATCH] Add new gcc_jit_context_new_blob entry point gcc/jit/ChangeLog 2020-06-02 Andrea Corallo * docs/topics/compatibility.rst (LIBGCCJIT_ABI_14): New ABI tag. * docs/topics/expressions.rst (Binary Blobs): New section documenting gcc_jit_context_new_blob. * jit-common.h: Document class blob. * jit-playback.c (playback::context::global_new_decl) (playback::context::global_finalize_lvalue): New methods. (playback::context::new_global): Make use of global_new_decl, global_finalize_lvalue. (playback::context::new_blob): New method. * jit-playback.h (new_blob, global_new_decl): New method declarations. * jit-recording.c (recording::context::new_blob) (recording::blob::replay_into) (recording::global::write_qualifier_to_dump): New methods. (recording::global::write_to_dump): Use write_qualifier_to_dump. (recording::blob::write_to_dump): New method. * jit-recording.h (class blob): New class. (class global): Have m_kind and m_name as protected. (class global): Remove FINAL qualifier to replay_into and write_to_dump. (class global): New method write_qualifier_to_dump decl. (class context): New method new_blob decl. * libgccjit++.h (context::new_blob): New method. * libgccjit.c (gcc_jit_lvalue_as_rvalue): New function. * libgccjit.h (gcc_jit_context_new_blob): New function declaration. (LIBGCCJIT_HAVE_gcc_jit_context_new_blob): New macro. * libgccjit.map (LIBGCCJIT_ABI_14): New ABI tag. gcc/testsuite/ChangeLog 2020-06-02 Andrea Corallo * jit.dg/all-non-failing-tests.h: Add test-blob.c. * jit.dg/test-blob.c: New test. --- gcc/jit/docs/topics/compatibility.rst | 5 ++ gcc/jit/docs/topics/expressions.rst | 49 ++++++++++ gcc/jit/jit-common.h | 1 + gcc/jit/jit-playback.c | 67 ++++++++++++-- gcc/jit/jit-playback.h | 16 ++++ gcc/jit/jit-recording.c | 95 +++++++++++++++++--- gcc/jit/jit-recording.h | 48 +++++++++- gcc/jit/libgccjit++.h | 21 +++++ gcc/jit/libgccjit.c | 30 +++++++ gcc/jit/libgccjit.h | 17 ++++ gcc/jit/libgccjit.map | 7 +- gcc/testsuite/jit.dg/all-non-failing-tests.h | 7 ++ gcc/testsuite/jit.dg/test-blob.c | 54 +++++++++++ 13 files changed, 393 insertions(+), 24 deletions(-) create mode 100644 gcc/testsuite/jit.dg/test-blob.c diff --git a/gcc/jit/docs/topics/compatibility.rst b/gcc/jit/docs/topics/compatibility.rst index 0c0ce070d722..3d9684c73b4b 100644 --- a/gcc/jit/docs/topics/compatibility.rst +++ b/gcc/jit/docs/topics/compatibility.rst @@ -215,3 +215,8 @@ entrypoints: * :func:`gcc_jit_version_minor` * :func:`gcc_jit_version_patchlevel` + +``LIBGCCJIT_ABI_14`` +-------------------- +``LIBGCCJIT_ABI_14`` covers the addition of +:func:`gcc_jit_context_new_blob` diff --git a/gcc/jit/docs/topics/expressions.rst b/gcc/jit/docs/topics/expressions.rst index db2f2ca2e9c6..deb46594d80a 100644 --- a/gcc/jit/docs/topics/expressions.rst +++ b/gcc/jit/docs/topics/expressions.rst @@ -576,6 +576,55 @@ Global variables referring to it. Analogous to using an "extern" global from a header file. +Binary Blobs +**************** + +.. function:: gcc_jit_context_new_blob (gcc_jit_context *ctxt,\ + gcc_jit_location *loc,\ + enum gcc_jit_global_kind kind,\ + const void *ptr,\ + size_t size,\ + const char *name); + + Add a new global variable equivalent to an initialized array of + char to the context. + + The parameter ``name`` must be non-NULL. The call takes a copy of the + underlying string, so it is valid to pass in a pointer to an on-stack + buffer. + + Similarly the parameter ``ptr`` must be non-NULL. The call copies + the memory content pointed by ``ptr`` for ``size`` bytes. This + content will be stores in the compilation unit and used as + initialization value of the array. + + .. c:macro:: GCC_JIT_GLOBAL_EXPORTED + + Global is defined by the client code and is visible + by name outside of this JIT context via + :c:func:`gcc_jit_result_get_global` (and this value is required for + the global to be accessible via that entrypoint). + + .. c:macro:: GCC_JIT_GLOBAL_INTERNAL + + Global is defined by the client code, but is invisible + outside of it. Analogous to a "static" global within a .c file. + Specifically, the variable will only be visible within this + context and within child contexts. + + .. c:macro:: GCC_JIT_GLOBAL_IMPORTED + + Global is not defined by the client code; we're merely + referring to it. Analogous to using an "extern" global from a + header file. + + This entrypoint was added in :ref:`LIBGCCJIT_ABI_14`; you can test for + its presence using + + .. code-block:: c + + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_blob + Working with pointers, structs and unions ----------------------------------------- diff --git a/gcc/jit/jit-common.h b/gcc/jit/jit-common.h index 4570bd2d717f..7a1578d10e89 100644 --- a/gcc/jit/jit-common.h +++ b/gcc/jit/jit-common.h @@ -127,6 +127,7 @@ namespace recording { class lvalue; class local; class global; + class blob; class param; class base_call; class function_pointer; diff --git a/gcc/jit/jit-playback.h b/gcc/jit/jit-playback.h index 074434a9f6b2..1cc674a8ddd9 100644 --- a/gcc/jit/jit-playback.h +++ b/gcc/jit/jit-playback.h @@ -111,6 +111,14 @@ public: type *type, const char *name); + lvalue * + new_blob (location *loc, + enum gcc_jit_global_kind kind, + type *type, + const void *ptr, + size_t size, + const char *name); + template rvalue * new_rvalue_from_const (type *type, @@ -266,6 +274,14 @@ private: const char * get_path_s_file () const; const char * get_path_so_file () const; + tree + global_new_decl (location *loc, + enum gcc_jit_global_kind kind, + type *type, + const char *name); + lvalue * + global_finalize_lvalue (tree inner); + private: /* Functions for implementing "compile". */ diff --git a/gcc/jit/jit-playback.c b/gcc/jit/jit-playback.c index d2c8bb4c154d..8cd986942bc7 100644 --- a/gcc/jit/jit-playback.c +++ b/gcc/jit/jit-playback.c @@ -506,14 +506,14 @@ new_function (location *loc, return func; } -/* Construct a playback::lvalue instance (wrapping a tree). */ +/* In use by new_global and new_blob. */ -playback::lvalue * +tree playback::context:: -new_global (location *loc, - enum gcc_jit_global_kind kind, - type *type, - const char *name) +global_new_decl (location *loc, + enum gcc_jit_global_kind kind, + type *type, + const char *name) { gcc_assert (type); gcc_assert (name); @@ -543,6 +543,15 @@ new_global (location *loc, if (loc) set_tree_location (inner, loc); + return inner; +} + +/* In use by new_global and new_blob. */ + +playback::lvalue * +playback::context:: +global_finalize_lvalue (tree inner) +{ varpool_node::get_create (inner); varpool_node::finalize_decl (inner); @@ -552,6 +561,52 @@ new_global (location *loc, return new lvalue (this, inner); } +/* Construct a playback::lvalue instance (wrapping a tree). */ + +playback::lvalue * +playback::context:: +new_global (location *loc, + enum gcc_jit_global_kind kind, + type *type, + const char *name) +{ + tree inner = global_new_decl (loc, kind, type, name); + + return global_finalize_lvalue (inner); +} + +playback::lvalue * +playback::context:: +new_blob (location *loc, + enum gcc_jit_global_kind kind, + type *type, + const void *ptr, + size_t size, + const char *name) +{ + tree inner = global_new_decl (loc, kind, type, name); + + static vec *constructor_elements; + const char *p = (const char *)ptr; + for (size_t i = 0; i < size; i++) + { + /* Compare with 'output_init_element' c-typeck.c:9691. */ + constructor_elt celt = + { build_int_cst (long_unsigned_type_node, i), + build_int_cst (char_type_node, p[i]) }; + vec_safe_push (constructor_elements, celt); + } + /* Compare with 'pop_init_level' c-typeck.c:8780. */ + tree ctor = build_constructor (type->as_tree (), constructor_elements); + constructor_elements = NULL; + + /* Compare with 'store_init_value' c-typeck.c:7555. */ + DECL_INITIAL (inner) = ctor; + + return global_finalize_lvalue (inner); +} + + /* Implementation of the various gcc::jit::playback::context::new_rvalue_from_const methods. diff --git a/gcc/jit/jit-recording.h b/gcc/jit/jit-recording.h index 726b9c4b8371..5e2762ff8785 100644 --- a/gcc/jit/jit-recording.h +++ b/gcc/jit/jit-recording.h @@ -204,6 +204,13 @@ public: rvalue *max_value, block *block); + lvalue * + new_blob (location *loc, + enum gcc_jit_global_kind kind, + const void *ptr, + size_t size, + const char *name); + void set_str_option (enum gcc_jit_str_option opt, const char *value); @@ -1329,11 +1336,14 @@ public: m_name (name) {} - void replay_into (replayer *) FINAL OVERRIDE; + void replay_into (replayer *) OVERRIDE; void visit_children (rvalue_visitor *) FINAL OVERRIDE {} - void write_to_dump (dump &d) FINAL OVERRIDE; + void write_to_dump (dump &d) OVERRIDE; + +protected: + void write_qualifier_to_dump (dump &d); private: string * make_debug_string () FINAL OVERRIDE { return m_name; } @@ -1343,11 +1353,43 @@ private: return PRECEDENCE_PRIMARY; } -private: +protected: enum gcc_jit_global_kind m_kind; string *m_name; }; +class blob : public global +{ +public: + blob (context *ctxt, + location *loc, + enum gcc_jit_global_kind kind, + void *ptr, + size_t size, + string *name) + : global (ctxt, loc, kind, + ctxt->new_array_type (loc, + ctxt->get_type (GCC_JIT_TYPE_CHAR), + size), + name), + m_ptr (ptr), + m_size (size) + {} + + ~blob () + { + free (m_ptr); + } + + void replay_into (replayer *) FINAL OVERRIDE; + + void write_to_dump (dump &d) FINAL OVERRIDE; + +private: + void *m_ptr; + size_t m_size; +}; + template class memento_of_new_rvalue_from_const : public rvalue { diff --git a/gcc/jit/jit-recording.c b/gcc/jit/jit-recording.c index b73cd76a0a02..b23ebdc57d2c 100644 --- a/gcc/jit/jit-recording.c +++ b/gcc/jit/jit-recording.c @@ -1229,6 +1229,29 @@ recording::context::new_case (recording::rvalue *min_value, return result; } +/* Create a recording::blob instance and add it to this context's list + of mementos. + + Implements the post-error-checking part of + gcc_jit_context_new_blob. */ + +recording::lvalue * +recording::context::new_blob (recording::location *loc, + enum gcc_jit_global_kind kind, + const void *ptr, + size_t size, + const char *name) +{ + void *data = xmalloc (size); + recording::blob *result = + new recording::blob (this, loc, kind, memcpy (data, ptr, size), size, + new_string (name)); + record (result); + m_globals.safe_push (result); + + return result; +} + /* Set the given string option for this context, or add an error if it's not recognized. @@ -4399,6 +4422,38 @@ recording::global::replay_into (replayer *r) playback_string (m_name))); } +void +recording::blob::replay_into (replayer *r) +{ + set_playback_obj (r->new_blob (playback_location (r, m_loc), + m_kind, + m_type->playback_type (), + m_ptr, + m_size, + playback_string (m_name))); +} + +void +recording::global::write_qualifier_to_dump (dump &d) +{ + switch (m_kind) + { + default: + gcc_unreachable (); + + case GCC_JIT_GLOBAL_EXPORTED: + break; + + case GCC_JIT_GLOBAL_INTERNAL: + d.write ("static "); + break; + + case GCC_JIT_GLOBAL_IMPORTED: + d.write ("extern "); + break; + } +} + /* Override the default implementation of recording::memento::write_to_dump for globals. This will be of the form: @@ -4424,25 +4479,37 @@ recording::global::write_to_dump (dump &d) if (d.update_locations ()) m_loc = d.make_location (); - switch (m_kind) - { - default: - gcc_unreachable (); + write_qualifier_to_dump (d); - case GCC_JIT_GLOBAL_EXPORTED: - break; + d.write ("%s %s;\n", + m_type->get_debug_string (), + get_debug_string ()); +} - case GCC_JIT_GLOBAL_INTERNAL: - d.write ("static "); - break; - case GCC_JIT_GLOBAL_IMPORTED: - d.write ("extern "); - break; - } - d.write ("%s %s;\n", +/* Override the default implementation of + recording::memento::write_to_dump for blobs. */ + +void +recording::blob::write_to_dump (dump &d) +{ + if (d.update_locations ()) + m_loc = d.make_location (); + + write_qualifier_to_dump (d); + + d.write ("%s %s =\n { ", m_type->get_debug_string (), get_debug_string ()); + + const char *p = (const char *)m_ptr; + for (size_t i = 0; i < m_size; i++) + { + d.write ("0x%x, ", p[i]); + if (i && !(i % 64)) + d.write ("\n "); + } + d.write ("};\n"); } /* A table of enum gcc_jit_global_kind values expressed in string diff --git a/gcc/jit/libgccjit++.h b/gcc/jit/libgccjit++.h index 69e67766640c..bb663e9aca1f 100644 --- a/gcc/jit/libgccjit++.h +++ b/gcc/jit/libgccjit++.h @@ -182,6 +182,12 @@ namespace gccjit const std::string &name, location loc = location ()); + lvalue new_blob (enum gcc_jit_global_kind kind, + const void *ptr, + size_t size, + const std::string &name, + location loc = location ()); + rvalue new_rvalue (type numeric_type, int value) const; rvalue new_rvalue (type numeric_type, @@ -860,6 +866,21 @@ context::new_global (enum gcc_jit_global_kind kind, name.c_str ())); } +inline lvalue +context::new_blob (enum gcc_jit_global_kind kind, + const void *ptr, + size_t size, + const std::string &name, + location loc) +{ + return lvalue (gcc_jit_context_new_blob (m_inner_ctxt, + loc.get_inner_location (), + kind, + ptr, + size, + name.c_str ())); +} + inline rvalue context::new_rvalue (type numeric_type, int value) const diff --git a/gcc/jit/libgccjit.h b/gcc/jit/libgccjit.h index 1c5a12e9c015..a66be3568fa0 100644 --- a/gcc/jit/libgccjit.h +++ b/gcc/jit/libgccjit.h @@ -788,6 +788,23 @@ gcc_jit_context_new_global (gcc_jit_context *ctxt, gcc_jit_type *type, const char *name); +#define LIBGCCJIT_HAVE_gcc_jit_context_new_blob + +/* Create a binary blob. + + This API entrypoint was added in LIBGCCJIT_ABI_14; you can test for its + presence using + #ifdef LIBGCCJIT_HAVE_gcc_jit_context_new_blob +*/ + +extern gcc_jit_lvalue * +gcc_jit_context_new_blob (gcc_jit_context *ctxt, + gcc_jit_location *loc, + enum gcc_jit_global_kind kind, + const void *ptr, + size_t size, + const char *name); + /* Upcasting. */ extern gcc_jit_object * gcc_jit_lvalue_as_object (gcc_jit_lvalue *lvalue); diff --git a/gcc/jit/libgccjit.c b/gcc/jit/libgccjit.c index a29e9885e59b..a504d568b388 100644 --- a/gcc/jit/libgccjit.c +++ b/gcc/jit/libgccjit.c @@ -1097,6 +1097,36 @@ gcc_jit_context_new_global (gcc_jit_context *ctxt, return (gcc_jit_lvalue *)ctxt->new_global (loc, kind, type, name); } +/* Public entrypoint. See description in libgccjit.h. + + After error-checking, the real work is done by the + gcc::jit::recording::context::new_blob method, in + jit-recording.c. */ + +gcc_jit_lvalue * +gcc_jit_context_new_blob (gcc_jit_context *ctxt, + gcc_jit_location *loc, + enum gcc_jit_global_kind kind, + const void *ptr, + size_t size, + 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 (name, ctxt, loc, "NULL ptr"); + /* SIZE can be zero. */ + RETURN_NULL_IF_FAIL (name, ctxt, loc, "NULL name"); + + return (gcc_jit_lvalue *)ctxt->new_blob (loc, kind, ptr, size, name); +} + /* Public entrypoint. See description in libgccjit.h. After error-checking, this calls the trivial diff --git a/gcc/jit/libgccjit.map b/gcc/jit/libgccjit.map index 6137dd4b4b03..40c3605310d8 100644 --- a/gcc/jit/libgccjit.map +++ b/gcc/jit/libgccjit.map @@ -186,4 +186,9 @@ LIBGCCJIT_ABI_13 { gcc_jit_version_major; gcc_jit_version_minor; gcc_jit_version_patchlevel; -} LIBGCCJIT_ABI_12; \ No newline at end of file +} LIBGCCJIT_ABI_12; + +LIBGCCJIT_ABI_14 { + global: + gcc_jit_context_new_blob; +} LIBGCCJIT_ABI_13; diff --git a/gcc/testsuite/jit.dg/all-non-failing-tests.h b/gcc/testsuite/jit.dg/all-non-failing-tests.h index ad469dad6994..7eaf373e6ed8 100644 --- a/gcc/testsuite/jit.dg/all-non-failing-tests.h +++ b/gcc/testsuite/jit.dg/all-non-failing-tests.h @@ -67,6 +67,13 @@ #undef create_code #undef verify_code +/* test-blob.c */ +#define create_code create_code_blob +#define verify_code verify_code_blob +#include "test-blob.c" +#undef create_code +#undef verify_code + /* test-calling-external-function.c */ #define create_code create_code_calling_external_function #define verify_code verify_code_calling_external_function diff --git a/gcc/testsuite/jit.dg/test-blob.c b/gcc/testsuite/jit.dg/test-blob.c new file mode 100644 index 000000000000..d344975dd5a7 --- /dev/null +++ b/gcc/testsuite/jit.dg/test-blob.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include + +#include "libgccjit.h" + +#include "harness.h" + +#define BIG_BLOB_SIZE (1 << 12) /* 4KB. */ + +static char test_blob1[] = { 0xc, 0xa, 0xf, 0xf, 0xe }; +static char test_blob2[] = { 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3 }; +static char test_blob3[BIG_BLOB_SIZE]; + +void +create_code (gcc_jit_context *ctxt, void *user_data) +{ + /* Let's try to inject the equivalent of: + + char bin_blob1[] = { 0xc, 0xa, 0xf, 0xf, 0xe }; + char bin_blob2[] = { 0x3, 0x2, 0x1, 0x0, 0x1, 0x2, 0x3 }; + char bin_blob3[4096]... + */ + + gcc_jit_context_new_blob (ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED, test_blob1, + sizeof (test_blob1), "bin_blob1"); + + gcc_jit_context_new_blob (ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED, test_blob2, + sizeof (test_blob2), "bin_blob2"); + + for (size_t i = 0; i < BIG_BLOB_SIZE; i++) + test_blob3[i] = i * i + i; + gcc_jit_context_new_blob (ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED, test_blob3, + sizeof (test_blob3), "bin_blob3"); +} + +void +verify_code (gcc_jit_context *ctxt, gcc_jit_result *result) +{ + CHECK_NON_NULL (result); + void *blob = gcc_jit_result_get_global (result, "bin_blob1"); + CHECK_NON_NULL (blob); + CHECK_VALUE (memcmp (test_blob1, blob, sizeof (test_blob1)), 0); + + blob = gcc_jit_result_get_global (result, "bin_blob2"); + CHECK_NON_NULL (blob); + CHECK_VALUE (memcmp (test_blob2, blob, sizeof (test_blob2)), 0); + + blob = gcc_jit_result_get_global (result, "bin_blob3"); + CHECK_NON_NULL (blob); + CHECK_VALUE (memcmp (test_blob3, blob, sizeof (test_blob3)), 0); + +} -- 2.17.1