The assertion function takes two arguments, a condition and a message
string. The return value is an expression "condition!=0". Additionally, if
that expression can be evaluated to a constant of zero at compile time, an
error will be issued that includes the message string in its text.
The main reason I'd like to see this added to gcc is to help improve the Linux
kernel's robustness by catching certain conditions at compile time. For
instance, Linux's udelay() function (which waits for a number of microseconds
up to a limit of 20000uS), is implemented in the i386 architecture thus:
| extern void __bad_udelay(void);
| extern void __udelay(unsigned long usecs);
| extern void __const_udelay(unsigned long usecs);
|
| #define udelay(n) (__builtin_constant_p(n) ? \
| ((n) > 20000 ? __bad_udelay() : __const_udelay((n) * 0x10c6ul)) : \
| __udelay(n))
This relies on __bad_udelay() getting referenced in the program when n is too
large, and causing a linker error, which is quite hard to trace since the
kernel build makes heavy use of incremental linking.
This can be re-implemented using my gcc patch:
| #define udelay(n) ( \
| __builtin_ct_assert((n)<=20000,"udelay() value should be <=20000uS"), \
| __builtin_constant_p(n) ? __const_udelay((n) * 0x10c6ul) : __udelay(n))
And producing an error of the following sort:
| test.c:21: compile-time assertion failed: udelay() value should be <=20000uS
Cheers,
David Howells
_______________________________________________________________________________
Index: builtins.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.c,v
retrieving revision 1.98
diff -u -r1.98 builtins.c
--- builtins.c 2001/03/28 11:03:48 1.98
+++ builtins.c 2001/04/27 14:07:06
@@ -142,6 +142,7 @@
static rtx expand_builtin_fputs PARAMS ((tree, int));
static tree stabilize_va_list PARAMS ((tree, int));
static rtx expand_builtin_expect PARAMS ((tree, rtx));
+static rtx expand_builtin_ct_assert PARAMS ((tree));
static tree fold_builtin_constant_p PARAMS ((tree));
static tree build_function_call_expr PARAMS ((tree, tree));
static int validate_arglist PARAMS ((tree, ...));
@@ -3267,6 +3268,62 @@
return target;
}
+
+/* Expand expression EXP, which is a call to __builtin_ct_assert.
+ * - return 1 on assertion passed, 0 on assertion condition not constant
+ * and emit an error on assertion failure
+ */
+static rtx
+expand_builtin_ct_assert (arglist)
+ tree arglist;
+{
+ const char *msg;
+ tree cond, msgexp;
+ rtx result;
+
+ if (arglist == NULL_TREE
+ || TREE_CHAIN (arglist) == NULL_TREE
+ || TREE_CHAIN (TREE_CHAIN (arglist)) != NULL_TREE
+ )
+ {
+ error("__builtin_ct_assert takes two arguments");
+ return 0;
+ }
+ cond = TREE_VALUE (arglist);
+ msgexp = TREE_VALUE (TREE_CHAIN (arglist));
+
+ if (AGGREGATE_TYPE_P(TREE_TYPE (cond)))
+ {
+ error ("first arg to `__builtin_ct_assert' must be a scalar");
+ return 0;
+ }
+
+ if (TREE_CODE (TREE_TYPE (msgexp)) != POINTER_TYPE)
+ {
+ error ("second arg to `__builtin_ct_assert' must be a constant string");
+ return 0;
+ }
+
+ msg = c_getstr (msgexp);
+
+ if (!msg || *msg == '\0')
+ {
+ error("__builtin_ct_assert was not given a valid message string");
+ return const1_rtx;
+ }
+
+ cond = build (NE_EXPR, integer_type_node, cond, integer_zero_node);
+
+ /* try and evaluate the condition */
+ result = expand_expr (cond, NULL_RTX, VOIDmode, 0);
+ if (GET_CODE (result) != CONST_INT)
+ return result;
+
+ if (INTVAL (result) == 0)
+ error("compile-time assertion failed: %s",msg);
+
+ return result;
+}
/* Expand an expression EXP that calls a built-in function,
with result going to TARGET if that's convenient
@@ -3621,6 +3678,8 @@
return expand_builtin_va_copy (arglist);
case BUILT_IN_EXPECT:
return expand_builtin_expect (arglist, target);
+ case BUILT_IN_CT_ASSERT:
+ return expand_builtin_ct_assert (arglist);
default: /* just do library call, if unknown builtin */
error ("built-in function `%s' not currently supported",
Index: builtins.def
===================================================================
RCS file: /cvs/gcc/gcc/gcc/builtins.def,v
retrieving revision 1.19
diff -u -r1.19 builtins.def
--- builtins.def 2001/03/28 11:03:48 1.19
+++ builtins.def 2001/04/27 14:07:06
@@ -104,6 +104,7 @@
DEF_BUILTIN(BUILT_IN_VA_END)
DEF_BUILTIN(BUILT_IN_VA_COPY)
DEF_BUILTIN(BUILT_IN_EXPECT)
+DEF_BUILTIN(BUILT_IN_CT_ASSERT)
/* C++ extensions */
DEF_BUILTIN(BUILT_IN_NEW)
Index: c-common.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/c-common.c,v
retrieving revision 1.230
diff -u -r1.230 c-common.c
--- c-common.c 2001/04/24 08:22:03 1.230
+++ c-common.c 2001/04/27 14:07:07
@@ -3446,6 +3446,10 @@
endlink))),
BUILT_IN_EXPECT, BUILT_IN_NORMAL, NULL_PTR);
+ builtin_function ("__builtin_ct_assert",
+ default_function_type,
+ BUILT_IN_CT_ASSERT, BUILT_IN_NORMAL, NULL_PTR);
+
/* Currently under experimentation. */
builtin_function_2 ("__builtin_memcpy", "memcpy",
memcpy_ftype, memcpy_ftype,
Index: extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/extend.texi,v
retrieving revision 1.95
diff -u -r1.95 extend.texi
--- extend.texi 2001/03/23 01:49:08 1.95
+++ extend.texi 2001/04/27 14:07:16
@@ -3770,6 +3770,44 @@
@noindent
when testing pointer or floating-point values.
+
+@findex __builtin_ct_assert
+@item __builtin_ct_assert(@var{exp}, @var{errmsg})
+You can use @code{__builtin_ct_assert} to perform compile time
+assertion checking. @var{exp} will be used as the assertion
+condition, and should be of scalar type. The compiler will try
+to evaluate @var{exp} to a constant, and if successful, the
+compiler will check the result. If @var{exp} evaluates to zero,
+then an error will be emitted that includes the @var{errmsg}
+string in the text.
+
+The return value is @var{exp}!=0.
+
+For example:
+
+@smallexample
+extern void __udelay(int x);
+#define udelay(x) \
+ (__builtin_ct_assert((x)<=20000,"udelay limited to 20000uS"), \
+ __udelay(n))
+@end smallexample
+
+@noindent
+would indicate that if it can, the compiler should check that the
+value passed to udelay() does not exceed 20000. So:
+
+@smallexample
+udelay(1000);
+udelay(40000); /* line 17 */
+@end smallexample
+
+@noindent
+would produce an error message similar to the following:
+
+@smallexample
+wibble.c:17: compile-time assertion failed: udelay limited to 20000uS
+@end smallexample
+
@end table
@node C++ Extensions