--------------------
``LIBGCCJIT_ABI_28`` covers the addition of
:func:`gcc_jit_context_new_alignof`
+
+.. _LIBGCCJIT_ABI_29:
+
+``LIBGCCJIT_ABI_29``
+--------------------
+``LIBGCCJIT_ABI_29`` covers the addition of
+:func:`gcc_jit_global_set_readonly`
#ifdef LIBGCCJIT_HAVE_ATTRIBUTES
+.. function:: void\
+ gcc_jit_global_set_readonly (gcc_jit_lvalue *global)
+
+ Set the global variable as read-only, meaning you cannot assign to this variable.
+
+ This entrypoint was added in :ref:`LIBGCCJIT_ABI_29`; you can test for its
+ presence using:
+
+ .. code-block:: c
+
+ #ifdef LIBGCCJIT_HAVE_gcc_jit_global_set_readonly
+
Working with pointers, structs and unions
-----------------------------------------
const char *name,
enum global_var_flags flags,
const std::vector<std::pair<gcc_jit_variable_attribute,
- std::string>> &attributes)
+ std::string>> &attributes,
+ bool readonly)
{
gcc_assert (type);
gcc_assert (name);
break;
}
- if (TYPE_READONLY (type_tree))
+ if (TYPE_READONLY (type_tree) || readonly)
TREE_READONLY (inner) = 1;
if (loc)
const char *name,
enum global_var_flags flags,
const std::vector<std::pair<gcc_jit_variable_attribute,
- std::string>> &attributes)
+ std::string>> &attributes,
+ bool readonly)
{
tree inner =
- global_new_decl (loc, kind, type, name, flags, attributes);
+ global_new_decl (loc, kind, type, name, flags, attributes, readonly);
return global_finalize_lvalue (inner);
}
const char *name,
enum global_var_flags flags,
const std::vector<std::pair<gcc_jit_variable_attribute,
- std::string>> &attributes)
+ std::string>> &attributes,
+ bool readonly)
{
- tree inner = global_new_decl (loc, kind, type, name, flags, attributes);
+ tree inner = global_new_decl (loc, kind, type, name, flags, attributes, readonly);
vec<constructor_elt, va_gc> *constructor_elements = NULL;
const char *name,
enum global_var_flags flags,
const std::vector<std::pair<gcc_jit_variable_attribute,
- std::string>> &attributes);
+ std::string>> &attributes,
+ bool readonly);
lvalue *
new_global_initialized (location *loc,
const std::vector<std::pair<
gcc_jit_variable_attribute,
std::string>>
- &attributes);
+ &attributes,
+ bool readonly);
rvalue *
new_ctor (location *log,
const char *name,
enum global_var_flags flags,
const std::vector<std::pair<gcc_jit_variable_attribute,
- std::string>> &attributes);
+ std::string>> &attributes,
+ bool readonly);
lvalue *
global_finalize_lvalue (tree inner);
m_initializer,
playback_string (m_name),
m_flags,
- m_string_attributes)
+ m_string_attributes,
+ m_readonly)
: r->new_global (playback_location (r, m_loc),
m_kind,
m_type->playback_type (),
playback_string (m_name),
m_flags,
- m_string_attributes);
+ m_string_attributes,
+ m_readonly);
if (m_tls_model != GCC_JIT_TLS_MODEL_NONE)
global->set_tls_model (recording::tls_models[m_tls_model]);
gcc_jit_variable_attribute_enum_strings[std::get<0>(attribute)],
std::get<1>(attribute).c_str());
+ if (m_readonly)
+ r.write (" gcc_jit_global_set_readonly (%s /* gcc_jit_lvalue *lvalue */);\n",
+ id);
if (m_initializer)
switch (m_type->dereference ()->get_size ())
void add_string_attribute (gcc_jit_variable_attribute attribute, const char* value);
+ bool get_readonly () const
+ {
+ return m_readonly;
+ }
+
+ void set_readonly ()
+ {
+ m_readonly = true;
+ }
+
virtual const char *access_as_lvalue (reproducer &r);
virtual bool is_global () const { return false; }
virtual bool is_local () const { return false; }
unsigned m_alignment;
std::vector<std::pair<gcc_jit_variable_attribute,
std::string>> m_string_attributes;
+ bool m_readonly = false;
};
class param : public lvalue
return global;
}
+/* Public entrypoint. See description in libgccjit.h.
+
+ After error-checking, the real work is done by the
+ gcc::jit::recording::global::set_readonly method, in
+ jit-recording.cc. */
+
+extern void
+gcc_jit_global_set_readonly (gcc_jit_lvalue *global)
+{
+ RETURN_IF_FAIL (global, NULL, NULL, "NULL global");
+ RETURN_IF_FAIL_PRINTF1 (global->is_global (), NULL, NULL,
+ "lvalue \"%s\" not a global",
+ global->get_debug_string ());
+
+ global->set_readonly ();
+}
+
/* Public entrypoint. See description in libgccjit.h.
After error-checking, this calls the trivial
lvalue->get_type ()->get_debug_string (),
rvalue->get_debug_string (),
rvalue->get_type ()->get_debug_string ());
+ RETURN_IF_FAIL_PRINTF1 (
+ !lvalue->get_readonly (),
+ ctxt, loc,
+ "cannot assign to readonly variable: %s",
+ lvalue->get_debug_string ());
gcc::jit::recording::statement *stmt = block->add_assignment (loc, lvalue, rvalue);
const void *blob,
size_t num_bytes);
+extern void
+gcc_jit_global_set_readonly (gcc_jit_lvalue *global);
+
+#define LIBGCCJIT_HAVE_gcc_jit_global_set_readonly
+
/* Upcasting. */
extern gcc_jit_object *
gcc_jit_lvalue_as_object (gcc_jit_lvalue *lvalue);
global:
gcc_jit_context_new_alignof;
} LIBGCCJIT_ABI_27;
+
+LIBGCCJIT_ABI_29 {
+ global:
+ gcc_jit_global_set_readonly;
+} LIBGCCJIT_ABI_28;
#undef create_code
#undef verify_code
+/* test-readonly.c: This can't be in the testcases array as it
+ is target-specific. */
+
/* test-restrict.c: This can't be in the testcases array as it needs
the `-O3` flag. */
--- /dev/null
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+ /* Let's try to inject the equivalent of:
+
+ const long integer = 10;
+
+ void
+ test_fn ()
+ {
+ integer = 12;
+ }
+
+ and verify that the API complains about assigning to a read-only
+ variable.
+ */
+ gcc_jit_type *void_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID);
+ gcc_jit_type *long_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_LONG);
+
+ gcc_jit_function *func =
+ gcc_jit_context_new_function (ctxt, NULL,
+ GCC_JIT_FUNCTION_EXPORTED,
+ void_type,
+ "test_fn",
+ 0, NULL,
+ 0);
+ gcc_jit_block *initial =
+ gcc_jit_function_new_block (func, "initial");
+
+ gcc_jit_lvalue *integer =
+ gcc_jit_context_new_global (
+ ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED, long_type, "integer");
+
+ gcc_jit_rvalue *ten = gcc_jit_context_new_rvalue_from_int (ctxt, long_type, 10);
+ gcc_jit_global_set_initializer_rvalue (integer, ten);
+ gcc_jit_global_set_readonly(integer);
+
+ gcc_jit_rvalue *twelve = gcc_jit_context_new_rvalue_from_int (ctxt, long_type, 12);
+ gcc_jit_block_add_assignment(initial, NULL, integer, twelve);
+
+ gcc_jit_block_end_with_void_return (initial, NULL);
+}
+
+void
+verify_code (gcc_jit_context *ctxt, gcc_jit_result *result)
+{
+ CHECK_VALUE (result, NULL);
+
+ /* Verify that the correct error messages were emitted. */
+ CHECK_STRING_VALUE (gcc_jit_context_get_first_error (ctxt),
+ "gcc_jit_block_add_assignment:"
+ " cannot assign to readonly variable: integer");
+}
--- /dev/null
+/* { dg-do compile { target x86_64-*-* } } */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "libgccjit.h"
+
+/* We don't want set_options() in harness.h to set -O3 so our little local
+ is optimized away. */
+#define TEST_ESCHEWS_SET_OPTIONS
+static void set_options (gcc_jit_context *ctxt, const char *argv0)
+{
+}
+
+#define TEST_COMPILING_TO_FILE
+#define OUTPUT_KIND GCC_JIT_OUTPUT_KIND_ASSEMBLER
+#define OUTPUT_FILENAME "output-of-test-readonly.c.s"
+#include "harness.h"
+
+void
+create_code (gcc_jit_context *ctxt, void *user_data)
+{
+ /* Let's try to inject the equivalent of:
+ const int foo;
+ */
+ gcc_jit_type *int_type =
+ gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
+ gcc_jit_lvalue *foo =
+ gcc_jit_context_new_global (
+ ctxt, NULL, GCC_JIT_GLOBAL_EXPORTED, int_type, "foo");
+
+ gcc_jit_rvalue *ten = gcc_jit_context_new_rvalue_from_int (ctxt, int_type, 10);
+ gcc_jit_global_set_initializer_rvalue (foo, ten);
+ gcc_jit_global_set_readonly(foo);
+}
+
+/* { dg-final { jit-verify-output-file-was-created "" } } */
+/* { dg-final { jit-verify-assembler-output ".section\t.rodata" } } */