On Tue, Oct 6, 2026 at 9:21 AM Richard Biener
<[email protected]> wrote:
>
> On Mon, Oct 5, 2026 at 6:01 PM Jasper Niebuhr <[email protected]> wrote:
> >
> > On Mon, Oct 5, 2026 at 4:04 PM Richard Biener
> > <[email protected]> wrote:
> > >
> > > On Mon, Oct 5, 2026 at 3:13 PM York Jasper Niebuhr <[email protected]> 
> > > wrote:
> > > >
> > > > Hi,
> > > > as mentioned today at the plumbers toolchain track, here is my approach
> > > > to tackling constant-folding issues regarding eBPF CORE and runtime
> > > > structure layout randomization. I developed it to support the next
> > > > iteration of Selfpatch-SLR and its linux kernel version:
> > > >
> > > > https://lore.kernel.org/all/[email protected]/
> > > >
> > > > The problem this addresses is that the frontend folds e.g.
> > > > offsetof-builtin calls to constants, erasing any information on what
> > > > fields contributed to the constant, and even that the constant depends
> > > > on field offsets at all. This is obviously a problem when you want to
> > > > finalize field offsets at a later time.
> > > >
> > > > For marked structures, this patch makes the frontend construct
> > > > DEFERRED_CST_COMPONENT_REF nodes instead of normal COMPONENT_REFs.
> > > > Deferred cst in this context means that something would usually be a
> > > > compile-time constant and still behaves as one at runtime, but is not
> > > > yet known.
> > > >
> > > > When fold_offsetof encounters these deferred cst component refs, it does
> > > > not just return a constant integer, but a DEFERRED_CST_EXPR around a
> > > > tree that still holds the relevant semantic information. In this tree,
> > > > each contributing deferred cst field offset is represented by a call to
> > > > a corresponding IFN.
> > > >
> > > > Additionally, I adapted c_fully_fold to merge true surrounding constants
> > > > into the DEFERRED_CST_EXPRs. For example, "4 + DEFERRED_CST_EXPR<x>"
> > > > becomes "DEFERRED_CST_EXPR<4 + x>". When checking for constness in some
> > > > contexts, like in static initializers, DEFERRED_CST_EXPRs act as opaque
> > > > and report as valid in that context if -fdeferred-cst-static is set.
> > > > This allows code such as:
> > > >
> > > >     struct A {
> > > >         int x;
> > > >     } __attribute__((deferred_cst_layout));
> > > >
> > > >     static const size_t x_offset = offsetof(struct A, x);
> > > >
> > > > Or:
> > > >
> > > >     struct cred *init_cred_ptr = &init_task.cred;
> > > >
> > > > Which would usually break when transformed to expressions that are not
> > > > compile-time constants.
> > > >
> > > > Deferred constants are supported up to the deferred_cst_barrier pass,
> > > > which runs at the very beginning of the small IPA passes, immediately
> > > > after lowering. By default, the barrier will replace all deferred
> > > > constants in gimple and static initializers with their true compile-time
> > > > values. If -fdeferred-cst-barrier-hard is set, the barrier will error
> > > > instead, if any deferred constants still exist.
> > > >
> > > > The big idea here is that an IPA pass right before the barrier pass can
> > > > iterate over all functions and static initializers and turn all deferred
> > > > constant nodes into whatever is needed. For static initializers
> > > > specifically, that pass can generate functions that recalculate and
> > > > update the static data at runtime, by essentially copy pasting the
> > > > trees under the DEFERRED_CST_EXPR and generating the according
> > > > MODIFY_EXPRs. In the case of CORE, I guess you could even generate info
> > > > for the JIT on how to calculate the static values and fill them in.
> > > >
> > > > Anyways, after this actual instrumentation pass there should be no
> > > > deferred constant nodes left so the hard barrier is happy.
> > > >
> > > > The way this currently applies to field offsets and offsetof
> > > > specifically, I believe this could be expanded to also work for enum
> > > > values, by producing something along the lines of:
> > > >
> > > >     DEFERRED_CST_EXPR<IFN_DEFERRED_CST_ENUM_VALUE(...)>
> > > >
> > > > I am looking forward for your input on this, and feel free to approach
> > > > me at plumbers any time to talk about this.
> > >
> > > GCC already supports 'runtime layout' via PLACEHOLDER_EXPR and
> > > a non-constant offset in the FIELD_DECL/COMPONENT_REF.  I don't think
> > > we want a second mechanism for this.
> > >
> > > Richard.
> >
> > Hi,
> > I should have clarified this better: this patch is not about the
> > DEFERRED_CST_COMPONENT_REF, rather about deferred constants as a
> > general frontend feature. The C frontend's fold_offsetof does not
> > inspect the cached field offset of a COMPONENT_REF, but makes it
> > disappear entirely. That is what forces libbpf to provide alternatives
> > to offsetof.
> >
> > Besides them being inconveneient, these alternatives can not occur in
> > static initializers, because builtin calls do not qualify as compile-
> > time constants.
> >
> > Apart from field offsets, David mentioned earlier that they could also
> > use this to handle enum values. By turning enum values into
> >
> >     DEFERRED_CST_EXPR<something_enum_value(MY_ENUM_LITERAL)>
> >
> > and treating that expr the way I described, a middle-end pass can
> > reliably recover what enum the expression refers to, not just 42.
> >
> > So this mechanism is supposed to provide a way to preserve semantic
> > origins of otherwise folded constants through frontends, without the
> > frontends producing errors because they expect fully folded constants
> > in some places. It is not supposed to be the 100th way of supporting
> > "runtime layouts".
> >
> > And as far as I can tell, PLACEHOLDER_EXPR will not solve this problem
> > in the C/C++ frontend either.
>
> I suppose you need to explain then what "runtime structure layout
> randomization" is.

The prototype I built for runtime structure layout randomization in C
programs generates metadata that describes what instructions and data
of a program image have to be patched to reflect different structure
layouts. When the program starts (or the kernel boots), the SLR
runtime shuffles structure layouts and applies the corresponding
patches in-memory (rewriting immediates/displacements in the
instruction stream, reordering static instances of randomized types,
updating static data leaves that depend on changed field offsets).

For this to not turn the image into a buggy mess, it is necessary that
the toolchain correctly identifies and tracks every single dependency
on field offsets that can change at runtime. For the most part, this
means instrumenting COMPONENT_REFs in the mid-/back-end. But the
front-end irreversably eradicates some relevant dependencies.

For example, consider:

    #define container_of(ptr, type, member) ({
        void *__mptr = (void *)(ptr);
        ((type *)(__mptr - offsetof(type, member))); })

The frontend turns

    base_t *base_ptr = container_of(field_ptr, base_t, field);

into

    base_t *base_ptr = field_ptr - <integer constant>;

No following pass can identify this field offset dependency. And not
generating corresponding patch information causes the program image
to end up in a semantically invalid state.

The DEFERRED_CST_COMPONENT_REF is a method to let fold_offsetof know
when special handling is required, and to make sure no COMPONENT_REFs
are lost elsewhere. However, all other cases I could find in which
COMPONENT_REFs are folded do not constitute runtime offset
dependencies. For exampe, it is not a problem if a

    COMPONENT_REF<CONSTRUCTOR<...>>

is folded to the corresponding constructor element, because there is
no logic dependency on the offset itself. If there is really no other
case besides fold_offsetof in which field offset dependencies are
lost, DEFERRED_CST_COMPONENT_REFs are not required at all because
fold_offsetof can simply check if the accessed type has the according
attribute.

So the real substance of this patch is the DEFERRED_CST_EXPR node and
the selective permission for those to occur in contexts that usually
require compile-time constants (like initializers). This does not
implement runtime structure layout randomization, but is a toolchain
requirement for it.

And eBPF is a potential additional user of this. They have run into
the same problem (frontend folding relevant info), recently regarding
enums. Since the C frontend turns

    enum E {
        A, B, C, D
    };

    int something_with_C()
    {
        int x = C;
        return x;
    }

into

    ;; Function something_with_C (null)
    ;; enabled by -tree-original

    {
      int x = 2;

        int x = 2;
      return x;
    }

a normal pass can not iterate over that function body and identify
that the integer constant needs to be replaced by the eBPF loader to
reflect a potentially different value for that specific enum literal.

The only existing way to make this kind of info persist through the
frontend is custom parser hooks that replace these would-be-constants
with something not constant (builtin calls). This inevitably makes
the C frontend error in contexts that do not permit non-constant
expressions. Stuff like

    static enum E e = C;

breaks under such instrumentation. Additionally, these parser hooks
are uncomfortably hacky and the deferred constant mechanism could
solve this issue in a unified matter that allows language-independent
instrumentation in the early middle-end.

I hope this makes my intentions with this clearer.

Jasper.

