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. > 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 >
