loci: rewrite assertions

Assertion failure now calls abort() instead of dereferencing a null pointer.

The noreturn attribute is important for static analysis.
diff --git a/c_gen/c_code_gen.py b/c_gen/c_code_gen.py
index 483e1c5..cbbcfba 100644
--- a/c_gen/c_code_gen.py
+++ b/c_gen/c_code_gen.py
@@ -609,12 +609,19 @@
 
 extern const char *const of_error_strings[];
 
+#ifdef __GNUC__
+#define LOCI_NORETURN_ATTR __attribute__((__noreturn__))
+#else
+#define LOCI_NORETURN_ATTR
+#endif
+
+extern void loci_assert_fail(
+    const char *cond,
+    const char *file,
+    unsigned int line) LOCI_NORETURN_ATTR;
+
 #ifndef NDEBUG
-/* #define LOCI_ASSERT(val) assert(val) */
-#define FORCE_FAULT *(volatile int *)0 = 1
-#define LOCI_ASSERT(val) if (!(val)) \\
-    fprintf(stderr, "\\nASSERT %s. %s:%d\\n", #val, __FILE__, __LINE__), \\
-    FORCE_FAULT
+#define LOCI_ASSERT(val) ((val) ? (void)0 : loci_assert_fail(#val, __FILE__, __LINE__))
 #else
 #define LOCI_ASSERT(val)
 #endif
diff --git a/c_gen/templates/of_utils.c b/c_gen/templates/of_utils.c
index ba22536..373ad59 100644
--- a/c_gen/templates/of_utils.c
+++ b/c_gen/templates/of_utils.c
@@ -35,6 +35,8 @@
  ****************************************************************/
 
 #include <loci/of_utils.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 
 /**
@@ -76,3 +78,9 @@
     return rv;
 }
 
+void
+loci_assert_fail(const char *msg, const char *file, unsigned int line)
+{
+    fprintf(stderr, "\\nASSERT %s. %s:%d\\n", msg, file, line);
+    abort();
+}