Skip to content

Commit 753f9da

Browse files
committed
Encode and decode vptr when writing and reading
I think it might be working now Naive implementation of restart_lifetime Don't XOR vptr if class has constexpr constructor special quotes required for error_at Don't encode vptr in GCC compiler itself Suppress compiler warning becoming error Need cast to void for warning promoted to errors tag LSB of vptr Runtime environment variable CXX_VPTR_XOR_DISABLE to disable XOR Set environment variable CXX_VPTR_XOR_DISABLE in Makefile.in Use C++ code for restart_lifetime until __restart_lifetime is debugged Change signature of fold_builtin_restart_lifetime to have 3 parameters Remove minor typo -- an additional closing curly brace Purely cosmetic -- just a comment about deliberate fall through
1 parent cf03014 commit 753f9da

File tree

9 files changed

+392
-0
lines changed

9 files changed

+392
-0
lines changed

Makefile.in

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
# <http://www.gnu.org/licenses/>.
2222
#
2323

24+
# Disable vptr XOR when GCC is being built
25+
CXX_VPTR_XOR_DISABLE = 1
26+
export CXX_VPTR_XOR_DISABLE
27+
2428
# First, test for a proper version of make, but only where one is required.
2529

2630
@if gcc

gcc/cp/class.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,86 @@ static bool type_maybe_constexpr_destructor (tree);
207207
static bool field_poverlapping_p (tree);
208208
static void propagate_class_warmth_attribute (tree);
209209

210+
/* Use XOR to encode the vtable pointer for the given object. */
211+
212+
static tree
213+
xor_vptr_with_this (tree this_ptr, tree vtbl)
214+
{
215+
tree v_as_int = fold_convert (pointer_sized_int_node, vtbl);
216+
tree key = fold_convert (pointer_sized_int_node, this_ptr);
217+
tree xored = fold_build2 (BIT_XOR_EXPR, pointer_sized_int_node, v_as_int, key);
218+
return fold_convert (TREE_TYPE (vtbl), xored);
219+
}
220+
221+
/* Get the class type from a 'this' expression (pointer or reference). */
222+
223+
static tree
224+
vptr_class_type_from_this (tree this_ptr)
225+
{
226+
tree t = TREE_TYPE (this_ptr);
227+
if (!t)
228+
return NULL_TREE;
229+
230+
if (INDIRECT_TYPE_P (t))
231+
t = TREE_TYPE (t);
232+
233+
if (TREE_CODE (t) != RECORD_TYPE && TREE_CODE (t) != UNION_TYPE)
234+
return NULL_TREE;
235+
236+
return TYPE_MAIN_VARIANT (t);
237+
}
238+
239+
tree
240+
encode_vptr (tree this_ptr, tree vtbl)
241+
{
242+
/* Runtime knob:
243+
set CXX_VPTR_XOR_DISABLE to any non-emtpy string
244+
in order to disable encoding (needed for cxx1 invocation.) */
245+
246+
char const *const penv = getenv ("CXX_VPTR_XOR_DISABLE");
247+
if ( penv && ('\0' != *penv) ) return vtbl;
248+
249+
tree classtype = vptr_class_type_from_this (this_ptr);
250+
251+
/* For classes with constexpr ctors, never encode the vptr. */
252+
if (classtype && TYPE_HAS_CONSTEXPR_CTOR (classtype))
253+
return vtbl;
254+
255+
/* First XOR with 'this'. */
256+
tree ptr_type = TREE_TYPE (vtbl);
257+
tree encoded = xor_vptr_with_this (this_ptr, vtbl);
258+
259+
/* Then set the least significant bit as a tag. */
260+
tree encoded_int = fold_convert (sizetype, encoded);
261+
tree tag = build_int_cst (sizetype, 1);
262+
encoded_int = fold_build2 (BIT_IOR_EXPR, sizetype, encoded_int, tag);
263+
264+
return fold_convert (ptr_type, encoded_int);
265+
}
266+
267+
tree
268+
decode_vptr (tree this_ptr, tree vtbl)
269+
{
270+
tree ptr_type = TREE_TYPE (vtbl);
271+
tree vtbl_int = fold_convert (sizetype, vtbl);
272+
tree tag = build_int_cst (sizetype, 1);
273+
274+
/* Is the tag bit set? */
275+
tree tagged = fold_build2 (BIT_AND_EXPR, sizetype, vtbl_int, tag);
276+
tree cond = fold_build2 (NE_EXPR, boolean_type_node,
277+
tagged, build_int_cst (sizetype, 0));
278+
279+
/* Clear the tag bit. */
280+
tree clear_mask = fold_build1 (BIT_NOT_EXPR, sizetype, tag);
281+
tree untagged_int = fold_build2 (BIT_AND_EXPR, sizetype, vtbl_int, clear_mask);
282+
tree untagged = fold_convert (ptr_type, untagged_int);
283+
284+
/* If tagged, decode (XOR with this); otherwise return as-is. */
285+
tree decoded = xor_vptr_with_this (this_ptr, untagged);
286+
287+
return build3 (COND_EXPR, ptr_type, cond, decoded, vtbl);
288+
}
289+
210290
/* Set CURRENT_ACCESS_SPECIFIER based on the protection of DECL. */
211291

