From 9a090c02aedd5ee23951bacb5f0c5275a373875d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=A1s=20B=C3=A9rtolo?= <nicolasbertolo@gmail.com>
Date: Wed, 20 May 2020 00:34:32 -0300
Subject: [PATCH 1/2] Define static data using string literals.

The purpose of this change is to dump prettier C files.
This does not affect compilation times in my tests.

* src/comp.c (emit_static_object): Define static objects using string
literals, memcpy and bzero.
---
 src/comp.c | 300 +++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 282 insertions(+), 18 deletions(-)

diff --git a/src/comp.c b/src/comp.c
index 996168bbb5..3f4b3e03b7 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -46,6 +46,7 @@
 # include "w32common.h"
 
 #undef gcc_jit_block_add_assignment
+#undef gcc_jit_block_add_assignment_op
 #undef gcc_jit_block_add_comment
 #undef gcc_jit_block_add_eval
 #undef gcc_jit_block_end_with_conditional
@@ -75,6 +76,7 @@
 #undef gcc_jit_context_new_rvalue_from_int
 #undef gcc_jit_context_new_rvalue_from_long
 #undef gcc_jit_context_new_rvalue_from_ptr
+#undef gcc_jit_context_new_string_literal
 #undef gcc_jit_context_new_struct_type
 #undef gcc_jit_context_new_unary_op
 #undef gcc_jit_context_new_union_type
@@ -164,6 +166,8 @@ DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_rvalue_from_long,
             (gcc_jit_context *ctxt, gcc_jit_type *numeric_type, long value));
 DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_rvalue_from_ptr,
             (gcc_jit_context *ctxt, gcc_jit_type *pointer_type, void *value));
+DEF_DLL_FN (gcc_jit_rvalue *, gcc_jit_context_new_string_literal,
+            (gcc_jit_context *ctxt, const char *value));
 DEF_DLL_FN (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,
@@ -197,6 +201,9 @@ DEF_DLL_FN (gcc_jit_type *, gcc_jit_type_get_pointer, (gcc_jit_type *type));
 DEF_DLL_FN (void, gcc_jit_block_add_assignment,
             (gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_lvalue *lvalue,
              gcc_jit_rvalue *rvalue));
+DEF_DLL_FN (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));
 DEF_DLL_FN (void, gcc_jit_block_add_eval,
             (gcc_jit_block *block, gcc_jit_location *loc,
              gcc_jit_rvalue *rvalue));
@@ -239,6 +246,7 @@ init_gccjit_functions (void)
 
   /* In alphabetical order */
   LOAD_DLL_FN (library, gcc_jit_block_add_assignment);
+  LOAD_DLL_FN (library, gcc_jit_block_add_assignment_op);
   LOAD_DLL_FN (library, gcc_jit_block_add_comment);
   LOAD_DLL_FN (library, gcc_jit_block_add_eval);
   LOAD_DLL_FN (library, gcc_jit_block_end_with_conditional);
@@ -268,6 +276,7 @@ init_gccjit_functions (void)
   LOAD_DLL_FN (library, gcc_jit_context_new_rvalue_from_int);
   LOAD_DLL_FN (library, gcc_jit_context_new_rvalue_from_long);
   LOAD_DLL_FN (library, gcc_jit_context_new_rvalue_from_ptr);
+  LOAD_DLL_FN (library, gcc_jit_context_new_string_literal);
   LOAD_DLL_FN (library, gcc_jit_context_new_struct_type);
   LOAD_DLL_FN (library, gcc_jit_context_new_unary_op);
   LOAD_DLL_FN (library, gcc_jit_context_new_union_type);
@@ -296,6 +305,7 @@ init_gccjit_functions (void)
 
 /* In alphabetical order */
 #define gcc_jit_block_add_assignment fn_gcc_jit_block_add_assignment
+#define gcc_jit_block_add_assignment_op fn_gcc_jit_block_add_assignment_op
 #define gcc_jit_block_add_comment fn_gcc_jit_block_add_comment
 #define gcc_jit_block_add_eval fn_gcc_jit_block_add_eval
 #define gcc_jit_block_end_with_conditional fn_gcc_jit_block_end_with_conditional
@@ -325,6 +335,7 @@ #define gcc_jit_context_new_param fn_gcc_jit_context_new_param
 #define gcc_jit_context_new_rvalue_from_int fn_gcc_jit_context_new_rvalue_from_int
 #define gcc_jit_context_new_rvalue_from_long fn_gcc_jit_context_new_rvalue_from_long
 #define gcc_jit_context_new_rvalue_from_ptr fn_gcc_jit_context_new_rvalue_from_ptr
