I'd like to introduce a new builtin, __builtin_constant_function_p,
with nicer semantics and better performance than __builtin_constant_p.
Background:
I want to define an assert_or_assume() macro that is equivalent to
assume() or assert(), whichever results in better compiled code.
I'd like to use __builtin_constant_p to decide whether
assert_or_assume evaluates this construct:
if (!(CONDITION))
__builtin_unreachable ();
My current idea is to write:
if (__builtin_constant_p (!(CONDITION) != !(CONDITION)))
if (!(CONDITION))
__builtin_unreachable ();
This would check that evaluating CONDITION "twice" (in undefined
order) results in the same truth value; if CONDITION includes, for
example, external function calls, we won't be able to prove that, so
the expensive construct would be skipped. However, for common
conditions such as read-only logical and arithmetic expressions,
!(CONDITION) != !(CONDITION) will be folded to 0, which
__builtin_constant_p can then recognize as a constant.
The Problem:
However, it seems that with trunk GCC, __builtin_constant_p always
returns false for expressions which contain calls to functions not
explicitly marked with attribute((const)), even when those functions
are both inlined and found to allow the const attribute by
-Wsuggest-attr=const.
This is in contrast to macros which work just fine in
__builtin_constant_p's argument. So, in this case, an inline function
isn't as fast as a macro.
The problem is that __builtin_constant_p must fold its argument very
early, before gimplification, to avoid evaluation of the argument.
My proposed solution:
The idea I'm currently playing around with is to add a new
__builtin_constant_function_p builtin, which folds to 1 as soon if its
argument can be shown, at compile time, to be a pointer to a constant
function. This can be used in conjunction with inner functions (C
only) to redefine constant_p as:
#define constant_p(EXPR) ({ int inner(void) { return EXPR; };
__builtin_constant_function_p (inner); })
As mentioned, this definition of constant_p would be superior to
__builtin_constant_p while also ensuring the expression itself is
never evaluated.
Patch:
In early test runs, the attached patch appears to work, but I'm
inexperienced when it comes to GCC internals, and I also find that
people often disagree with me for good reasons.
Limitations:
This currently works for C only (using lambdas to expand it to C++
looks like it ought to be possible to me).
Compilation time probably increases significantly, because an inner
function is generated and optimized only to be eventually
discarded. It's also possible this affects code generation in the rest
of the outer function.
This absolutely hasn't been tested enough. I'm attaching my main test
case.
We give up in the fold-builtins pass. It might be a better idea to
wait for the last such pass, if we could.
For very good reasons, this doesn't work as I naively expected it
would for functions marked with attribute((const)), because the
compiler cannot assume that those functions, if called, would return.
Questions:
Am I totally on the wrong track here?
Should we have __builtin_assume?
Should we instead fix our code so forgetting a "const" attribute won't
hurt performance, if it can be inferred?
Should there be a new function attribute to mark functions that may be
assumed by the compiler to return?
Should we fix after-the-fact assume()s to work better?
#include
extern int nonnegative (int) __attribute__((const));
#ifdef FAST
int nonnegative (int i)
{
return i >= 0;
}
#endif
int main(int argc, char **argv)
{
int i = argc;
int c;
{
auto int inner(void)
{
return nonnegative(i) == nonnegative(i);
}
c = __builtin_constant_function_p(inner);
}
if (c)
if (!nonnegative(i))
__builtin_unreachable ();
printf("%d\n", i & 0x8000);
}
From 5e694e0e35b56caf4469cb516db50608f026c741 Mon Sep 17 00:00:00 2001
From: Pip Cet
Date: Sun, 30 Jun 2019 12:24:17 +
Subject: [PATCH] Add a __builtin_constant_function_p builtin.
---
gcc/builtins.c | 78 ++
gcc/builtins.def | 1 +
gcc/expr.c | 4 ++-
gcc/tree-ssa-ccp.c | 8 +
4 files changed, 90 insertions(+), 1 deletion(-)
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 4ecfd49d03c..e1ff327c84b 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -30,6 +30,8 @@ along with GCC; see the file COPYING3. If not see
#include "tree.h"
#include "memmodel.h"
#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-walk.h"
#include "predict.h"
#include "params.h"
#include "tm_p.h"
@@ -150,6 +152,7 @@ static tree stabilize_va_list_loc (location_t, tree, int);
static rtx expand_builtin_expect (tree, rtx);
static rtx expand_builtin_expect_with_probability (tree, rtx);
static tree fold_builtin_constant_p (tree);
+static tree fold_builtin_constant_function_p (tree);
static tree fold_builtin_classify_typ