212292
void
@@ -479,6 +559,8 @@ build_base_path (enum tree_code code,
479559
}
480560
v_offset = build_vfield_ref (cp_build_fold_indirect_ref (t),
481561
TREE_TYPE (TREE_TYPE (expr)));
562+
if (v_offset == error_mark_node) return error_mark_node;
563+
v_offset = decode_vptr (t, v_offset);
482564
}
483565

484566
if (v_offset == error_mark_node)
@@ -759,7 +841,15 @@ build_vtbl_ref (tree instance, tree idx)
759841
}
760842

761843
if (!vtbl)
844+
{
762845
vtbl = build_vfield_ref (instance, basetype);
846+
gcc_assert (vtbl != error_mark_node);
847+
/* Dynamic case: load vptr from the object and decode it. */
848+
tree this_ptr;
849+
if (INDIRECT_TYPE_P (TREE_TYPE (instance))) this_ptr = instance; /* INSTANCE is already a pointer/reference. */
850+
else this_ptr = cp_build_addr_expr (instance, tf_warning_or_error); /* INSTANCE is an object; use its address as the key. */
851+
vtbl = decode_vptr (this_ptr, vtbl);
852+
}
763853

764854
aref = build_array_ref (input_location, vtbl, idx);
765855
TREE_CONSTANT (aref) |= TREE_CONSTANT (vtbl) && TREE_CONSTANT (idx);

gcc/cp/constexpr.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ along with GCC; see the file COPYING3. If not see
4343
#include "toplev.h"
4444
#include "contracts.h"
4545

46+
extern tree decode_vptr (tree this_ptr, tree vtbl); /* defined in class.cc */
47+
4648
static bool verify_constant (tree, bool, bool *, bool *);
4749
#define VERIFY_CONSTANT(X) \
4850
do { \
@@ -2457,6 +2459,23 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun,
24572459
}
24582460
new_call = fold_builtin_is_corresponding_member (loc, nargs, args);
24592461
}
2462+
else if (fndecl_built_in_p (fun,
2463+
CP_BUILT_IN_RESTART_LIFETIME,
2464+
BUILT_IN_FRONTEND))
2465+
{
2466+
#if 1
2467+
/* Easiest: forbid it in constant expressions. */
2468+
if (!*non_constant_p && !ctx->quiet)
2469+
error_at (EXPR_LOCATION (t),
2470+
"%q+E is not a constant expression", t);
2471+
*non_constant_p = true;
2472+
return t;
2473+
#else
2474+
location_t loc = EXPR_LOCATION (t);
2475+
new_call = fold_builtin_restart_lifetime (loc, nargs, args);
2476+
/* deliberate fall through here -- no return statement */
2477+
#endif
2478+
}
24602479
else
24612480
new_call = fold_builtin_call_array (EXPR_LOCATION (t), TREE_TYPE (t),
24622481
CALL_EXPR_FN (t), nargs, args);
@@ -3410,6 +3429,13 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call,
34103429
considered to be a most derived object that has the type of the
34113430
constructor or destructor's class. */
34123431
tree vtable = build_vfield_ref (obj, objtype);
3432+
if (vtable == error_mark_node)
3433+
{
3434+
*non_constant_p = true;
3435+
return call; /* fall back to runtime behavior */
3436+
}
3437+
tree this_ptr = cp_build_addr_expr (obj, complain);
3438+
vtable = decode_vptr (this_ptr, vtable);
34133439
vtable = cxx_eval_constant_expression (ctx, vtable, vc_prvalue,
34143440
non_constant_p, overflow_p,
34153441
jump_target);

gcc/cp/cp-gimplify.cc

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,12 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p)
956956
"__builtin_eh_ptr_adjust_ref");
957957
*expr_p = void_node;
958958
break;
959+
case CP_BUILT_IN_RESTART_LIFETIME:
960+
*expr_p
961+
= fold_builtin_restart_lifetime
962+
(EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p),
963+
&CALL_EXPR_ARG (*expr_p, 0));
964+
break;
959965
default:
960966
break;
961967
}
@@ -4084,4 +4090,206 @@ fold_builtin_source_location (const_tree t)
40844090
return build_fold_addr_expr_with_type_loc (loc, var, TREE_TYPE (t));
40854091
}
40864092

