[cfe-dev] How to create a new builtin in clang?

Richard Smith richard at metafoo.co.uk
Fri May 18 12:13:45 PDT 2012


On Fri, May 18, 2012 at 9:12 AM, Andy Gibbs <andyg1001 at hotmail.co.uk> wrote:

> I'd like to learn a little about how clang works on the inside, and would
> like to have a go at implementing a new "builtin" function which forces an
> expression to be folded to a constant.


OK. The first thing you need to be aware of is that 'folded to a constant'
is not the same as 'evaluated as a constant expression'. The former case
just means "take an expression, and use any tricks we know to reduce it to
a compile-time constant". The latter means following the relevant language
standards, which is a nebulous affair:

* In C{89,99,11}, precise rules are given for "integer constant
expressions" (ICEs, which are required in various contexts), as well as for
"constant expressions in initializers". However, implementations are
allowed to accept other (non-standard) forms of constant expression in
initializers, and we do not have an implementation of the standard rules:
instead, Expr::isConstantInitializer checks whether an expression is
something we can emit as a constant (this is a superset of the set of
expressions we can fold to a constant), and we use that to check whether an
expression is a constant expression for the purpose of initialization.
Expr::isIntegerConstantExpr checks whether an expression is an integer
constant expression, following the language standard.

* The situation in C++98 is similar to C, except that "integer constant
expression" is renamed to "integral constant expression", and
implementations are not allowed to extend what "constant expression" means.
However, they are allowed to use constant initialization for dynamic
initializers which they are able to evaluate, which has the same
consequences.

* The situation in C++11 is completely different. The term "constant
expression" is defined to cover a much broader set of cases there (which
can be checked by Expr::isCXX11ConstantExpr). Implementations are still
permitted to convert dynamic initialization to constant initialization if
they can fold an expression.

Additionally, the set of things we can fold is slightly different in the
different languages. Anyway, the point is, outside of C++11, we don't
really have an implemented, checkable notion of 'constant expression' other
than 'integer constant expression'.


> The idea would be that it would
> either compile-time-evaluate the expression or halt compilation with an
> error.  I believe there isn't anything currently that does this for
> arbitrary types, only integers.
>

For what it's worth, if you want something which folds expressions, I think
you can already build that (horrifically):

// Outside C++11, where everything we can fold has a '!'
struct assert_foldable_t { int arr[1]; };
#define fold(expr) (__builtin_constant_p(1) ? (expr) : (expr))
#define ignore(expr) ((int)!(expr))
#define assert_foldable(expr) ((void)(struct
assert_foldable_t){.arr[ignore(fold(expr))] = 1})
#define builtin_fold(expr) (assert_foldable(expr), fold(expr))

// In C++11, same as above, except:
template<typename T> constexpr int ignore(T&&) { return 0; }

What I envisage is something like...
>
>   auto x = __builtin_fold(<expression>)
>
> which returns a constant value of appropriate type for the expression.
>
> I'd envisage it as a replacement for an already-possible two-step approach
> as follows:
>
> struct __fold_impl_1
>    {
>    static constexpr auto value = <expression>;
>    };
>
> ...
> auto x = __fold_impl_1::value;
>

As noted above, this evaluates the expression as a constant expression,
which isn't the same as folding it.


> The obvious down-side of the two-step approach is requiring a special macro
> for generating __fold_impl_xxx for each folded expression, plus the
> inconvenience and possible scope problems of having to use such a macro!
>

If you just wanted C++11 constant expression semantics, you could use
something like this:

#define builtin_fold(expr) \
  ([]{ \
    struct S { \
      constexpr decltype(expr) get() const { \
        return (expr); \
      } \
    } s; \
    constexpr auto val = s.get(); (void)val; \
    return s; \
  }().get())


> I'd also like it to work in non-C++11 code.
>

Then you will need to either fold rather than evaluate as a constant
expression, or implement the full constant expression rules for non-C++11
languages (which would be a great thing to have -- see llvm.org/PR4402).


> I could imagine that it should be very possible to get clang to generate
> such a struct on-the-fly which avoided the scope problems and substitute
> the
> expression for a reference to the value.
>
> The questions I have are:
>
>  - how does one go about getting clang to build such substitutiary code in
> this way?
>

This is the wrong approach :)


>  - is this the best approach, or is there a better/simpler way?


If you want to add a new builtin for this, you should search through the
codebase to find the places where builtins are currently handled, and add
the relevant support for your builtin there. In particular, you will need
to add a new builtin to include/clang/Basic/Builtins.def, add some code to
Sema to check that the argument can be folded, and add some code to
AST/ExprConstant.cpp to fold the argument. You shouldn't need to change
CodeGen, since ExprConstant should be able to fold all occurrences of this
builtin.

However, I think a better feature (and one which I would definitely support
pushing into trunk clang) would be an attribute which can be applied to
declarations (maybe [[clang::const_init]]), which requires the declaration
to be initialized by a constant expression. This isn't equivalent to any
kind of __builtin_fold, since it would work in cases where there is no
expression to wrap in __builtin_fold (such as "T x(1, 2, 3);" or "T x = {
1, 2, 3 };"), and would allow constant initialization to be required for
objects with constexpr constructors but non-trivial destructors.

-- Richard
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20120518/281168fe/attachment.html>


More information about the cfe-dev mailing list