+#define gcc_jit_context_new_string_literal fn_gcc_jit_context_new_string_literal
 #define gcc_jit_context_new_struct_type fn_gcc_jit_context_new_struct_type
 #define gcc_jit_context_new_unary_op fn_gcc_jit_context_new_unary_op
 #define gcc_jit_context_new_union_type fn_gcc_jit_context_new_union_type
@@ -548,6 +559,8 @@ #define F_RELOC_MAX_SIZE 1500
   gcc_jit_rvalue *data_relocs_ephemeral;
   /* Synthesized struct holding func relocs.  */
   gcc_jit_lvalue *func_relocs;
+  gcc_jit_function *naive_bzero;
+  gcc_jit_function *naive_memcpy;
   Lisp_Object d_default_idx;
   Lisp_Object d_impure_idx;
   Lisp_Object d_ephemeral_idx;
@@ -2346,10 +2359,7 @@ emit_static_object (const char *name, Lisp_Object obj)
 {
   /* libgccjit has no support for initialized static data.
      The mechanism below is certainly not aesthetic but I assume the bottle neck
-     in terms of performance at load time will still be the reader.
-     NOTE: we can not relay on libgccjit even for valid NULL terminated C
-     strings cause of this funny bug that will affect all pre gcc10 era gccs:
-     https://gcc.gnu.org/ml/jit/2019-q3/msg00013.html  */
+     in terms of performance at load time will still be the reader. */
 
   Lisp_Object str = Fprin1_to_string (obj, Qnil);
   ptrdiff_t len = SBYTES (str);
@@ -2398,22 +2408,91 @@ emit_static_object (const char *name, Lisp_Object obj)
   gcc_jit_lvalue *arr =
       gcc_jit_lvalue_access_field (data_struct, NULL, fields[1]);
 
-  for (ptrdiff_t i = 0; i < len; i++, p++)
+  gcc_jit_lvalue *ptrvar = gcc_jit_function_new_local (f, NULL,
+                                                       comp.char_ptr_type,
+                                                       "ptr");
+
+  gcc_jit_block_add_assignment (
+    block,
+    NULL,
+    ptrvar,
+    gcc_jit_lvalue_get_address (
+      gcc_jit_context_new_array_access (
+        comp.ctxt,
+        NULL,
+        gcc_jit_lvalue_as_rvalue (arr),
+        gcc_jit_context_new_rvalue_from_int (comp.ctxt, comp.int_type, 0)),
+      NULL));
+
+  /* Emit a call to naive_bzero() to clear all the static object. It
+     is not strictly necessary since the dynamic linker will take care
+     of zeroing the memory too. */
+  gcc_jit_rvalue *bzero_args[2]
+    = {gcc_jit_lvalue_as_rvalue (ptrvar),
+       gcc_jit_context_new_rvalue_from_long (comp.ctxt,
+                                             comp.unsigned_long_long_type,
+                                             len + 1)};
+
+  gcc_jit_block_add_eval (block, NULL,
+                          gcc_jit_context_new_call (comp.ctxt, NULL,
+                                                    comp.naive_bzero, 2,
+                                                    bzero_args));
+
+  for (ptrdiff_t i = 0; i < len;)
     {
-      gcc_jit_block_add_assignment (
-	block,
-	NULL,
-	gcc_jit_context_new_array_access (
-	  comp.ctxt,
-	  NULL,
-	  gcc_jit_lvalue_as_rvalue (arr),
-	  gcc_jit_context_new_rvalue_from_int (comp.ctxt,
-					       comp.ptrdiff_type,
-					       i)),
-	gcc_jit_context_new_rvalue_from_int (comp.ctxt,
-					     comp.char_type,
-					     *p));
+      /* We can't use string literals longer that 200 bytes because
+         they cause a crash in older versions of gccjit.
+         https://gcc.gnu.org/ml/jit/2019-q3/msg00013.html  */
+      char str[200];
+      strncpy (str, p, 200);
+      str[199] = 0;
+      uintptr_t l = strlen (str);
+
+      if (l != 0)
+        {
+          p += l;
+          i += l;
+
+          gcc_jit_rvalue *args[3]
+            = {gcc_jit_lvalue_as_rvalue (ptrvar),
+               gcc_jit_context_new_string_literal (comp.ctxt, str),
+               gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                    comp.unsigned_long_long_type,
+                                                    l)};
+
+          gcc_jit_block_add_eval (block, NULL,
+                                  gcc_jit_context_new_call (comp.ctxt, NULL,
+                                                            comp.naive_memcpy,
+                                                            3, args));
+          gcc_jit_block_add_assignment (block, NULL, ptrvar,
+            gcc_jit_lvalue_get_address (
+              gcc_jit_context_new_array_access (comp.ctxt, NULL,
+                gcc_jit_lvalue_as_rvalue (ptrvar),
+                gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                     comp.uintptr_type,
+                                                     l)),
+              NULL));
+        }
+      else
+        {
+          /* If strlen returned 0 that means that the static object
+             contains a NULL byte. In that case just move over to the
+             next block. We can rely on the byte being zero because of
+             the previous call to bzero and because the dynamic linker
+             cleared it. */
+          p++;
+          i++;
+          gcc_jit_block_add_assignment (
+            block, NULL, ptrvar,
+            gcc_jit_lvalue_get_address (
+              gcc_jit_context_new_array_access (
+                comp.ctxt, NULL, gcc_jit_lvalue_as_rvalue (ptrvar),
+                gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                     comp.uintptr_type, 1)),
+              NULL));
+        }
     }