4093+
extern tree encode_vptr (tree this_ptr, tree vtbl); /* Defined in class.cc */
4094+
4095+
/* Get the class type from an expression that is (or converts to) T*. */
4096+
static tree
4097+
restart_lifetime_class_type_from_ptr (tree expr)
4098+
{
4099+
tree x = expr;
4100+
STRIP_NOPS (x);
4101+
tree t = TREE_TYPE (x);
4102+
if (!t)
4103+
return NULL_TREE;
4104+
4105+
if (TREE_CODE (t) == POINTER_TYPE || TREE_CODE (t) == REFERENCE_TYPE)
4106+
t = TREE_TYPE (t);
4107+
4108+
t = TYPE_MAIN_VARIANT (t);
4109+
if (!CLASS_TYPE_P (t))
4110+
return NULL_TREE;
4111+
4112+
return t;
4113+
}
4114+
4115+
/* Forward decl; defined below. */
4116+
static tree restart_lifetime_for_binfo (location_t, tree, tree, tree);
4117+
4118+
/* Handle polymorphic non-static data members of CLASSTYPE.
4119+
OBJ_PTR is a pointer to an object of type CLASSTYPE (T*).
4120+
SIDE_EFFECTS is a (possibly NULL) sequence of previous stores,
4121+
returned updated. */
4122+
static tree
4123+
restart_lifetime_for_members (location_t loc, tree obj_ptr,
4124+
tree classtype, tree side_effects)
4125+
{
4126+
/* Indirect to get a reference to this object. */
4127+
tree this_ref = cp_build_fold_indirect_ref (obj_ptr);
4128+
4129+
for (tree field = TYPE_FIELDS (classtype);
4130+
field;
4131+
field = TREE_CHAIN (field))
4132+
{
4133+
if (TREE_CODE (field) != FIELD_DECL)
4134+
continue;
4135+
if (TREE_STATIC (field))
4136+
continue;
4137+
4138+
tree ftype = TREE_TYPE (field);
4139+
4140+
/* For now, ignore arrays of polymorphic types; handling each element
4141+
would need a loop. */
4142+
if (TREE_CODE (ftype) == ARRAY_TYPE)
4143+
continue;
4144+
4145+
tree mtype = TYPE_MAIN_VARIANT (ftype);
4146+
if (!CLASS_TYPE_P (mtype))
4147+
continue;
4148+
4149+
if (!TYPE_CONTAINS_VPTR_P (mtype)
4150+
|| !COMPLETE_TYPE_P (mtype)
4151+
|| !TYPE_BINFO (mtype))
4152+
continue;
4153+
4154+
/* If this member type has constexpr ctors, we expect its vptrs not
4155+
to be encoded at all; don't touch it here. */
4156+
if (TYPE_HAS_CONSTEXPR_CTOR (mtype))
4157+
continue;
4158+
4159+
/* Build a reference to the member: this_ref.field. */
4160+
tree mem_ref
4161+
= build3 (COMPONENT_REF, ftype, this_ref, field, NULL_TREE);
4162+
4163+
/* Take its address: &this_ref.field. */
4164+
tree mem_ptr = cp_build_addr_expr (mem_ref, tf_warning_or_error);
4165+
if (mem_ptr == error_mark_node)
4166+
continue;
4167+
4168+
/* Reinit all vptrs in this member object by walking its BINFO tree. */
4169+
side_effects =
4170+
restart_lifetime_for_binfo (loc, mem_ptr, TYPE_BINFO (mtype),
4171+
side_effects);
4172+
}
4173+
4174+
return side_effects;
4175+
}
4176+
4177+
/* Recursively emit vptr reinitialization stores for BINFO and all its bases.
4178+
OBJ_PTR is a pointer to the most-derived object (T*), whose BINFO tree
4179+
we are walking at the root, and which we always pass unchanged to
4180+
build_base_path. SIDE_EFFECTS is a (possibly NULL) expression representing
4181+
the sequence of stores built so far; we return an updated sequence. */
4182+
static tree
4183+
restart_lifetime_for_binfo (location_t loc, tree obj_ptr, tree binfo,
4184+
tree side_effects)
4185+
{
4186+
tree basetype = BINFO_TYPE (binfo);
4187+
4188+
/* Compute pointer to this base subobject inside the complete object. */
4189+
tree base_ptr
4190+
= build_base_path (PLUS_EXPR, obj_ptr, binfo,
4191+
/*nonnull=*/1, tf_warning_or_error);
4192+
if (base_ptr == error_mark_node)
4193+
return side_effects;
4194+
4195+
/* If this base type uses encoded vptrs, reset its own vptr. */
4196+
if (TYPE_CONTAINS_VPTR_P (basetype)
4197+
&& !TYPE_HAS_CONSTEXPR_CTOR (basetype)
4198+
&& COMPLETE_TYPE_P (basetype)
4199+
&& TYPE_BINFO (basetype))
4200+
{
4201+
tree base_ref = cp_build_fold_indirect_ref (base_ptr);
4202+
tree vptr_lhs = build_vfield_ref (base_ref, basetype);
4203+
4204+
if (vptr_lhs != error_mark_node)
4205+
{
4206+
/* Canonical vtable pointer for this subobject. */
4207+
tree vtbl = build_vtbl_address (binfo);
4208+
vtbl = convert_force (TREE_TYPE (vptr_lhs), vtbl,
4209+
0, tf_warning_or_error);
4210+
vtbl = encode_vptr (base_ptr, vtbl);
4211+
4212+
tree store = cp_build_modify_expr (loc, vptr_lhs, NOP_EXPR, vtbl,
4213+
tf_warning_or_error);
4214+
4215+
if (!side_effects)
4216+
side_effects = store;
4217+
else
4218+
side_effects = build2 (COMPOUND_EXPR, void_type_node,
4219+
side_effects, store);
4220+
}
4221+
}
4222+
4223+
/* Also fix vptrs of any polymorphic member subobjects of this base. */
4224+
side_effects =
4225+
restart_lifetime_for_members (loc, base_ptr, basetype, side_effects);
4226+
4227+
/* Recurse into direct bases. */
4228+
int n = BINFO_N_BASE_BINFOS (binfo);
4229+
for (int i = 0; i < n; ++i)
4230+
{
4231+
tree base_binfo = BINFO_BASE_BINFO (binfo, i);
4232+
side_effects =
4233+
restart_lifetime_for_binfo (loc, obj_ptr, base_binfo, side_effects);
4234+
}
4235+
4236+
return side_effects;
4237+
}
4238+
4239+
tree
4240+
fold_builtin_restart_lifetime (location_t loc, int nargs, tree *args)
4241+
{
4242+
if (nargs != 2)
4243+
{
4244+
error_at (loc,
4245+
"%<__builtin_restart_lifetime%> expects 2 arguments, "
4246+
"the old address followed by the new address");
4247+
return error_mark_node;
4248+
}
4249+
4250+
tree oldp = args[0];
4251+
tree newp = args[1];
4252+
4253+
/* Result type – match the pointer we’re returning. */
4254+
tree rettype = TREE_TYPE (newp);
4255+
4256+
/* Find the dynamic class type from the pointer arguments. Prefer NEWP. */
4257+
tree classtype = restart_lifetime_class_type_from_ptr (newp);
4258+
if (!classtype)
4259+
classtype = restart_lifetime_class_type_from_ptr (oldp);
4260+
4261+
/* If we can't find a suitable class type, or it's not polymorphic, or
4262+
incomplete, just evaluate OLD and return NEW. */
4263+
if (!classtype
4264+
|| !TYPE_CONTAINS_VPTR_P (classtype)
4265+
|| !COMPLETE_TYPE_P (classtype)
4266+
|| !TYPE_BINFO (classtype))
4267+
return build2 (COMPOUND_EXPR, rettype, oldp, newp);
4268+
4269+
/* If the outer class has constexpr ctors, its vptrs are not encoded
4270+
at all by our scheme; do nothing. */
4271+
if (TYPE_HAS_CONSTEXPR_CTOR (classtype))
4272+
return build2 (COMPOUND_EXPR, rettype, oldp, newp);
4273+
4274+
/* Avoid evaluating NEWP twice. */
4275+
if (TREE_SIDE_EFFECTS (newp))
4276+
newp = save_expr (newp);
4277+
4278+
/* Treat NEWP as pointer to the most-derived object. */
4279+
tree ptr_T = build_pointer_type (classtype);
4280+
tree newp_as_T = build_nop (ptr_T, newp);
4281+
4282+
/* Emit vptr reinit stores for the whole BINFO tree (bases + members). */
4283+
tree effects
4284+
= restart_lifetime_for_binfo (loc, newp_as_T, TYPE_BINFO (classtype),
4285+
NULL_TREE);
4286+
4287+
if (!effects)
4288+
return build2 (COMPOUND_EXPR, rettype, oldp, newp);
4289+
4290+
/* Preserve OLD's side effects as well: (oldp, effects, newp). */
4291+
tree seq = build2 (COMPOUND_EXPR, void_type_node, oldp, effects);
4292+
return build2 (COMPOUND_EXPR, rettype, seq, newp);
4293+
}
4294+
40874295
#include "gt-cp-cp-gimplify.h"

0 commit comments

Comments
 (0)