> Richard.
>
> >
> > Jasper.
> >
> > > > Jasper
> > > >
> > > > Signed-off-by: York Jasper Niebuhr <[email protected]>
> > > > ---
> > > >  gcc/Makefile.in           |   4 +-
> > > >  gcc/alias.cc              |   1 +
> > > >  gcc/c-family/c-attribs.cc |  30 +-
> > > >  gcc/c-family/c-common.cc  | 107 ++++++-
> > > >  gcc/c/c-decl.cc           |   1 +
> > > >  gcc/c/c-fold.cc           | 157 +++++++++-
> > > >  gcc/c/c-typeck.cc         |  40 ++-
> > > >  gcc/common.opt            |   8 +
> > > >  gcc/deferred-cst.cc       | 626 ++++++++++++++++++++++++++++++++++++++
> > > >  gcc/deferred-cst.h        |  39 +++
> > > >  gcc/expr.cc               |  35 ++-
> > > >  gcc/fold-const.cc         |  14 +
> > > >  gcc/gimple-expr.cc        |   5 +
> > > >  gcc/gimple.cc             |   3 +-
> > > >  gcc/gimplify.cc           |  54 +++-
> > > >  gcc/internal-fn.cc        |   9 +
> > > >  gcc/internal-fn.def       |   3 +
> > > >  gcc/passes.def            |   1 +
> > > >  gcc/tree-pass.h           |   1 +
> > > >  gcc/tree-pretty-print.cc  |  31 +-
> > > >  gcc/tree.cc               |  19 +-
> > > >  gcc/tree.def              |  13 +
> > > >  gcc/tree.h                |   1 +
> > > >  gcc/varasm.cc             |  18 +-
> > > >  24 files changed, 1176 insertions(+), 44 deletions(-)
> > > >  create mode 100644 gcc/deferred-cst.cc
> > > >  create mode 100644 gcc/deferred-cst.h
> > > >
> > > > diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> > > > index 8ecef4ccdc7..e70fe8dc21b 100644
> > > > --- a/gcc/Makefile.in
> > > > +++ b/gcc/Makefile.in
> > > > @@ -1464,6 +1464,7 @@ OBJS = \
> > > >         dce.o \
> > > >         ddg.o \
> > > >         debug.o \
> > > > +       deferred-cst.o \
> > > >         hierarchical_discriminator.o \
> > > >         dep-fusion.o \
> > > >         df-core.o \
> > > > @@ -3163,6 +3164,7 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h 
> > > > $(srcdir)/coretypes.h \
> > > >    $(srcdir)/internal-fn.h \
> > > >    $(srcdir)/calls.cc \
> > > >    $(srcdir)/analyzer/analyzer-language.cc \
> > > > +  $(srcdir)/deferred-cst.cc \
> > > >    @all_gtfiles@
> > > >
> > > >  # Compute the list of GT header files from the corresponding C sources,
> > > > @@ -4173,7 +4175,7 @@ PLUGIN_HEADERS = $(TREE_H) $(CONFIG_H) 
> > > > $(SYSTEM_H) coretypes.h $(TM_H) \
> > > >    lcm.h cfgloopmanip.h file-prefix-map.h builtins.def $(INSN_ATTR_H) \
> > > >    pass-instances.def params.list $(srcdir)/../include/gomp-constants.h 
> > > > \
> > > >    $(EXPR_H) $(srcdir)/analyzer/*.h $(srcdir)/diagnostics/*.h \
> > > > -  $(srcdir)/text-art/*.h $(srcdir)/topics/*.h
> > > > +  $(srcdir)/text-art/*.h $(srcdir)/topics/*.h deferred-cst.h
> > > >
> > > >  # generate the 'build fragment' b-header-vars
> > > >  s-header-vars: Makefile
> > > > diff --git a/gcc/alias.cc b/gcc/alias.cc
> > > > index 6e16b21327f..2d5d672a520 100644
> > > > --- a/gcc/alias.cc
> > > > +++ b/gcc/alias.cc
> > > > @@ -618,6 +618,7 @@ ends_tbaa_access_path_p (const_tree t)
> > > >    switch (TREE_CODE (t))
> > > >      {
> > > >      case COMPONENT_REF:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > >        if (DECL_NONADDRESSABLE_P (TREE_OPERAND (t, 1)))
> > > >         return true;
> > > >        /* Permit type-punning when accessing a union, provided the 
> > > > access
> > > > diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc
> > > > index d437c55285e..a8dae5e7f52 100644
> > > > --- a/gcc/c-family/c-attribs.cc
> > > > +++ b/gcc/c-family/c-attribs.cc
> > > > @@ -193,6 +193,8 @@ static tree 
> > > > handle_null_terminated_string_arg_attribute (tree *, tree, tree, int
> > > >  static tree handle_btf_decl_tag_attribute (tree *, tree, tree, int, 
> > > > bool *);
> > > >  static tree handle_btf_type_tag_attribute (tree *, tree, tree, int, 
> > > > bool *);
> > > >
> > > > +static tree handle_deferred_cst_access_attribute (tree *, tree, tree, 
> > > > int, bool *);
> > > > +
> > > >  /* Helper to define attribute exclusions.  */
> > > >  #define ATTR_EXCL(name, function, type, variable)      \
> > > >    { name, function, type, variable }
> > > > @@ -667,7 +669,9 @@ const struct attribute_spec 
> > > > c_common_gnu_attributes[] =
> > > >    { "btf_type_tag",          1, 1, false, true, false, false,
> > > >                               handle_btf_type_tag_attribute, NULL},
> > > >    { "btf_decl_tag",          1, 1, true, false, false, false,
> > > > -                             handle_btf_decl_tag_attribute, NULL}
> > > > +                             handle_btf_decl_tag_attribute, NULL},
> > > > +  { "deferred_cst_access",    0, 0, false, true, false, false,
> > > > +                             handle_deferred_cst_access_attribute, 
> > > > NULL }
> > > >  };
> > > >
> > > >  const struct scoped_attribute_specs c_common_gnu_attribute_table =
> > > > @@ -5292,6 +5296,30 @@ handle_btf_type_tag_attribute (tree *node, tree 
> > > > name, tree args,
> > > >    return NULL_TREE;
> > > >  }
> > > >
> > > > +// TODO
> > > > +
> > > > +/* Handle the "deferred_cst_access" struct attribute.  */
> > > > +
> > > > +static tree
> > > > +handle_deferred_cst_access_attribute (tree *node,
> > > > +                                     tree name,
> > > > +                                     tree ARG_UNUSED (args),
> > > > +                                     int ARG_UNUSED (flags),
> > > > +                                     bool *no_add_attrs)
> > > > +{
> > > > +  tree type = *node;
> > > > +
> > > > +  if (TREE_CODE (type) != RECORD_TYPE)
> > > > +    {
> > > > +      warning (OPT_Wattributes,
> > > > +             "%qE attribute only applies to struct types",
> > > > +             name);
> > > > +      *no_add_attrs = true;
> > > > +    }
> > > > +
> > > > +  return NULL_TREE;
> > > > +}
> > > > +
> > > >  /* Handle the "nonstring" variable attribute.  */
> > > >
> > > >  static tree
> > > > diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc
> > > > index 5acb221d31f..34a4ea7a0de 100644
> > > > --- a/gcc/c-family/c-common.cc
> > > > +++ b/gcc/c-family/c-common.cc
> > > > @@ -57,6 +57,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >  #include "gcc-rich-location.h"
> > > >  #include "gcc-urlifier.h"
> > > >  #include "diagnostics/diagnostics-selftests.h"
> > > > +#include "deferred-cst.h"
> > > >
> > > >  cpp_reader *parse_in;          /* Declared in c-pragma.h.  */
> > > >
> > > > @@ -7217,13 +7218,32 @@ c_common_to_target_charset (HOST_WIDE_INT c)
> > > >      return uc;
> > > >  }
> > > >
> > > > +/* Helper for fold_offsetof_internal.  Add OFFSET to the ACCUMULATOR
> > > > +   tree.  */
> > > > +static void
> > > > +accumulate_offset (tree *accumulator, tree offset)
> > > > +{
> > > > +  gcc_assert (accumulator);
> > > > +  gcc_assert (offset);
> > > > +
> > > > +  offset = convert (sizetype, offset);
> > > > +
> > > > +  if (*accumulator == NULL_TREE)
> > > > +    *accumulator = offset;
> > > > +  else
> > > > +    *accumulator = size_binop_loc (
> > > > +      input_location, PLUS_EXPR, *accumulator, offset);
> > > > +}
> > > > +
> > > >  /* Fold an offsetof-like expression.  EXPR is a nested sequence of 
> > > > component
> > > >     references with an INDIRECT_REF of a constant at the bottom; much 
> > > > like the
> > > >     traditional rendering of offsetof as a macro.  TYPE is the desired 
> > > > type of
> > > > -   the whole expression.  Return the folded result.  */
> > > > +   the whole expression.  Return the folded result.  NON_CST is set to 
> > > > the
> > > > +   sum of all non-constant portions of the offset.  DEFERRED is set to 
> > > > the
> > > > +   deferred constant portion of the offset.  */
> > > >
> > > > -tree
> > > > -fold_offsetof (tree expr, tree type, enum tree_code ctx)
> > > > +static tree
> > > > +fold_offsetof_internal (tree expr, tree type, enum tree_code ctx, tree 
> > > > *non_cst, tree *deferred)
> > > >  {
> > > >    tree base, off, t;
> > > >    tree_code code = TREE_CODE (expr);
> > > > @@ -7233,10 +7253,10 @@ fold_offsetof (tree expr, tree type, enum 
> > > > tree_code ctx)
> > > >        return expr;
> > > >
> > > >      case REALPART_EXPR:
> > > > -     return fold_offsetof (TREE_OPERAND (expr, 0), type, code);
> > > > +     return fold_offsetof_internal (TREE_OPERAND (expr, 0), type, 
> > > > code, non_cst, deferred);
> > > >
> > > >      case IMAGPART_EXPR:
> > > > -     base = fold_offsetof (TREE_OPERAND (expr, 0), type, code);
> > > > +     base = fold_offsetof_internal (TREE_OPERAND (expr, 0), type, 
> > > > code, non_cst, deferred);
> > > >       if (base == error_mark_node)
> > > >         return base;
> > > >       off = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (TREE_OPERAND (expr, 
> > > > 0))));
> > > > @@ -7261,7 +7281,7 @@ fold_offsetof (tree expr, tree type, enum 
> > > > tree_code ctx)
> > > >        return convert (type, TREE_OPERAND (expr, 0));
> > > >
> > > >      case COMPONENT_REF:
> > > > -      base = fold_offsetof (TREE_OPERAND (expr, 0), type, code);
> > > > +      base = fold_offsetof_internal (TREE_OPERAND (expr, 0), type, 
> > > > code, non_cst, deferred);
> > > >        if (base == error_mark_node)
> > > >         return base;
> > > >
> > > > @@ -7277,8 +7297,24 @@ fold_offsetof (tree expr, tree type, enum 
> > > > tree_code ctx)
> > > >                                       / BITS_PER_UNIT));
> > > >        break;
> > > >
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > > +      base = fold_offsetof_internal (TREE_OPERAND (expr, 0), type, 
> > > > code, non_cst, deferred);
> > > > +      if (base == error_mark_node)
> > > > +       return base;
> > > > +
> > > > +      t = TREE_OPERAND (expr, 1);
> > > > +      if (DECL_C_BIT_FIELD (t))
> > > > +       {
> > > > +         error_at (EXPR_LOCATION (expr),
> > > > +                   "attempt to take address of bit-field structure 
> > > > member");
> > > > +         return error_mark_node;
> > > > +       }
> > > > +      t = deferred_cst_call_ifn_field_offset (EXPR_LOCATION (expr), t);
> > > > +      accumulate_offset (deferred, t);
> > > > +      return base;
> > > > +
> > > >      case ARRAY_REF:
> > > > -      base = fold_offsetof (TREE_OPERAND (expr, 0), type, code);
> > > > +      base = fold_offsetof_internal (TREE_OPERAND (expr, 0), type, 
> > > > code, non_cst, deferred);
> > > >        if (base == error_mark_node)
> > > >         return base;
> > > >
> > > > @@ -7330,13 +7366,26 @@ fold_offsetof (tree expr, tree type, enum 
> > > > tree_code ctx)
> > > >
> > > >        t = convert (sizetype, t);
> > > >        off = size_binop (MULT_EXPR, TYPE_SIZE_UNIT (TREE_TYPE (expr)), 
> > > > t);
> > > > +
> > > > +      /* For classification as cst/non-cst, the array index has to be
> > > > +         fully folded.  */
> > > > +      off = c_fully_fold(off, false, NULL);
> > > > +
> > > > +      if (off == error_mark_node)
> > > > +       return off;
> > > > +
> > > > +      if (!TREE_CONSTANT (off))
> > > > +       {
> > > > +         accumulate_offset (non_cst, off);
> > > > +         return base;
> > > > +       }
> > > >        break;
> > > >
> > > >      case COMPOUND_EXPR:
> > > >        /* Handle static members of volatile structs.  */
> > > >        t = TREE_OPERAND (expr, 1);
> > > >        gcc_checking_assert (VAR_P (get_base_address (t)));
> > > > -      return fold_offsetof (t, type);
> > > > +      return fold_offsetof_internal (t, type, ERROR_MARK, non_cst, 
> > > > deferred);
> > > >
> > > >      default:
> > > >        gcc_unreachable ();
> > > > @@ -7346,6 +7395,48 @@ fold_offsetof (tree expr, tree type, enum 
> > > > tree_code ctx)
> > > >      return size_binop (PLUS_EXPR, base, convert (type, off));
> > > >    return fold_build_pointer_plus (base, off);
> > > >  }
> > > > +
> > > > +/* Like fold_offsetof_internal, but integrates deferred constant 
> > > > components
> > > > +   into the result.  If such components exist, the result is wrapped 
> > > > in a
> > > > +   DEFERRED_CST_EXPR node.  */
> > > > +
> > > > +tree
> > > > +fold_offsetof (tree expr, tree type, enum tree_code ctx)
> > > > +{
> > > > +  tree res = NULL_TREE;
> > > > +  tree non_cst = NULL_TREE;
> > > > +  tree deferred = NULL_TREE;
> > > > +  tree cst = fold_offsetof_internal (expr, type, ctx, &non_cst, 
> > > > &deferred);
> > > > +
> > > > +  res = cst;
> > > > +
> > > > +  if (res == error_mark_node)
> > > > +    return res;
> > > > +
> > > > +  /* DEFERRED_CST_EXPRs contain only ordinary constants and deferred
> > > > +     constant components.  */
> > > > +  if (deferred != NULL_TREE)
> > > > +    {
> > > > +      if (POINTER_TYPE_P (type))
> > > > +       res = fold_build_pointer_plus (res, convert (sizetype, 
> > > > deferred));
> > > > +      else
> > > > +       res = size_binop (PLUS_EXPR, res, convert (type, deferred));
> > > > +
> > > > +      res = deferred_cst_build_expr (res);
> > > > +    }
> > > > +
> > > > +  /* Runtime-dependent terms are added only after the deferred
> > > > +     boundary has been constructed.  */
> > > > +  if (non_cst != NULL_TREE)
> > > > +    {
> > > > +      if (POINTER_TYPE_P (type))
> > > > +       res = fold_build_pointer_plus (res, convert (type, non_cst));
> > > > +      else
> > > > +       res = size_binop (PLUS_EXPR, res, convert (type, non_cst));
> > > > +    }
> > > > +
> > > > +  return res;
> > > > +}
> > > >
> > > >  /* *PTYPE is an incomplete array.  Complete it with a domain based on
> > > >     INITIAL_VALUE.  If INITIAL_VALUE is not present, use 1 if DO_DEFAULT
> > > > diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc
> > > > index 8a894de3a1b..62da65e0efb 100644
> > > > --- a/gcc/c/c-decl.cc
> > > > +++ b/gcc/c/c-decl.cc
> > > > @@ -62,6 +62,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >  #include "omp-offload.h"  /* For offload_vars.  */
> > > >  #include "c-parser.h"
> > > >  #include "gcc-urlifier.h"
> > > > +#include "deferred-cst.h"
> > > >
> > > >  #include "tree-pretty-print.h"
> > > >
> > > > diff --git a/gcc/c/c-fold.cc b/gcc/c/c-fold.cc
> > > > index 122049daf87..f333a33153c 100644
> > > > --- a/gcc/c/c-fold.cc
> > > > +++ b/gcc/c/c-fold.cc
> > > > @@ -26,9 +26,11 @@ along with GCC; see the file COPYING3.  If not see
> > > >  #include "c-tree.h"
> > > >  #include "intl.h"
> > > >  #include "gimplify.h"
> > > > +#include "deferred-cst.h"
> > > >
> > > >  static tree c_fully_fold_internal (tree expr, bool, bool *, bool *, 
> > > > bool,
> > > >                                    bool);
> > > > +static tree c_fully_fold_deferred_cst_internal (tree expr);
> > > >
> > > >  /* If DISABLE is true, stop issuing warnings.  This is used when
> > > >     parsing code that we know will not be executed.  This function may
> > > > @@ -125,6 +127,7 @@ c_fully_fold (tree expr, bool in_init, bool 
> > > > *maybe_const, bool lval)
> > > >      }
> > > >    ret = c_fully_fold_internal (expr, in_init, maybe_const,
> > > >                                &maybe_const_itself, false, lval);
> > > > +  ret = c_fully_fold_deferred_cst_internal (ret);
> > > >    if (eptype)
> > > >      ret = fold_convert_loc (loc, eptype, ret);
> > > >    *maybe_const &= maybe_const_itself;
> > > > @@ -258,14 +261,22 @@ c_fully_fold_internal (tree expr, bool in_init, 
> > > > bool *maybe_const_operands,
> > > >        goto out;
> > > >
> > > >      case COMPONENT_REF:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > >        orig_op0 = op0 = TREE_OPERAND (expr, 0);
> > > >        op1 = TREE_OPERAND (expr, 1);
> > > > -      op2 = TREE_OPERAND (expr, 2);
> > > > +      if (code == COMPONENT_REF)
> > > > +       op2 = TREE_OPERAND (expr, 2);
> > > >        op0 = c_fully_fold_internal (op0, in_init, maybe_const_operands,
> > > >                                    maybe_const_itself, for_int_const, 
> > > > lval);
> > > >        STRIP_TYPE_NOPS (op0);
> > > >        if (op0 != orig_op0)
> > > > -       ret = build3 (COMPONENT_REF, TREE_TYPE (expr), op0, op1, op2);
> > > > +       {
> > > > +         if (code == COMPONENT_REF)
> > > > +           ret = build3 (COMPONENT_REF, TREE_TYPE (expr), op0, op1, 
> > > > op2);
> > > > +         else
> > > > +           ret = deferred_cst_build_component_ref (TREE_TYPE (expr),
> > > > +                                                   op0, op1);
> > > > +       }
> > > >        if (ret != expr)
> > > >         {
> > > >           TREE_READONLY (ret) = TREE_READONLY (expr);
> > > > @@ -720,6 +731,148 @@ c_fully_fold_internal (tree expr, bool in_init, 
> > > > bool *maybe_const_operands,
> > > >    return ret;
> > > >  }
> > > >
> > > > +/* Internal helper for c_fully_fold.  Fold surrounding constant
> > > > +   operations into DEFERRED_CST_EXPR nodes.
> > > > +
> > > > +   An operation is absorbed when:
> > > > +
> > > > +     - its tree code is explicitly supported by
> > > > +       deferred_cst_operator_p;
> > > > +     - every operand is constant-like; and
> > > > +     - at least one operand is a DEFERRED_CST_EXPR.
> > > > +
> > > > +   Existing deferred wrappers are removed when an operation is 
> > > > absorbed,
> > > > +   giving a single outer DEFERRED_CST_EXPR.  For example:
> > > > +
> > > > +       DEFERRED_CST_EXPR (A) + 1
> > > > +        -> DEFERRED_CST_EXPR (A + 1)
> > > > +
> > > > +       (DEFERRED_CST_EXPR (A) + 1) * 2
> > > > +        -> DEFERRED_CST_EXPR ((A + 1) * 2)
> > > > +
> > > > +       DEFERRED_CST_EXPR (A) + DEFERRED_CST_EXPR (B)
> > > > +        -> DEFERRED_CST_EXPR (A + B)
> > > > +
> > > > +       DEFERRED_CST_EXPR (C) ? 1 : 2
> > > > +        -> DEFERRED_CST_EXPR (C ? 1 : 2)
> > > > +
> > > > +   An operation involving a nonconstant operand is not absorbed:
> > > > +
> > > > +       X + DEFERRED_CST_EXPR (A)
> > > > +        -> X + DEFERRED_CST_EXPR (A)  */
> > > > +
> > > > +static tree
> > > > +c_fully_fold_deferred_cst_internal (tree expr)
> > > > +{
> > > > +  if (expr == NULL_TREE)
> > > > +    return NULL_TREE;
> > > > +
> > > > +  enum tree_code code = TREE_CODE (expr);
> > > > +
> > > > +  /* The true constants inside deferred constants can be folded here,
> > > > +     while the deferred components are represented as calls to
> > > > +     internal functions and thus can not and should not be folded
> > > > +     here.  */
> > > > +  if (code == DEFERRED_CST_EXPR)
> > > > +  {
> > > > +    tree old_inner = TREE_OPERAND (expr, 0);
> > > > +
> > > > +    bool maybe_const_operands = true;
> > > > +    bool maybe_const_itself = true;
> > > > +
> > > > +    tree inner
> > > > +      = c_fully_fold_internal (old_inner, false,
> > > > +                              &maybe_const_operands,
> > > > +                              &maybe_const_itself,
> > > > +                              false, false);
> > > > +
> > > > +    if (inner == old_inner)
> > > > +      return expr;
> > > > +
> > > > +    return deferred_cst_build_expr (inner);
> > > > +  }
> > > > +
> > > > +  /* Unsupported operations form a boundary for deferred folding.  */
> > > > +  if (!deferred_cst_operator_p (code))
> > > > +    return expr;
> > > > +
> > > > +  int nops = TREE_OPERAND_LENGTH (expr);
> > > > +
> > > > +  /* Every currently-supported operator has at most three operands.  */
> > > > +  gcc_assert (nops >= 1 && nops <= 3);
> > > > +
> > > > +  tree ops[3];
> > > > +  bool changed = false;
> > > > +  bool has_deferred = false;
> > > > +  bool all_constant = true;
> > > > +  bool has_side_effects = false;
> > > > +
> > > > +  for (int i = 0; i < nops; ++i)
> > > > +    {
> > > > +      tree old_op = TREE_OPERAND (expr, i);
> > > > +      tree op = c_fully_fold_deferred_cst_internal (old_op);
> > > > +
> > > > +      ops[i] = op;
> > > > +
> > > > +      if (op != old_op)
> > > > +       changed = true;
> > > > +
> > > > +      if (TREE_CODE (op) == DEFERRED_CST_EXPR)
> > > > +       has_deferred = true;
> > > > +      else if (!TREE_CONSTANT (op))
> > > > +       all_constant = false;
> > > > +
> > > > +      if (TREE_SIDE_EFFECTS (op))
> > > > +       has_side_effects = true;
> > > > +    }
> > > > +
> > > > +  /* There is no deferred expression directly participating in this
> > > > +     operation, or there are non-constant operands or operands with
> > > > +     side effects involved, so the operation itself must not become
> > > > +     deferred.
> > > > +
> > > > +     We may nevertheless need to propagate a transformation made in one
> > > > +     of the operands.  */
> > > > +  if (!has_deferred || !all_constant || has_side_effects)
> > > > +    {
> > > > +      if (!changed)
> > > > +       return expr;
> > > > +
> > > > +      tree copy = copy_node (expr);
> > > > +      for (int i = 0; i < nops; i++)
> > > > +       TREE_OPERAND (copy, i) = ops[i];
> > > > +
> > > > +      return copy;
> > > > +    }
> > > > +
> > > > +  /* Every operand is constant-like and at least one operand is
> > > > +     deferred.  Move this complete operation inside one deferred
> > > > +     wrapper.  */
> > > > +
> > > > +  tree inner = copy_node (expr);
> > > > +  for (int i = 0; i < nops; i++)
> > > > +    {
> > > > +      bool op_deferred_cst = ops[i] != NULL_TREE
> > > > +                            && TREE_CODE (ops[i]) == DEFERRED_CST_EXPR;
> > > > +      TREE_OPERAND (inner, i) = op_deferred_cst
> > > > +                               ? TREE_OPERAND (ops[i], 0) : ops[i];
> > > > +    }
> > > > +
> > > > +  bool maybe_const_operands = true;
> > > > +  bool maybe_const_itself = true;
> > > > +
> > > > +  /* The true constants inside deferred constants can be folded here,
> > > > +     while the deferred components are represented as calls to
> > > > +     internal functions and thus can not and should not be folded
> > > > +     here.  */
> > > > +  inner = c_fully_fold_internal (inner, false,
> > > > +                                &maybe_const_operands,
> > > > +                                &maybe_const_itself,
> > > > +                                false, false);
> > > > +
> > > > +  return deferred_cst_build_expr (inner);
> > > > +}
> > > > +
> > > >  /* Fold X for consideration by one of the warning functions when 
> > > > checking
> > > >     whether an expression has a constant value.  */
> > > >
> > > > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc
> > > > index aa368a6e95a..4d9c0179b29 100644
> > > > --- a/gcc/c/c-typeck.cc
> > > > +++ b/gcc/c/c-typeck.cc
> > > > @@ -55,6 +55,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >  #include "realmpfr.h"
> > > >  #include "tree-pretty-print-markup.h"
> > > >  #include "gcc-urlifier.h"
> > > > +#include "deferred-cst.h"
> > > >
> > > >  /* Possible cases of implicit conversions.  Used to select diagnostic 
> > > > messages
> > > >     and control folding initializers in convert_for_assignment.  */
> > > > @@ -2290,6 +2291,19 @@ c_size_in_bytes (const_tree type)
> > > >                                    / BITS_PER_UNIT));
> > > >  }
> > > >
> > > > +/* Return true if INIT is a valid deferred constant initializer.  */
> > > > +static bool
> > > > +deferred_constant_initializer_p (tree init)
> > > > +{
> > > > +  if (!flag_deferred_cst_static
> > > > +      || !init
> > > > +      || !deferred_cst_present_p (init))
> > > > +    return false;
> > > > +
> > > > +  return initializer_constant_valid_p (init, TREE_TYPE (init))
> > > > +        != NULL_TREE;
> > > > +}
> > > > +
> > > >  /* Return either DECL or its known constant value (if it has one).  */
> > > >
> > > >  tree
> > > > @@ -2304,7 +2318,8 @@ decl_constant_value_1 (tree decl, bool in_init)
> > > >        /* This is invalid if initial value is not constant.
> > > >          If it has either a function call, a memory reference,
> > > >          or a variable, then re-evaluating it could give different 
> > > > results.  */
> > > > -      && TREE_CONSTANT (DECL_INITIAL (decl))
> > > > +      && (TREE_CONSTANT (DECL_INITIAL (decl))
> > > > +         || deferred_constant_initializer_p (DECL_INITIAL (decl)))
> > > >        /* Check for cases where this is sub-optimal, even though valid. 
> > > >  */
> > > >        && (in_init || TREE_CODE (DECL_INITIAL (decl)) != CONSTRUCTOR))
> > > >      return DECL_INITIAL (decl);
> > > > @@ -3199,9 +3214,14 @@ build_counted_by_ref (tree datum, tree subdatum,
> > > >        /* Get the TYPE of the counted_by field.  */
> > > >        *counted_by_type = TREE_TYPE (counted_by_subdatum);
> > > >
> > > > -      counted_by_ref
> > > > -       = build3 (COMPONENT_REF, TREE_TYPE (counted_by_subdatum),
> > > > -                 datum, counted_by_subdatum, NULL_TREE);
> > > > +      if (deferred_cst_field_p (counted_by_subdatum))
> > > > +       counted_by_ref
> > > > +         = deferred_cst_build_component_ref (TREE_TYPE 
> > > > (counted_by_subdatum),
> > > > +                                             datum, 
> > > > counted_by_subdatum);
> > > > +      else
> > > > +       counted_by_ref
> > > > +         = build3 (COMPONENT_REF, TREE_TYPE (counted_by_subdatum),
> > > > +                   datum, counted_by_subdatum, NULL_TREE);
> > > >
> > > >        datum = counted_by_ref;
> > > >        counted_by_field = TREE_CHAIN (counted_by_field);
> > > > @@ -3422,8 +3442,11 @@ build_component_ref (location_t loc, tree datum, 
> > > > tree component,
> > > >             quals |= TYPE_QUALS (TREE_TYPE (datum));
> > > >           subtype = c_build_qualified_type (TREE_TYPE (subdatum), 
> > > > quals);
> > > >
> > > > -         ref = build3 (COMPONENT_REF, subtype, datum, subdatum,
> > > > -                       NULL_TREE);
> > > > +         if (deferred_cst_field_p (subdatum))
> > > > +           ref = deferred_cst_build_component_ref (subtype, datum, 
> > > > subdatum);
> > > > +         else
> > > > +           ref = build3 (COMPONENT_REF, subtype, datum, subdatum,
> > > > +                         NULL_TREE);
> > > >           SET_EXPR_LOCATION (ref, loc);
> > > >
> > > >           check_counted_by_attribute (loc, ref);
> > > > @@ -6364,6 +6387,7 @@ lvalue_p (const_tree ref)
> > > >      case REALPART_EXPR:
> > > >      case IMAGPART_EXPR:
> > > >      case COMPONENT_REF:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > >        return lvalue_p (TREE_OPERAND (ref, 0));
> > > >
> > > >      case C_MAYBE_CONST_EXPR:
> > > > @@ -10209,7 +10233,9 @@ digest_init (location_t init_loc, tree decl, 
> > > > tree type, tree init,
> > > >        /* Check to see if we have already given an error message.  */
> > > >        if (inside_init == error_mark_node)
> > > >         ;
> > > > -      else if (require_constant && !TREE_CONSTANT (inside_init))
> > > > +      else if (require_constant
> > > > +              && !TREE_CONSTANT (inside_init)
> > > > +              && !deferred_constant_initializer_p (inside_init))
> > > >         {
> > > >           error_init (init_loc, "initializer element is not constant");
> > > >           inside_init = error_mark_node;
> > > > diff --git a/gcc/common.opt b/gcc/common.opt
> > > > index 5d0e255a40d..648b0cb193f 100644
> > > > --- a/gcc/common.opt
> > > > +++ b/gcc/common.opt
> > > > @@ -3675,6 +3675,14 @@ Enum(zero_init_padding_bits_kind) String(unions) 
> > > > Value(ZERO_INIT_PADDING_BITS_UN
> > > >  EnumValue
> > > >  Enum(zero_init_padding_bits_kind) String(all) 
> > > > Value(ZERO_INIT_PADDING_BITS_ALL)
> > > >
> > > > +fdeferred-cst-static
> > > > +Common Var(flag_deferred_cst_static) Init(0)
> > > > +Allow deferred constants to appear in static data.
> > > > +
> > > > +fdeferred-cst-barrier-hard
> > > > +Common Var(flag_deferred_cst_barrier_hard) Init(0)
> > > > +Make deferred-cst-barrier ICE if deferred constants remain.
> > > > +
> > > >  g
> > > >  Common Driver RejectNegative JoinedOrMissing
> > > >  Generate debug information in default format.
> > > > diff --git a/gcc/deferred-cst.cc b/gcc/deferred-cst.cc
> > > > new file mode 100644
> > > > index 00000000000..bec14508484
> > > > --- /dev/null
> > > > +++ b/gcc/deferred-cst.cc
> > > > @@ -0,0 +1,626 @@
> > > > +#include "config.h"
> > > > +#include "system.h"
> > > > +#include "coretypes.h"
> > > > +#include "tree.h"
> > > > +#include "stringpool.h"
> > > > +#include "fold-const.h"
> > > > +#include "internal-fn.h"
> > > > +#include "attribs.h"
> > > > +#include "tree-pass.h"
> > > > +#include "context.h"
> > > > +#include "cgraph.h"
> > > > +#include "function.h"
> > > > +#include "cfg.h"
> > > > +#include "basic-block.h"
> > > > +#include "gimple.h"
> > > > +#include "gimple-iterator.h"
> > > > +#include "gimple-walk.h"
> > > > +#include "diagnostic.h"
> > > > +
> > > > +#include "deferred-cst.h"
> > > > +
> > > > +/* Weak mapping from STRING_CST tokens used as arguments to
> > > > +   IFN_DEFERRED_CST_FIELD_OFFSET to the FIELD_DECL whose byte offset
> > > > +   the call represents.
> > > > +
> > > > +   For an ordinary field the value is that field's FIELD_DECL.
> > > > +
> > > > +   For a bit-field the value is DECL_BIT_FIELD_REPRESENTATIVE (field),
> > > > +   since IFN_DEFERRED_CST_FIELD_OFFSET describes the byte offset of
> > > > +   the whole bit-field representative rather than the individual
> > > > +   field.  */
> > > > +static GTY((cache)) tree_cache_map *field_offset_field_decls;
> > > > +
> > > > +/* Used only to make STRING_CST contents unique.  The actual string 
> > > > value has
> > > > +   no semantic meaning; the STRING_CST node itself is the metadata 
> > > > token.  */
> > > > +static unsigned HOST_WIDE_INT next_field_offset_token = 0;
> > > > +
> > > > +/* Return true if TYPE is a record carrying
> > > > +   __attribute__((deferred_cst_access)).  */
> > > > +bool
> > > > +deferred_cst_access_type_p (tree type)
> > > > +{
> > > > +  return (type
> > > > +         && TREE_CODE (type) == RECORD_TYPE
> > > > +         && lookup_attribute ("deferred_cst_access",
> > > > +                              TYPE_ATTRIBUTES (type)) != NULL_TREE);
> > > > +}
> > > > +
> > > > +bool
> > > > +deferred_cst_field_p (tree field)
> > > > +{
> > > > +  if (!field || TREE_CODE (field) != FIELD_DECL)
> > > > +    return false;
> > > > +
> > > > +  return deferred_cst_access_type_p (DECL_CONTEXT (field));
> > > > +}
> > > > +
> > > > +/* Return true if CODE is an operation which may be absorbed into a
> > > > +   DEFERRED_CST_EXPR.
> > > > +
> > > > +   This is deliberately an explicit whitelist.  In particular, do not
> > > > +   absorb arbitrary unary/binary/reference tree codes merely because 
> > > > all
> > > > +   of their operands happen to be constant.  */
> > > > +bool
> > > > +deferred_cst_operator_p (enum tree_code code)
> > > > +{
> > > > +  switch (code)
> > > > +    {
> > > > +    /* Binary arithmetic operations.  */
> > > > +    case PLUS_EXPR:
> > > > +    case MINUS_EXPR:
> > > > +    case MULT_EXPR:
> > > > +    case POINTER_PLUS_EXPR:
> > > > +    case POINTER_DIFF_EXPR:
> > > > +    case TRUNC_DIV_EXPR:
> > > > +    case CEIL_DIV_EXPR:
> > > > +    case FLOOR_DIV_EXPR:
> > > > +    case TRUNC_MOD_EXPR:
> > > > +    case RDIV_EXPR:
> > > > +    case EXACT_DIV_EXPR:
> > > > +    case LSHIFT_EXPR:
> > > > +    case RSHIFT_EXPR:
> > > > +    case LROTATE_EXPR:
> > > > +    case RROTATE_EXPR:
> > > > +    case BIT_IOR_EXPR:
> > > > +    case BIT_XOR_EXPR:
> > > > +    case BIT_AND_EXPR:
> > > > +      return true;
> > > > +
> > > > +    /* Comparisons.  */
> > > > +    case LT_EXPR:
> > > > +    case LE_EXPR:
> > > > +    case GT_EXPR:
> > > > +    case GE_EXPR:
> > > > +    case EQ_EXPR:
> > > > +    case NE_EXPR:
> > > > +    case UNORDERED_EXPR:
> > > > +    case ORDERED_EXPR:
> > > > +    case UNLT_EXPR:
> > > > +    case UNLE_EXPR:
> > > > +    case UNGT_EXPR:
> > > > +    case UNGE_EXPR:
> > > > +    case UNEQ_EXPR:
> > > > +      return true;
> > > > +
> > > > +    /* Boolean operations.
> > > > +
> > > > +       TRUTH_ANDIF_EXPR and TRUTH_ORIF_EXPR retain their short-circuit
> > > > +       semantics inside the deferred expression.  Nothing is evaluated
> > > > +       when absorbing expressions into deferred constants.  */
> > > > +    case TRUTH_AND_EXPR:
> > > > +    case TRUTH_OR_EXPR:
> > > > +    case TRUTH_XOR_EXPR:
> > > > +    case TRUTH_ANDIF_EXPR:
> > > > +    case TRUTH_ORIF_EXPR:
> > > > +      return true;
> > > > +
> > > > +    /* Unary arithmetic and conversions.  */
> > > > +    case FIX_TRUNC_EXPR:
> > > > +    case FLOAT_EXPR:
> > > > +    CASE_CONVERT:
> > > > +    case ADDR_SPACE_CONVERT_EXPR:
> > > > +    case NON_LVALUE_EXPR:
> > > > +    case NEGATE_EXPR:
> > > > +    case BIT_NOT_EXPR:
> > > > +    case TRUTH_NOT_EXPR:
> > > > +    case CONJ_EXPR:
> > > > +    case REALPART_EXPR:
> > > > +    case IMAGPART_EXPR:
> > > > +    case VIEW_CONVERT_EXPR:
> > > > +    case PAREN_EXPR:
> > > > +      return true;
> > > > +
> > > > +    case COMPLEX_EXPR:
> > > > +      return true;
> > > > +
> > > > +    /* Conditional expressions.  These are absorbed only when the
> > > > +       condition and both alternatives are all constant-like.  */
> > > > +    case COND_EXPR:
> > > > +    case VEC_COND_EXPR:
> > > > +      return true;
> > > > +
> > > > +    default:
> > > > +      return false;
> > > > +    }
> > > > +}
> > > > +
> > > > +tree
> > > > +deferred_cst_build_component_ref (tree type, tree base, tree field)
> > > > +{
> > > > +  gcc_assert (field);
> > > > +  gcc_assert (TREE_CODE (field) == FIELD_DECL);
> > > > +
> > > > +  return build2 (DEFERRED_CST_COMPONENT_REF, type, base, field);
> > > > +}
> > > > +
> > > > +tree
> > > > +deferred_cst_build_expr (tree expr)
> > > > +{
> > > > +  /* TODO assert that expr is actually a deferred constant  */
> > > > +  return build1_loc (EXPR_LOCATION (expr), DEFERRED_CST_EXPR,
> > > > +                    TREE_TYPE (expr), expr);
> > > > +}
> > > > +
> > > > +/* Create a unique STRING_CST to use as a metadata token.  */
> > > > +static tree
> > > > +make_token (const char *name, unsigned HOST_WIDE_INT id)
> > > > +{
> > > > +  char buf[128];
> > > > +
> > > > +  int len = snprintf (buf, sizeof (buf),
> > > > +                     "__deferred_cst_token_%s_"
> > > > +                     HOST_WIDE_INT_PRINT_UNSIGNED,
> > > > +                     name, id);
> > > > +
> > > > +  gcc_assert (len > 0);
> > > > +  gcc_assert ((unsigned int) len < sizeof (buf));
> > > > +
> > > > +  /* Include the trailing NUL.  */
> > > > +  unsigned int string_len = len + 1;
> > > > +
> > > > +  tree token = build_string (string_len, buf);
> > > > +
> > > > +  TREE_TYPE (token)
> > > > +    = build_array_type_nelts (char_type_node, string_len);
> > > > +
> > > > +  return token;
> > > > +}
> > > > +
> > > > +/* Generate a new key token into field_offset_field_decls and store
> > > > +   the FIELD_DECL FIELD under it.  Return the new token.  */
> > > > +static tree
> > > > +register_field_offset_field_decl (tree field)
> > > > +{
> > > > +  tree token = make_token ("field_offset", next_field_offset_token++);
> > > > +
> > > > +  if (!field_offset_field_decls)
> > > > +    field_offset_field_decls = tree_cache_map::create_ggc ();
> > > > +
> > > > +  bool existed = field_offset_field_decls->put (token, field);
> > > > +
> > > > +  /* Every token created above is unique.  */
> > > > +  gcc_assert (!existed);
> > > > +
> > > > +  return token;
> > > > +}
> > > > +
> > > > +/* Look up the field declaration associated with a token argument of
> > > > +   IFN_DEFERRED_CST_FIELD_OFFSET.  */
> > > > +static tree
> > > > +lookup_field_offset_field_decl (tree token)
> > > > +{
> > > > +  if (!token)
> > > > +    return NULL_TREE;
> > > > +
> > > > +  tree *slot
> > > > +    = field_offset_field_decls
> > > > +      ? field_offset_field_decls->get (token)
> > > > +      : NULL;
> > > > +
> > > > +  return slot ? *slot : NULL_TREE;
> > > > +}
> > > > +
> > > > +/* Create a call to IFN_DEFERRED_CST_FIELD_OFFSET for the physical,
> > > > +   addressable FIELD whose byte offset is deferred.
> > > > +
> > > > +   FIELD must not be a bit-field.  Callers handling bit-fields must 
> > > > pass
> > > > +   DECL_BIT_FIELD_REPRESENTATIVE (field).
> > > > +
> > > > +   Internally, these call hold the field declarations in a side-map
> > > > +   keyed by tokens used as call arguments.  */
> > > > +tree
> > > > +deferred_cst_call_ifn_field_offset (location_t loc, tree field)
> > > > +{
> > > > +  gcc_assert (field);
> > > > +  gcc_assert (TREE_CODE (field) == FIELD_DECL);
> > > > +  gcc_assert (!DECL_BIT_FIELD (field));
> > > > +
> > > > +  tree token = register_field_offset_field_decl (field);
> > > > +
> > > > +  return build_call_expr_internal_loc (loc,
> > > > +                                      IFN_DEFERRED_CST_FIELD_OFFSET,
> > > > +                                      sizetype, 1, token);
> > > > +}
> > > > +
> > > > +tree
> > > > +deferred_cst_ifn_field_offset_field (tree token)
> > > > +{
> > > > +  if (!token)
> > > > +    return NULL_TREE;
> > > > +
> > > > +  return lookup_field_offset_field_decl (token);
> > > > +}
> > > > +
> > > > +static tree
> > > > +find_deferred_cst_node (tree *tp, int *walk_subtrees, void *)
> > > > +{
> > > > +  tree node = *tp;
> > > > +
> > > > +  if (!node)
> > > > +    return NULL_TREE;
> > > > +
> > > > +  switch (TREE_CODE (node))
> > > > +    {
> > > > +    case DEFERRED_CST_EXPR:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > > +      *walk_subtrees = 0;
> > > > +      return node;
> > > > +
> > > > +    default:
> > > > +      break;
> > > > +    }
> > > > +
> > > > +  /* Declarations and types are metadata here. Do not walk into their
> > > > +     contexts, types, or initializers.  */
> > > > +  if (DECL_P (node) || TYPE_P (node))
> > > > +    *walk_subtrees = 0;
> > > > +
> > > > +  return NULL_TREE;
> > > > +}
> > > > +
> > > > +bool
> > > > +deferred_cst_present_p (tree expr)
> > > > +{
> > > > +  if (!expr)
> > > > +    return false;
> > > > +
> > > > +  tree root = expr;
> > > > +
> > > > +  return walk_tree_without_duplicates (
> > > > +          &root,
> > > > +          find_deferred_cst_node,
> > > > +          NULL)
> > > > +        != NULL_TREE;
> > > > +}
> > > > +
> > > > +/* Replace deferred cst components under DEFERRED_CST_EXPR nodes with
> > > > +   their compile-time values.
> > > > +
> > > > +   Calls to IFN_DEFERRED_CST_FIELD_OFFSET become the compile-time
> > > > +   field offsets.
> > > > +
> > > > +   Return NULL_TREE if no operation took place.  ICE if a substitution
> > > > +   failed.  */
> > > > +static tree
> > > > +substitute_deferred_cst_component (tree expr)
> > > > +{
> > > > +  if (!expr)
> > > > +    return NULL_TREE;
> > > > +
> > > > +  location_t loc = EXPR_LOCATION (expr);
> > > > +
> > > > +  if (TREE_CODE (expr) == CALL_EXPR)
> > > > +    {
> > > > +      if (CALL_EXPR_IFN (expr) == IFN_DEFERRED_CST_FIELD_OFFSET)
> > > > +       {
> > > > +         gcc_assert (call_expr_nargs (expr) == 1);
> > > > +         tree field
> > > > +           = deferred_cst_ifn_field_offset_field (
> > > > +                           CALL_EXPR_ARG (expr, 0));
> > > > +
> > > > +         gcc_assert (field && TREE_CODE (field) == FIELD_DECL);
> > > > +
> > > > +         tree offset = byte_position (field);
> > > > +         return fold_convert_loc (loc, TREE_TYPE (expr), offset);
> > > > +       }
> > > > +    }
> > > > +
> > > > +  return NULL_TREE;
> > > > +}
> > > > +
> > > > +/* Replace all deferred cst components with their compile-time values
> > > > +   and return the fully folded result.  */
> > > > +static tree
> > > > +substitute_deferred_cst_components (tree expr)
> > > > +{
> > > > +  if (CONSTANT_CLASS_P (expr))
> > > > +    return expr;
> > > > +
> > > > +  tree deferred_component = substitute_deferred_cst_component (expr);
> > > > +  if (deferred_component)
> > > > +    return deferred_component;
> > > > +
> > > > +  gcc_assert (deferred_cst_operator_p (TREE_CODE (expr)));
> > > > +
> > > > +  switch (TREE_OPERAND_LENGTH (expr))
> > > > +    {
> > > > +    case 1:
> > > > +      {
> > > > +       tree op0
> > > > +         = substitute_deferred_cst_components (TREE_OPERAND (expr, 0));
> > > > +
> > > > +       return fold_build1_loc (EXPR_LOCATION (expr),
> > > > +                               TREE_CODE (expr),
> > > > +                               TREE_TYPE (expr),
> > > > +                               op0);
> > > > +      }
> > > > +
> > > > +    case 2:
> > > > +      {
> > > > +       tree op0
> > > > +         = substitute_deferred_cst_components (TREE_OPERAND (expr, 0));
> > > > +       tree op1
> > > > +         = substitute_deferred_cst_components (TREE_OPERAND (expr, 1));
> > > > +
> > > > +       return fold_build2_loc (EXPR_LOCATION (expr),
> > > > +                               TREE_CODE (expr),
> > > > +                               TREE_TYPE (expr),
> > > > +                               op0, op1);
> > > > +      }
> > > > +
> > > > +    case 3:
> > > > +      {
> > > > +       tree op0
> > > > +         = substitute_deferred_cst_components (TREE_OPERAND (expr, 0));
> > > > +       tree op1
> > > > +         = substitute_deferred_cst_components (TREE_OPERAND (expr, 1));
> > > > +       tree op2
> > > > +         = substitute_deferred_cst_components (TREE_OPERAND (expr, 2));
> > > > +
> > > > +       return fold_build3_loc (EXPR_LOCATION (expr),
> > > > +                               TREE_CODE (expr),
> > > > +                               TREE_TYPE (expr),
> > > > +                               op0, op1, op2);
> > > > +      }
> > > > +
> > > > +    default:
> > > > +      gcc_unreachable ();
> > > > +    }
> > > > +}
> > > > +
> > > > +/* Replace all deferred components in EXPR with their compile-time
> > > > +   values and evaluate the result.  Return an actual constant.  */
> > > > +static tree
> > > > +evaluate_deferred_cst_now (tree expr)
> > > > +{
> > > > +  tree result = substitute_deferred_cst_components (expr);
> > > > +  result = fold (result);
> > > > +
> > > > +  if (!CONSTANT_CLASS_P (result))
> > > > +    internal_error ("invalid compile-time value of deferred constant");
> > > > +
> > > > +  return result;
> > > > +}
> > > > +
> > > > +/* Returns true if the given tree node may not exist past the deferred
> > > > +   constant barrier pass.  */
> > > > +static bool
> > > > +deferred_cst_barrier_applies (tree node)
> > > > +{
> > > > +  if (!node)
> > > > +    return false;
> > > > +
> > > > +  switch (TREE_CODE (node))
> > > > +    {
> > > > +    case DEFERRED_CST_EXPR:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > > +      return true;
> > > > +
> > > > +    default:
> > > > +      return false;
> > > > +    }
> > > > +}
> > > > +
> > > > +/* On the deferred constant barrier pass, return the replacement for
> > > > +   a tree node that may not exist beyond it.
> > > > +
> > > > +   DEFERRED_CST_COMPONENT_REFs are simpy replaced with a standard
> > > > +   COMPONENT_REF node.
> > > > +
> > > > +   All deferred components under a DEFERRED_CST_EXPR are replaced with
> > > > +   their actual compile-time values.  Afterwards, the expression is
> > > > +   fully evaluated to replace NODE.  */
> > > > +static tree
> > > > +deferred_cst_barrier_apply (tree node)
> > > > +{
> > > > +  gcc_assert (deferred_cst_barrier_applies (node));
> > > > +
> > > > +  if (flag_deferred_cst_barrier_hard)
> > > > +    internal_error (
> > > > +      "unresolved deferred constant reached hard barrier");
> > > > +
> > > > +  switch (TREE_CODE (node))
> > > > +    {
> > > > +    case DEFERRED_CST_EXPR:
> > > > +      return evaluate_deferred_cst_now (TREE_OPERAND (node, 0));
> > > > +
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > > +      {
> > > > +       tree ref
> > > > +         = build3_loc (EXPR_LOCATION (node),
> > > > +                       COMPONENT_REF,
> > > > +                       TREE_TYPE (node),
> > > > +                       TREE_OPERAND (node, 0),
> > > > +                       TREE_OPERAND (node, 1),
> > > > +                       NULL_TREE);
> > > > +
> > > > +       /* Preserve expression properties that may have been attached
> > > > +          to the deferred reference during lowering.  */
> > > > +       TREE_READONLY (ref) = TREE_READONLY (node);
> > > > +       TREE_THIS_VOLATILE (ref) = TREE_THIS_VOLATILE (node);
> > > > +       TREE_SIDE_EFFECTS (ref) = TREE_SIDE_EFFECTS (node);
> > > > +
> > > > +       return ref;
> > > > +      }
> > > > +
> > > > +    default:
> > > > +      gcc_unreachable ();
> > > > +    }
> > > > +}
> > > > +
> > > > +/* Walker to go through functions and initializers.  It applies the
> > > > +   deferred constant barrier to all deferred constant tree nodes.  */
> > > > +static tree
> > > > +deferred_cst_barrier_walk (tree *tp, int *walk_subtrees, void *data)
> > > > +{
> > > > +  tree node = *tp;
> > > > +
> > > > +  if (!node)
> > > > +    return NULL_TREE;
> > > > +
> > > > +  if (TYPE_P (node) || DECL_P (node))
> > > > +    {
> > > > +      *walk_subtrees = 0;
> > > > +      return NULL_TREE;
> > > > +    }
> > > > +
> > > > +  if (!deferred_cst_barrier_applies (node))
> > > > +    return NULL_TREE;
> > > > +
> > > > +  /*
> > > > +     A deferred component can have another deferred component in its 
> > > > base.
> > > > +     Clean the base explicitly before replacing the outer node.  
> > > > Operand 1
> > > > +     is a FIELD_DECL and must not be walked.
> > > > +  */
> > > > +  if (TREE_CODE (node) == DEFERRED_CST_COMPONENT_REF)
> > > > +    {
> > > > +      tree base = TREE_OPERAND (node, 0);
> > > > +
> > > > +      walk_tree (&base,
> > > > +                deferred_cst_barrier_walk,
> > > > +                data,
> > > > +                NULL);
> > > > +
> > > > +      TREE_OPERAND (node, 0) = base;
> > > > +    }
> > > > +
> > > > +  *tp = deferred_cst_barrier_apply (node);
> > > > +  *walk_subtrees = 0;
> > > > +
> > > > +  return NULL_TREE;
> > > > +}
> > > > +
> > > > +static void
> > > > +deferred_cst_barrier_function (function *fn)
> > > > +{
> > > > +  basic_block bb;
> > > > +
> > > > +  FOR_EACH_BB_FN (bb, fn)
> > > > +    {
> > > > +      for (gimple_stmt_iterator gsi = gsi_start_bb (bb);
> > > > +          !gsi_end_p (gsi);
> > > > +          gsi_next (&gsi))
> > > > +       {
> > > > +         gimple *stmt = gsi_stmt (gsi);
> > > > +
> > > > +         for (unsigned int i = 0; i < gimple_num_ops (stmt); ++i)
> > > > +           {
> > > > +             tree *operand = gimple_op_ptr (stmt, i);
> > > > +
> > > > +             if (!operand || !*operand)
> > > > +               continue;
> > > > +
> > > > +             walk_tree (operand,
> > > > +                        deferred_cst_barrier_walk,
> > > > +                        NULL,
> > > > +                        NULL);
> > > > +           }
> > > > +       }
> > > > +    }
> > > > +}
> > > > +
> > > > +static tree
> > > > +deferred_cst_barrier_init (tree init)
> > > > +{
> > > > +  if (!init)
> > > > +    return init;
> > > > +
> > > > +  walk_tree (&init, deferred_cst_barrier_walk, NULL, NULL);
> > > > +
> > > > +  return init;
> > > > +}
> > > > +
> > > > +static void
> > > > +deferred_cst_barrier_functions ()
> > > > +{
> > > > +  cgraph_node *node;
> > > > +
> > > > +  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
> > > > +    {
> > > > +      function *fn = DECL_STRUCT_FUNCTION (node->decl);
> > > > +      gcc_assert (fn);
> > > > +
> > > > +      push_cfun (fn);
> > > > +      deferred_cst_barrier_function (fn);
> > > > +      pop_cfun ();
> > > > +    }
> > > > +}
> > > > +
> > > > +static void
> > > > +deferred_cst_barrier_inits ()
> > > > +{
> > > > +  varpool_node *vnode;
> > > > +
> > > > +  FOR_EACH_VARIABLE (vnode)
> > > > +    {
> > > > +      tree decl = vnode->decl;
> > > > +      tree init = DECL_INITIAL (decl);
> > > > +
> > > > +      if (!init || init == error_mark_node)
> > > > +       continue;
> > > > +
> > > > +      tree clean_init = deferred_cst_barrier_init (init);
> > > > +      if (clean_init != init)
> > > > +       DECL_INITIAL (decl) = clean_init;
> > > > +    }
> > > > +}
> > > > +
> > > > +namespace {
> > > > +
> > > > +const pass_data pass_data_deferred_cst_barrier =
> > > > +{
> > > > +  SIMPLE_IPA_PASS,
> > > > +  "deferred-cst-barrier",
> > > > +  OPTGROUP_NONE,
> > > > +  TV_NONE,
> > > > +  0, 0, 0, 0, 0
> > > > +};
> > > > +
> > > > +class pass_deferred_cst_barrier : public simple_ipa_opt_pass
> > > > +{
> > > > +public:
> > > > +  pass_deferred_cst_barrier (gcc::context *ctxt)
> > > > +    : simple_ipa_opt_pass (pass_data_deferred_cst_barrier, ctxt)
> > > > +  {}
> > > > +
> > > > +  unsigned int execute (function *) final override;
> > > > +};
> > > > +
> > > > +unsigned int
> > > > +pass_deferred_cst_barrier::execute (function *)
> > > > +{
> > > > +  deferred_cst_barrier_functions ();
> > > > +  deferred_cst_barrier_inits ();
> > > > +  return 0;
> > > > +}
> > > > +
> > > > +} // anonymous namespace
> > > > +
> > > > +simple_ipa_opt_pass *
> > > > +make_pass_deferred_cst_barrier (gcc::context *ctxt)
> > > > +{
> > > > +  return new pass_deferred_cst_barrier (ctxt);
> > > > +}
> > > > +
> > > > +#include "gt-deferred-cst.h"
> > > > diff --git a/gcc/deferred-cst.h b/gcc/deferred-cst.h
> > > > new file mode 100644
> > > > index 00000000000..d475030de01
> > > > --- /dev/null
> > > > +++ b/gcc/deferred-cst.h
> > > > @@ -0,0 +1,39 @@
> > > > +#ifndef GCC_DEFERRED_CST_H
> > > > +#define GCC_DEFERRED_CST_H
> > > > +
> > > > +/* Returns true of accesses to fields of this type should be based on
> > > > +   deferred cst offsets.  This is the case on RECORD_TYPEs with
> > > > +
> > > > +     __attribute__((deferred_cst_access))  */
> > > > +extern bool deferred_cst_access_type_p (tree type);
> > > > +
> > > > +/* Returns true if FIELD is part of a type that satisfies
> > > > +   deferred_cst_access_type_p.  */
> > > > +extern bool deferred_cst_field_p (tree field);
> > > > +
> > > > +/* Return true if CODE is an operation which may be absorbed into a
> > > > +   DEFERRED_CST_EXPR.  */
> > > > +extern bool deferred_cst_operator_p (enum tree_code code);
> > > > +
> > > > +/* Build DEFERRED_CST_COMPONENT_REF to FIELD in BASE.  */
> > > > +extern tree deferred_cst_build_component_ref (tree type, tree base, 
> > > > tree field);
> > > > +
> > > > +/* Build a DEFERRED_CST_EXPR around EXPR.  */
> > > > +extern tree deferred_cst_build_expr (tree expr);
> > > > +
> > > > +/* Create a call to IFN_DEFERRED_CST_FIELD_OFFSET for the physical,
> > > > +   addressable FIELD whose byte offset is deferred.
> > > > +
> > > > +   FIELD must not be a bit-field.  Callers handling bit-fields must 
> > > > pass
> > > > +   DECL_BIT_FIELD_REPRESENTATIVE (field).  */
> > > > +extern tree deferred_cst_call_ifn_field_offset (location_t loc, tree 
> > > > field);
> > > > +
> > > > +/* Get the FIELD_DECL associated with the argument TOKEN to a call of
> > > > +   IFN_DEFERRED_CST_FIELD_OFFSET.  */
> > > > +extern tree deferred_cst_ifn_field_offset_field (tree token);
> > > > +
> > > > +/* Return true if EXPR contains a DEFERRED_CST_EXPR or
> > > > +   DEFERRED_CST_COMPONENT_REF.  */
> > > > +extern bool deferred_cst_present_p (tree expr);
> > > > +
> > > > +#endif
> > > > diff --git a/gcc/expr.cc b/gcc/expr.cc
> > > > index 7ca5dd84ce9..d0925557cef 100644
> > > > --- a/gcc/expr.cc
> > > > +++ b/gcc/expr.cc
> > > > @@ -66,6 +66,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >  #include "tree-pretty-print.h"
> > > >  #include "flags.h"
> > > >  #include "internal-fn.h"
> > > > +#include "deferred-cst.h"
> > > >
> > > >
> > > >  /* If this is nonzero, we do not bother generating VOLATILE
> > > > @@ -8460,6 +8461,7 @@ store_field (rtx target, poly_int64 bitsize, 
> > > > poly_int64 bitpos,
> > > >      }
> > > >  }
> > > >
> > > > +
> > > >  /* Given an expression EXP that may be a COMPONENT_REF, a 
> > > > BIT_FIELD_REF,
> > > >     an ARRAY_REF, or an ARRAY_RANGE_REF, look for nested operations of 
> > > > these
> > > >     codes and find the ultimate containing object, which we return.
> > > > @@ -8497,7 +8499,8 @@ get_inner_reference (tree exp, poly_int64 
> > > > *pbitsize,
> > > >    /* First get the mode, signedness, storage order and size.  We do 
> > > > this from
> > > >       just the outermost expression.  */
> > > >    *pbitsize = -1;
> > > > -  if (TREE_CODE (exp) == COMPONENT_REF)
> > > > +  if (TREE_CODE (exp) == COMPONENT_REF
> > > > +      || TREE_CODE (exp) == DEFERRED_CST_COMPONENT_REF)
> > > >      {
> > > >        tree field = TREE_OPERAND (exp, 1);
> > > >        size_tree = DECL_SIZE (field);
> > > > @@ -8585,6 +8588,36 @@ get_inner_reference (tree exp, poly_int64 
> > > > *pbitsize,
> > > >           }
> > > >           break;
> > > >
> > > > +       case DEFERRED_CST_COMPONENT_REF:
> > > > +         {
> > > > +           tree field = TREE_OPERAND (exp, 1);
> > > > +           tree deferred_cst_field = field;
> > > > +
> > > > +           if (DECL_BIT_FIELD (field))
> > > > +             {
> > > > +               deferred_cst_field = DECL_BIT_FIELD_REPRESENTATIVE 
> > > > (field);
> > > > +
> > > > +               gcc_assert (deferred_cst_field);
> > > > +               gcc_assert (TREE_CODE (deferred_cst_field) == 
> > > > FIELD_DECL);
> > > > +               gcc_assert (!DECL_BIT_FIELD (deferred_cst_field));
> > > > +
> > > > +               tree relative_bit_offset
> > > > +                 = size_binop (MINUS_EXPR,
> > > > +                               bit_position (field),
> > > > +                               bit_position (deferred_cst_field));
> > > > +
> > > > +               gcc_assert (poly_int_tree_p (relative_bit_offset));
> > > > +               bit_offset += wi::to_poly_offset (relative_bit_offset);
> > > > +             }
> > > > +
> > > > +           tree this_offset = deferred_cst_call_ifn_field_offset (
> > > > +                                       EXPR_LOCATION (exp),
> > > > +                                       deferred_cst_field);
> > > > +           this_offset = deferred_cst_build_expr (this_offset);
> > > > +           offset = size_binop (PLUS_EXPR, offset, this_offset);
> > > > +         }
> > > > +         break;
> > > > +
> > > >         case ARRAY_REF:
> > > >         case ARRAY_RANGE_REF:
> > > >           {
> > > > diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> > > > index 56503e570bc..2fb0287b927 100644
> > > > --- a/gcc/fold-const.cc
> > > > +++ b/gcc/fold-const.cc
> > > > @@ -11139,6 +11139,20 @@ fold_binary_loc (location_t loc, enum 
> > > > tree_code code, tree type,
> > > >
> > > >    switch (code)
> > > >      {
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > > +      if (TREE_CODE (arg0) == CONSTRUCTOR
> > > > +         && ! type_contains_placeholder_p (TREE_TYPE (arg0)))
> > > > +       {
> > > > +         unsigned HOST_WIDE_INT idx;
> > > > +         tree field;
> > > > +         tree value;
> > > > +
> > > > +         FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (arg0), idx, 
> > > > field, value)
> > > > +           if (field == arg1)
> > > > +             return value;
> > > > +       }
> > > > +      return NULL_TREE;
> > > > +
> > > >      case MEM_REF:
> > > >        /* MEM[&MEM[p, CST1], CST2] -> MEM[p, CST1 + CST2].  */
> > > >        if (TREE_CODE (arg0) == ADDR_EXPR
> > > > diff --git a/gcc/gimple-expr.cc b/gcc/gimple-expr.cc
> > > > index 96f9fc720af..fabd6e10ea1 100644
> > > > --- a/gcc/gimple-expr.cc
> > > > +++ b/gcc/gimple-expr.cc
> > > > @@ -846,6 +846,11 @@ is_gimple_val (tree t)
> > > >        || TREE_CODE (t) == OMP_TARGET_DEVICE_MATCHES)
> > > >      return true;
> > > >
> > > > +  /* Technically, deferred constant expressions act as min invariants,
> > > > +     but they do not survice to where that would be relevant.  */
> > > > +  if (TREE_CODE (t) == DEFERRED_CST_EXPR)
> > > > +    return true;
> > > > +
> > > >    return (is_gimple_variable (t) || is_gimple_min_invariant (t));
> > > >  }
> > > >
> > > > diff --git a/gcc/gimple.cc b/gcc/gimple.cc
> > > > index c791e041243..7918552fce1 100644
> > > > --- a/gcc/gimple.cc
> > > > +++ b/gcc/gimple.cc
> > > > @@ -2564,7 +2564,8 @@ get_gimple_rhs_num_ops (enum tree_code code)
> > > >        || (SYM) == WITH_SIZE_EXPR                                       
> > > >     \
> > > >        || (SYM) == SSA_NAME                                             
> > > >     \
> > > >        || (SYM) == OMP_NEXT_VARIANT                                     
> > > >     \
> > > > -      || (SYM) == OMP_TARGET_DEVICE_MATCHES) ? GIMPLE_SINGLE_RHS       
> > > >     \
> > > > +      || (SYM) == OMP_TARGET_DEVICE_MATCHES                            
> > > >     \
> > > > +      || (SYM) == DEFERRED_CST_EXPR) ? GIMPLE_SINGLE_RHS               
> > > >     \
> > > >     : GIMPLE_INVALID_RHS),
> > > >  #define END_OF_BASE_TREE_CODES (unsigned char) GIMPLE_INVALID_RHS,
> > > >
> > > > diff --git a/gcc/gimplify.cc b/gcc/gimplify.cc
> > > > index 8bfc71315f9..6f55340dc34 100644
> > > > --- a/gcc/gimplify.cc
> > > > +++ b/gcc/gimplify.cc
> > > > @@ -75,6 +75,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >  #include "recog.h"
> > > >  #include "output.h"
> > > >  #include "gimplify_reg_info.h"
> > > > +#include "deferred-cst.h"
> > > >
> > > >  /* Identifier for a basic condition, mapping it to other basic 
> > > > conditions of
> > > >     its Boolean expression.  Basic conditions given the same uid (in 
> > > > the same
> > > > @@ -3497,8 +3498,8 @@ recalculate_side_effects (tree t)
> > > >     }
> > > >  }
> > > >
> > > > -/* Gimplify the COMPONENT_REF, ARRAY_REF, REALPART_EXPR or 
> > > > IMAGPART_EXPR
> > > > -   node *EXPR_P.
> > > > +/* Gimplify the COMPONENT_REF, DEFERRED_CST_COMPONENT_REF, ARRAY_REF,
> > > > +   REALPART_EXPR or IMAGPART_EXPR node *EXPR_P.
> > > >
> > > >        compound_lval
> > > >               : min_lval '[' val ']'
> > > > @@ -6086,7 +6087,7 @@ gimplify_init_ctor_preeval (tree *expr_p, 
> > > > gimple_seq *pre_p, gimple_seq *post_p,
> > > >     already been taken care of for us, in gimplify_init_ctor_preeval(). 
> > > >  */
> > > >
> > > >  static void gimplify_init_ctor_eval (tree, vec<constructor_elt, va_gc> 
> > > > *,
> > > > -                                    gimple_seq *, bool);
> > > > +                                    gimple_seq *, bool, bool);
> > > >
> > > >  static void
> > > >  gimplify_init_ctor_eval_range (tree object, tree lower, tree upper,
> > > > @@ -6116,10 +6117,15 @@ gimplify_init_ctor_eval_range (tree object, 
> > > > tree lower, tree upper,
> > > >       the store.  Otherwise just assign value to the reference.  */
> > > >
> > > >    if (TREE_CODE (value) == CONSTRUCTOR)
> > > > -    /* NB we might have to call ourself recursively through
> > > > -       gimplify_init_ctor_eval if the value is a constructor.  */
> > > > -    gimplify_init_ctor_eval (cref, CONSTRUCTOR_ELTS (value),
> > > > -                            pre_p, cleared);
> > > > +    {
> > > > +      /* NB we might have to call ourself recursively through
> > > > +         gimplify_init_ctor_eval if the value is a constructor.  */
> > > > +      bool deferred_cst_value
> > > > +       = deferred_cst_access_type_p (TREE_TYPE (value));
> > > > +
> > > > +      gimplify_init_ctor_eval (cref, CONSTRUCTOR_ELTS (value),
> > > > +                              pre_p, cleared, deferred_cst_value);
> > > > +    }
> > > >    else
> > > >      {
> > > >        if (gimplify_expr (&value, pre_p, NULL, is_gimple_val, fb_rvalue)
> > > > @@ -6154,7 +6160,8 @@ gimplify_init_ctor_eval_range (tree object, tree 
> > > > lower, tree upper,
> > > >
> > > >  static void
> > > >  gimplify_init_ctor_eval (tree object, vec<constructor_elt, va_gc> 
> > > > *elts,
> > > > -                        gimple_seq *pre_p, bool cleared)
> > > > +                        gimple_seq *pre_p, bool cleared,
> > > > +                        bool deferred_cst)
> > > >  {
> > > >    tree array_elt_type = NULL;
> > > >    unsigned HOST_WIDE_INT ix;
> > > > @@ -6220,14 +6227,25 @@ gimplify_init_ctor_eval (tree object, 
> > > > vec<constructor_elt, va_gc> *elts,
> > > >        else
> > > >         {
> > > >           gcc_assert (TREE_CODE (purpose) == FIELD_DECL);
> > > > -         cref = build3 (COMPONENT_REF, TREE_TYPE (purpose),
> > > > -                        unshare_expr (object), purpose, NULL_TREE);
> > > > +         tree base = unshare_expr (object);
> > > > +
> > > > +         if (deferred_cst)
> > > > +           cref = deferred_cst_build_component_ref (TREE_TYPE 
> > > > (purpose),
> > > > +                                                    base, purpose);
> > > > +         else
> > > > +           cref = build3 (COMPONENT_REF, TREE_TYPE (purpose),
> > > > +                          base, purpose, NULL_TREE);
> > > >         }
> > > >
> > > >        if (TREE_CODE (value) == CONSTRUCTOR
> > > >           && TREE_CODE (TREE_TYPE (value)) != VECTOR_TYPE)
> > > > -       gimplify_init_ctor_eval (cref, CONSTRUCTOR_ELTS (value),
> > > > -                                pre_p, cleared);
> > > > +       {
> > > > +         bool value_deferred_cst
> > > > +           = deferred_cst_access_type_p (TREE_TYPE (value));
> > > > +
> > > > +         gimplify_init_ctor_eval (cref, CONSTRUCTOR_ELTS (value),
> > > > +                                  pre_p, cleared, value_deferred_cst);
> > > > +       }
> > > >        else if (TREE_CODE (value) == RAW_DATA_CST)
> > > >         {
> > > >           if (RAW_DATA_LENGTH (value) <= 32)
> > > > @@ -6418,6 +6436,7 @@ gimplify_init_constructor (tree *expr_p, 
> > > > gimple_seq *pre_p, gimple_seq *post_p,
> > > >    bool cleared = false;
> > > >    bool is_empty_ctor = false;
> > > >    bool is_init_expr = (TREE_CODE (*expr_p) == INIT_EXPR);
> > > > +  bool deferred_cst = false;
> > > >
> > > >    gcc_assert (TREE_CODE (TREE_OPERAND (*expr_p, 1)) == CONSTRUCTOR);
> > > >
> > > > @@ -6434,6 +6453,8 @@ gimplify_init_constructor (tree *expr_p, 
> > > > gimple_seq *pre_p, gimple_seq *post_p,
> > > >      = optimize_compound_literals_in_ctor (TREE_OPERAND (*expr_p, 1));
> > > >    type = TREE_TYPE (ctor);
> > > >    elts = CONSTRUCTOR_ELTS (ctor);
> > > > +  deferred_cst
> > > > +    = deferred_cst_access_type_p (TREE_TYPE (ctor));
> > > >    ret = GS_ALL_DONE;
> > > >
> > > >    switch (TREE_CODE (type))
> > > > @@ -6698,7 +6719,8 @@ gimplify_init_constructor (tree *expr_p, 
> > > > gimple_seq *pre_p, gimple_seq *post_p,
> > > >         if (!cleared
> > > >             || num_nonzero_elements > 0
> > > >             || ctor_has_side_effects_p)
> > > > -         gimplify_init_ctor_eval (object, elts, pre_p, cleared);
> > > > +         gimplify_init_ctor_eval (object, elts, pre_p, cleared,
> > > > +                                  deferred_cst);
> > > >
> > > >         *expr_p = NULL_TREE;
> > > >        }
> > > > @@ -20425,6 +20447,7 @@ gimplify_expr (tree *expr_p, gimple_seq *pre_p, 
> > > > gimple_seq *post_p,
> > > >         case REALPART_EXPR:
> > > >         case IMAGPART_EXPR:
> > > >         case COMPONENT_REF:
> > > > +       case DEFERRED_CST_COMPONENT_REF:
> > > >           ret = gimplify_compound_lval (expr_p, pre_p, post_p,
> > > >                                         fallback ? fallback : 
> > > > fb_rvalue);
> > > >           break;
> > > > @@ -20658,6 +20681,11 @@ gimplify_expr (tree *expr_p, gimple_seq 
> > > > *pre_p, gimple_seq *post_p,
> > > >           ret = GS_ALL_DONE;
> > > >           break;
> > > >
> > > > +       case DEFERRED_CST_EXPR:
> > > > +         /* DEFERRED_CST_EXPRs act as min invariants.  */
> > > > +         ret = GS_ALL_DONE;
> > > > +         break;
> > > > +
> > > >         case CONST_DECL:
> > > >           /* If we require an lvalue, such as for ADDR_EXPR, retain the
> > > >              CONST_DECL node.  Otherwise the decl is replaceable by its
> > > > diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc
> > > > index 65be5aed35c..88a7334dadb 100644
> > > > --- a/gcc/internal-fn.cc
> > > > +++ b/gcc/internal-fn.cc
> > > > @@ -4266,6 +4266,15 @@ expand_reduc_sbool_optab_fn (internal_fn fn, 
> > > > gcall *stmt, direct_optab optab)
> > > >      assign_call_lhs (lhs, lhs_rtx, &ops[0]);
> > > >  }
> > > >
> > > > +/* IFN_DEFERRED_CST_FIELD_OFFSET should not survice GIMPLE.  */
> > > > +
> > > > +void
> > > > +expand_DEFERRED_CST_FIELD_OFFSET (internal_fn, gcall *)
> > > > +{
> > > > +  internal_error (
> > > > +    "IFN_DEFERRED_CST_FIELD_OFFSET reached RTL expansion without being 
> > > > lowered");
> > > > +}
> > > > +
> > > >  /* Expanders for optabs that can use expand_direct_optab_fn.  */
> > > >
> > > >  #define expand_unary_optab_fn(FN, STMT, OPTAB) \
> > > > diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
> > > > index 084a9271631..ce8aacf803f 100644
> > > > --- a/gcc/internal-fn.def
> > > > +++ b/gcc/internal-fn.def
> > > > @@ -622,6 +622,9 @@ DEF_INTERNAL_FN (BITINTTOFLOAT, ECF_PURE | 
> > > > ECF_LEAF, ". R . ")
> > > >  DEF_INTERNAL_OPTAB_FN (BIT_ANDN, ECF_CONST, andn, binary)
> > > >  DEF_INTERNAL_OPTAB_FN (BIT_IORN, ECF_CONST, iorn, binary)
> > > >
> > > > +/* Deferred constant functions.  */
> > > > +DEF_INTERNAL_FN (DEFERRED_CST_FIELD_OFFSET, ECF_CONST | ECF_LEAF | 
> > > > ECF_NOTHROW, NULL)
> > > > +
> > > >  #undef DEF_INTERNAL_WIDENING_OPTAB_FN
> > > >  #undef DEF_INTERNAL_SIGNED_COND_FN
> > > >  #undef DEF_INTERNAL_COND_FN
> > > > diff --git a/gcc/passes.def b/gcc/passes.def
> > > > index cdddb87302f..2b14864d200 100644
> > > > --- a/gcc/passes.def
> > > > +++ b/gcc/passes.def
> > > > @@ -47,6 +47,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >
> > > >    /* Interprocedural optimization passes.  */
> > > >    INSERT_PASSES_AFTER (all_small_ipa_passes)
> > > > +  NEXT_PASS (pass_deferred_cst_barrier);
> > > >    /* auto_profile_offline uses dwarf_name hook that only
> > > >       works before free_lang_data.  */
> > > >    NEXT_PASS (pass_ipa_auto_profile_offline);
> > > > diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
> > > > index b3c97658a8f..eddec889dc2 100644
> > > > --- a/gcc/tree-pass.h
> > > > +++ b/gcc/tree-pass.h
> > > > @@ -513,6 +513,7 @@ extern gimple_opt_pass 
> > > > *make_pass_coroutine_early_expand_ifns (gcc::context *ctx
> > > >  extern gimple_opt_pass *make_pass_adjust_alignment (gcc::context 
> > > > *ctxt);
> > > >
> > > >  /* IPA Passes */
> > > > +extern simple_ipa_opt_pass *make_pass_deferred_cst_barrier 
> > > > (gcc::context *ctxt);
> > > >  extern simple_ipa_opt_pass *make_pass_ipa_lower_emutls (gcc::context 
> > > > *ctxt);
> > > >  extern simple_ipa_opt_pass 
> > > > *make_pass_ipa_function_and_variable_visibility (gcc::context *ctxt);
> > > >  extern simple_ipa_opt_pass *make_pass_ipa_strub_mode (gcc::context 
> > > > *ctxt);
> > > > diff --git a/gcc/tree-pretty-print.cc b/gcc/tree-pretty-print.cc
> > > > index 79e0a734dbb..79251619fbe 100644
> > > > --- a/gcc/tree-pretty-print.cc
> > > > +++ b/gcc/tree-pretty-print.cc
> > > > @@ -2837,6 +2837,7 @@ dump_generic_node (pretty_printer *pp, tree node, 
> > > > int spc, dump_flags_t flags,
> > > >        break;
> > > >
> > > >      case COMPONENT_REF:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > >        op0 = TREE_OPERAND (node, 0);
> > > >        str = ".";
> > > >        if (op0
> > > > @@ -2873,16 +2874,21 @@ dump_generic_node (pretty_printer *pp, tree 
> > > > node, int spc, dump_flags_t flags,
> > > >        if (op_prio (op0) < op_prio (node))
> > > >         pp_right_paren (pp);
> > > >        pp_string (pp, str);
> > > > +      if (code == DEFERRED_CST_COMPONENT_REF)
> > > > +       pp_string (pp, "DEFERRED<");
> > > >        op1 = TREE_OPERAND (node, 1);
> > > >        dump_generic_node (pp, op1, spc, flags, false);
> > > > -      if (DECL_P (op1)) /* Not always a decl in the C++ FE.  */
> > > > -       if (tree off = component_ref_field_offset (node))
> > > > -         if (TREE_CODE (off) != INTEGER_CST)
> > > > -           {
> > > > -             pp_string (pp, "{off: ");
> > > > -             dump_generic_node (pp, off, spc, flags, false);
> > > > -             pp_right_brace (pp);
> > > > -           }
> > > > +      if (code == DEFERRED_CST_COMPONENT_REF)
> > > > +       pp_string (pp, ">");
> > > > +      else
> > > > +       if (DECL_P (op1)) /* Not always a decl in the C++ FE.  */
> > > > +         if (tree off = component_ref_field_offset (node))
> > > > +           if (TREE_CODE (off) != INTEGER_CST)
> > > > +             {
> > > > +               pp_string (pp, "{off: ");
> > > > +               dump_generic_node (pp, off, spc, flags, false);
> > > > +               pp_right_brace (pp);
> > > > +             }
> > > >        break;
> > > >
> > > >      case BIT_FIELD_REF:
> > > > @@ -4344,6 +4350,13 @@ dump_generic_node (pretty_printer *pp, tree 
> > > > node, int spc, dump_flags_t flags,
> > > >        }
> > > >        break;
> > > >
> > > > +    case DEFERRED_CST_EXPR:
> > > > +      pp_string (pp, "DEFERRED_CST_EXPR <");
> > > > +      dump_generic_node (pp, TREE_OPERAND (node, 0),
> > > > +                        spc, flags, false);
> > > > +      pp_character (pp, '>');
> > > > +      break;
> > > > +
> > > >      case OMP_TARGET_DEVICE_MATCHES:
> > > >        pp_string (pp, "OMP_TARGET_DEVICE_MATCHES <");
> > > >        dump_generic_node (pp, OMP_TARGET_DEVICE_MATCHES_SELECTOR 
> > > > (node), spc,
> > > > @@ -4790,6 +4803,7 @@ op_code_prio (enum tree_code code)
> > > >      case ARRAY_REF:
> > > >      case ARRAY_RANGE_REF:
> > > >      case COMPONENT_REF:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > >        return 15;
> > > >
> > > >        /* Special expressions.  */
> > > > @@ -5052,6 +5066,7 @@ print_call_name (pretty_printer *pp, tree node, 
> > > > dump_flags_t flags)
> > > >         }
> > > >        /* Fallthru.  */
> > > >      case COMPONENT_REF:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > >      case SSA_NAME:
> > > >      case OBJ_TYPE_REF:
> > > >        dump_generic_node (pp, op0, 0, flags, false);
> > > > diff --git a/gcc/tree.cc b/gcc/tree.cc
> > > > index 2868cb0867d..158c99b9aed 100644
> > > > --- a/gcc/tree.cc
> > > > +++ b/gcc/tree.cc
> > > > @@ -76,6 +76,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >  #include "asan.h"
> > > >  #include "ubsan.h"
> > > >  #include "attr-callback.h"
> > > > +#include "deferred-cst.h"
> > > >
> > > >  /* Names of tree components.
> > > >     Used for printing out the tree and error messages.  */
> > > > @@ -3908,7 +3909,13 @@ staticp (tree arg)
> > > >               ? arg : NULL);
> > > >
> > > >      case CONSTRUCTOR:
> > > > -      return TREE_STATIC (arg) ? arg : NULL;
> > > > +      {
> > > > +       if (!flag_deferred_cst_static
> > > > +           && deferred_cst_access_type_p (TREE_TYPE (arg)))
> > > > +         return NULL;
> > > > +
> > > > +       return TREE_STATIC (arg) ? arg : NULL;
> > > > +      }
> > > >
> > > >      case LABEL_DECL:
> > > >      case STRING_CST:
> > > > @@ -3926,6 +3933,16 @@ staticp (tree arg)
> > > >
> > > >        return staticp (TREE_OPERAND (arg, 0));
> > > >
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > > +      {
> > > > +       /* Deferred cst references are only compatible with static data 
> > > > if
> > > > +          explicitly permitted by -fdeferred-cst-static  */
> > > > +        if (flag_deferred_cst_static)
> > > > +         return staticp (TREE_OPERAND (arg, 0));
> > > > +       else
> > > > +         return NULL;
> > > > +      }
> > > > +
> > > >      case BIT_FIELD_REF:
> > > >        return NULL;
> > > >
> > > > diff --git a/gcc/tree.def b/gcc/tree.def
> > > > index ad8e676e942..43e94d0c361 100644
> > > > --- a/gcc/tree.def
> > > > +++ b/gcc/tree.def
> > > > @@ -443,6 +443,11 @@ DEFTREECODE (TRANSLATION_UNIT_DECL, 
> > > > "translation_unit_decl",\
> > > >     in units of DECL_OFFSET_ALIGN / BITS_PER_UNIT.  */
> > > >  DEFTREECODE (COMPONENT_REF, "component_ref", tcc_reference, 3)
> > > >
> > > > +/* Value is a structure component with a deferred constant offset.
> > > > +   Operand 0 is the structure expression.
> > > > +   Operand 1 is the field (a node of type FIELD_DECL).  */
> > > > +DEFTREECODE (DEFERRED_CST_COMPONENT_REF, "deferred_cst_component_ref", 
> > > > tcc_reference, 2)
> > > > +
> > > >  /* Reference to a group of bits within an object.  Similar to 
> > > > COMPONENT_REF
> > > >     except the position is given explicitly rather than via a 
> > > > FIELD_DECL.
> > > >     Operand 0 is the structure or union expression;
> > > > @@ -1610,6 +1615,14 @@ DEFTREECODE (TARGET_OPTION_NODE, 
> > > > "target_option_node", tcc_exceptional, 0)
> > > >     Operand 2 is additional data.  */
> > > >  DEFTREECODE (ANNOTATE_EXPR, "annotate_expr", tcc_expression, 3)
> > > >
> > > > +/* DEFERRED_CST_EXPR.
> > > > +   Operand 0 is an expression with deferred const-ness.
> > > > +   Such expressions are not constant at compile-time, but become
> > > > +   constant before the normal runtime.  For example, a
> > > > +   DEFERRED_CST_EXPR may wrap offsetof-like expressions that depend
> > > > +   on DEFERRED_CST_COMPONENT_REFs.  */
> > > > +DEFTREECODE (DEFERRED_CST_EXPR, "deferred_cst_expr", tcc_expression, 1)
> > > > +
> > > >  /*
> > > >  Local variables:
> > > >  mode:c
> > > > diff --git a/gcc/tree.h b/gcc/tree.h
> > > > index f8b2d718b7e..386a62051a8 100644
> > > > --- a/gcc/tree.h
> > > > +++ b/gcc/tree.h
> > > > @@ -5545,6 +5545,7 @@ handled_component_p (const_tree t)
> > > >    switch (TREE_CODE (t))
> > > >      {
> > > >      case COMPONENT_REF:
> > > > +    case DEFERRED_CST_COMPONENT_REF:
> > > >      case BIT_FIELD_REF:
> > > >      case ARRAY_REF:
> > > >      case ARRAY_RANGE_REF:
> > > > diff --git a/gcc/varasm.cc b/gcc/varasm.cc
> > > > index 5999f1e5420..c955670c7e7 100644
> > > > --- a/gcc/varasm.cc
> > > > +++ b/gcc/varasm.cc
> > > > @@ -64,6 +64,7 @@ along with GCC; see the file COPYING3.  If not see
> > > >  #include "asan.h"
> > > >  #include "recog.h"
> > > >  #include "gimple-expr.h"
> > > > +#include "deferred-cst.h"
> > > >
> > > >  /* The (assembler) name of the first globally-visible object output.  
> > > > */
> > > >  extern GTY(()) const char *first_global_object_name;
> > > > @@ -5056,7 +5057,12 @@ initializer_constant_valid_p_1 (tree value, tree 
> > > > endtype, tree *cache)
> > > >    switch (TREE_CODE (value))
> > > >      {
> > > >      case CONSTRUCTOR:
> > > > -      if (constructor_static_from_elts_p (value))
> > > > +      if (!flag_deferred_cst_static
> > > > +         && deferred_cst_access_type_p (TREE_TYPE (value)))
> > > > +       return NULL_TREE;
> > > > +
> > > > +      if (constructor_static_from_elts_p (value)
> > > > +         || flag_deferred_cst_static)
> > > >         {
> > > >           unsigned HOST_WIDE_INT idx;
> > > >           tree elt;
> > > > @@ -5107,6 +5113,16 @@ initializer_constant_valid_p_1 (tree value, tree 
> > > > endtype, tree *cache)
> > > >      case RAW_DATA_CST:
> > > >        return null_pointer_node;
> > > >
> > > > +    case DEFERRED_CST_EXPR:
> > > > +      {
> > > > +       /* Deferred constants are only allowed in static initializers
> > > > +          if explicitly permitted by -fdeferred-cst-static  */
> > > > +        if (flag_deferred_cst_static)
> > > > +         return null_pointer_node;
> > > > +       else
> > > > +         return NULL_TREE;
> > > > +      }
> > > > +
> > > >      case ADDR_EXPR:
> > > >      case FDESC_EXPR:
> > > >        {
> > > > --
> > > > 2.43.0
> > > >

Reply via email to