+
   gcc_jit_block_add_assignment (
 	block,
 	NULL,
@@ -2759,6 +2838,189 @@ define_jmp_buf (void)
 				     1, &field);
 }
 
+/*
+ * naive implementation of bzero. Hopefully it will be turned into a
+ * smarter implementation by the optimizer.
+ */
+
+static void
+define_naive_bzero (void)
+{
+  /*
+static void
+naive_bzero (char * ptr, unsigned long long len)
+{
+  unsigned long long i;
+
+entry_block:
+  i = (unsigned long long)0;
+  goto test_block;
+
+test_block:
+  if (len > i) goto loop_block; else goto return_block;
+
+loop_block:
+  ptr[i] = (char)0;
+  i += (unsigned long long)1;
+  goto test_block;
+
+return_block:
+  return;
+}
+  */
+  gcc_jit_param *params[2] = {
+    gcc_jit_context_new_param (comp.ctxt, NULL, comp.char_ptr_type, "ptr"),
+    gcc_jit_context_new_param (comp.ctxt, NULL, comp.unsigned_long_long_type,
+                               "len"),
+  };
+
+  comp.naive_bzero
+    = gcc_jit_context_new_function (comp.ctxt, NULL, GCC_JIT_FUNCTION_INTERNAL,
+                                    comp.void_type, "naive_bzero", 2,
+                                    params, false);
+
+  DECL_BLOCK (entry_block, comp.naive_bzero);
+  DECL_BLOCK (test_block, comp.naive_bzero);
+  DECL_BLOCK (loop_block, comp.naive_bzero);
+  DECL_BLOCK (return_block, comp.naive_bzero);
+
+  gcc_jit_lvalue *ptr = gcc_jit_param_as_lvalue (params[0]);
+  gcc_jit_lvalue *i = gcc_jit_function_new_local (comp.naive_bzero, NULL,
+                                                  comp.uintptr_type, "i");
+
+  gcc_jit_block_add_assignment (entry_block, NULL, i,
+                                gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                                     comp.uintptr_type,
+                                                                     0));
+  gcc_jit_block_end_with_jump (entry_block, NULL, test_block);
+
+  /* if (len > i) */
+
+  gcc_jit_rvalue *comparison_result
+    = gcc_jit_context_new_comparison (comp.ctxt, NULL, GCC_JIT_COMPARISON_GT,
+                                      gcc_jit_param_as_rvalue (params[1]),
+                                      gcc_jit_lvalue_as_rvalue (i));
+
+  gcc_jit_block_end_with_conditional (test_block, NULL, comparison_result,
+                                      loop_block, return_block);
+
+  /* ptr[i] = 0; */
+  /* i += 1; */
+
+  gcc_jit_block_add_assignment (loop_block, NULL,
+    gcc_jit_context_new_array_access (comp.ctxt, NULL,
+                                      gcc_jit_lvalue_as_rvalue (ptr),
+                                      gcc_jit_lvalue_as_rvalue (i)),
+    gcc_jit_context_new_rvalue_from_int (comp.ctxt, comp.char_type, 0));
+
+  gcc_jit_block_add_assignment_op (
+    loop_block, NULL, i, GCC_JIT_BINARY_OP_PLUS,
+    gcc_jit_context_new_rvalue_from_int (comp.ctxt, comp.uintptr_type, 1));
+
+  gcc_jit_block_end_with_jump (loop_block, NULL, test_block);
+
+  /* return; */
+
+  gcc_jit_block_end_with_void_return (return_block, NULL);
+}
+
+/*
+ * naive implementation of memcpy. Hopefully it will be turned into a
+ * smarter implementation by the optimizer.
+ */
+
+static void
+define_naive_memcpy (void)
+{
+  /*
+static void *
+naive_memcpy (char * dest, char * src, unsigned long long len)
+{
+  unsigned long long i;
+
+entry_block:
+  i = (unsigned long long)0;
+  goto test_block;
+
+test_block:
+  if (len > i) goto loop_block; else goto return_block;
+
+loop_block:
+  dest[i] = src[i];
+  i += (unsigned long long)1;
+  goto test_block;
+
+return_block:
+  return dest;
+}
+  */
+  gcc_jit_param *params[3] = {
+    gcc_jit_context_new_param (comp.ctxt, NULL, comp.char_ptr_type, "dest"),
+    gcc_jit_context_new_param (comp.ctxt, NULL, comp.char_ptr_type, "src"),
+    gcc_jit_context_new_param (comp.ctxt, NULL, comp.unsigned_long_long_type,
+                               "len"),
+  };
+
+  comp.naive_memcpy
+    = gcc_jit_context_new_function (comp.ctxt, NULL, GCC_JIT_FUNCTION_INTERNAL,
+                                    comp.void_ptr_type, "naive_memcpy", 3,
+                                    params, false);
+
+  DECL_BLOCK (entry_block, comp.naive_memcpy);
+  DECL_BLOCK (test_block, comp.naive_memcpy);
+  DECL_BLOCK (loop_block, comp.naive_memcpy);
+  DECL_BLOCK (return_block, comp.naive_memcpy);
+
+  gcc_jit_lvalue *dest = gcc_jit_param_as_lvalue (params[0]);
+  gcc_jit_lvalue *src = gcc_jit_param_as_lvalue (params[1]);
+  gcc_jit_lvalue *i = gcc_jit_function_new_local (comp.naive_memcpy, NULL,
+                                                  comp.uintptr_type, "i");
+
+  gcc_jit_block_add_assignment (entry_block, NULL, i,
+                                gcc_jit_context_new_rvalue_from_int (comp.ctxt,
+                                                                     comp.uintptr_type,
+                                                                     0));
+  gcc_jit_block_end_with_jump (entry_block, NULL, test_block);
+
+  /* if (len > i) */
+
+  gcc_jit_rvalue *comparison_result
+    = gcc_jit_context_new_comparison (comp.ctxt, NULL, GCC_JIT_COMPARISON_GT,
+                                      gcc_jit_param_as_rvalue (params[2]),
+                                      gcc_jit_lvalue_as_rvalue (i));
+
+  gcc_jit_block_end_with_conditional (test_block, NULL, comparison_result,
+                                      loop_block, return_block);
+
+  /* dest[i] = src[i]; */
+  /* i += 1; */
+
+  gcc_jit_block_add_assignment (loop_block, NULL,
+    gcc_jit_context_new_array_access (comp.ctxt, NULL,
+                                      gcc_jit_lvalue_as_rvalue (dest),
+                                      gcc_jit_lvalue_as_rvalue (i)),
+    gcc_jit_lvalue_as_rvalue (
+      gcc_jit_context_new_array_access (comp.ctxt, NULL,
+                                        gcc_jit_lvalue_as_rvalue (src),
+                                        gcc_jit_lvalue_as_rvalue (i))));
+
+  gcc_jit_block_add_assignment_op (loop_block,
+                                   NULL,
+                                   i,
+                                   GCC_JIT_BINARY_OP_PLUS,
+                                   gcc_jit_context_new_rvalue_from_int (
+                                     comp.ctxt,
+                                     comp.uintptr_type,
+                                     1));
+
+  gcc_jit_block_end_with_jump (loop_block, NULL, test_block);
+
+  /* return dest; */
+
+  gcc_jit_block_end_with_return (return_block, NULL,
+                                 gcc_jit_param_as_rvalue (params[0]));
+}
+
 /* struct handler definition  */
 
 static void
@@ -3780,6 +4042,8 @@ DEFUN ("comp--init-ctxt", Fcomp__init_ctxt, Scomp__init_ctxt,
   define_handler_struct ();
   define_thread_state_struct ();
   define_cast_union ();
+  define_naive_bzero ();
+  define_naive_memcpy ();
 
   return Qt;
 }
-- 
2.25.1.windows.1

