static unsigned HOST_WIDE_INT addr_object_size (struct object_size_info *osi, const_tree ptr, int object_size_type) { tree pt_var, pt_var_size = NULL_TREE, var_size, bytes; gcc_assert (TREE_CODE (ptr) == ADDR_EXPR); pt_var = TREE_OPERAND (ptr, 0); while (handled_component_p (pt_var)) pt_var = TREE_OPERAND (pt_var, 0); if (pt_var && TREE_CODE (pt_var) == MEM_REF) { unsigned HOST_WIDE_INT sz; if (!osi || (object_size_type & 1) != 0 || TREE_CODE (TREE_OPERAND (pt_var, 0)) != SSA_NAME) { sz = compute_builtin_object_size (TREE_OPERAND (pt_var, 0), object_size_type & ~1); } else { tree var = TREE_OPERAND (pt_var, 0); if (osi->pass == 0) collect_object_sizes_for (osi, var); if (bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (var))) sz = object_sizes[object_size_type][SSA_NAME_VERSION (var)]; else sz = unknown[object_size_type]; } if (sz != unknown[object_size_type]) { double_int dsz = double_int::from_uhwi (sz) - mem_ref_offset (pt_var); if (dsz.is_negative ()) sz = 0; else if (dsz.fits_uhwi ()) sz = dsz.to_uhwi (); else sz = unknown[object_size_type]; } if (sz != unknown[object_size_type] && sz < offset_limit) pt_var_size = size_int (sz); } else if (pt_var && DECL_P (pt_var) && host_integerp (DECL_SIZE_UNIT (pt_var), 1) && (unsigned HOST_WIDE_INT) tree_low_cst (DECL_SIZE_UNIT (pt_var), 1) < offset_limit) pt_var_size = DECL_SIZE_UNIT (pt_var); else if (pt_var && TREE_CODE (pt_var) == STRING_CST && TYPE_SIZE_UNIT (TREE_TYPE (pt_var)) && host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1) && (unsigned HOST_WIDE_INT) tree_low_cst (TYPE_SIZE_UNIT (TREE_TYPE (pt_var)), 1) < offset_limit) pt_var_size = TYPE_SIZE_UNIT (TREE_TYPE (pt_var)); else return unknown[object_size_type]; if (pt_var != TREE_OPERAND (ptr, 0)) { tree var; if (object_size_type & 1) { var = TREE_OPERAND (ptr, 0); while (var != pt_var && TREE_CODE (var) != BIT_FIELD_REF && TREE_CODE (var) != COMPONENT_REF && TREE_CODE (var) != ARRAY_REF && TREE_CODE (var) != ARRAY_RANGE_REF && TREE_CODE (var) != REALPART_EXPR && TREE_CODE (var) != IMAGPART_EXPR) var = TREE_OPERAND (var, 0); if (var != pt_var && TREE_CODE (var) == ARRAY_REF) var = TREE_OPERAND (var, 0); if (! TYPE_SIZE_UNIT (TREE_TYPE (var)) || ! host_integerp (TYPE_SIZE_UNIT (TREE_TYPE (var)), 1) || (pt_var_size && tree_int_cst_lt (pt_var_size, TYPE_SIZE_UNIT (TREE_TYPE (var))))) var = pt_var; else if (var != pt_var && TREE_CODE (pt_var) == MEM_REF) { tree v = var; /* For &X->fld, compute object size only if fld isn't the last field, as struct { int i; char c[1]; } is often used instead of flexible array member. */ while (v && v != pt_var) switch (TREE_CODE (v)) { case ARRAY_REF: if (TYPE_SIZE_UNIT (TREE_TYPE (TREE_OPERAND (v, 0))) && TREE_CODE (TREE_OPERAND (v, 1)) == INTEGER_CST) { tree domain = TYPE_DOMAIN (TREE_TYPE (TREE_OPERAND (v, 0))); if (domain && TYPE_MAX_VALUE (domain) && TREE_CODE (TYPE_MAX_VALUE (domain)) == INTEGER_CST && tree_int_cst_lt (TREE_OPERAND (v, 1), TYPE_MAX_VALUE (domain))) { v = NULL_TREE; break; } } v = TREE_OPERAND (v, 0); break; case REALPART_EXPR: case IMAGPART_EXPR: v = NULL_TREE; break; case COMPONENT_REF: if (TREE_CODE (TREE_TYPE (v)) != ARRAY_TYPE) { v = NULL_TREE; break; } while (v != pt_var && TREE_CODE (v) == COMPONENT_REF) if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) != UNION_TYPE && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) != QUAL_UNION_TYPE) break; else v = TREE_OPERAND (v, 0); if (TREE_CODE (v) == COMPONENT_REF && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) == RECORD_TYPE) { tree fld_chain = DECL_CHAIN (TREE_OPERAND (v, 1)); for (; fld_chain; fld_chain = DECL_CHAIN (fld_chain)) if (TREE_CODE (fld_chain) == FIELD_DECL) break; if (fld_chain) { v = NULL_TREE; break; } v = TREE_OPERAND (v, 0); } while (v != pt_var && TREE_CODE (v) == COMPONENT_REF) if (TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) != UNION_TYPE && TREE_CODE (TREE_TYPE (TREE_OPERAND (v, 0))) != QUAL_UNION_TYPE) break; else v = TREE_OPERAND (v, 0); if (v != pt_var) v = NULL_TREE; else v = pt_var; break; default: v = pt_var; break; } if (v == pt_var) var = pt_var; } } else var = pt_var; if (var != pt_var) var_size = TYPE_SIZE_UNIT (TREE_TYPE (var)); else if (!pt_var_size) return unknown[object_size_type]; else var_size = pt_var_size; bytes = compute_object_offset (TREE_OPERAND (ptr, 0), var); if (bytes != error_mark_node) { if (TREE_CODE (bytes) == INTEGER_CST && tree_int_cst_lt (var_size, bytes)) bytes = size_zero_node; else bytes = size_binop (MINUS_EXPR, var_size, bytes); } if (var != pt_var && pt_var_size && TREE_CODE (pt_var) == MEM_REF && bytes != error_mark_node) { tree bytes2 = compute_object_offset (TREE_OPERAND (ptr, 0), pt_var); if (bytes2 != error_mark_node) { if (TREE_CODE (bytes2) == INTEGER_CST && tree_int_cst_lt (pt_var_size, bytes2)) bytes2 = size_zero_node; else bytes2 = size_binop (MINUS_EXPR, pt_var_size, bytes2); bytes = size_binop (MIN_EXPR, bytes, bytes2); } } } else if (!pt_var_size) return unknown[object_size_type]; else bytes = pt_var_size; if (host_integerp (bytes, 1)) return tree_low_cst (bytes, 1); return unknown[object_size_type]; }
unsigned HOST_WIDE_INT compute_builtin_object_size (tree ptr, int object_size_type) { gcc_assert (object_size_type >= 0 && object_size_type <= 3); if (! offset_limit) init_offset_limit (); if (TREE_CODE (ptr) == ADDR_EXPR) return addr_object_size (ptr, object_size_type); else if (TREE_CODE (ptr) == CALL_EXPR) { tree arg = pass_through_call (ptr); if (arg) return compute_builtin_object_size (arg, object_size_type); else return alloc_object_size (ptr, object_size_type); } else if (TREE_CODE (ptr) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (ptr)) && object_sizes[object_size_type] != NULL) { if (!bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr))) { struct object_size_info osi; bitmap_iterator bi; unsigned int i; if (dump_file) { fprintf (dump_file, "Computing %s %sobject size for ", (object_size_type & 2) ? "minimum" : "maximum", (object_size_type & 1) ? "sub" : ""); print_generic_expr (dump_file, ptr, dump_flags); fprintf (dump_file, ":\n"); } osi.visited = BITMAP_ALLOC (NULL); osi.reexamine = BITMAP_ALLOC (NULL); osi.object_size_type = object_size_type; osi.depths = NULL; osi.stack = NULL; osi.tos = NULL; /* First pass: walk UD chains, compute object sizes that can be computed. osi.reexamine bitmap at the end will contain what variables were found in dependency cycles and therefore need to be reexamined. */ osi.pass = 0; osi.changed = false; collect_object_sizes_for (&osi, ptr); /* Second pass: keep recomputing object sizes of variables that need reexamination, until no object sizes are increased or all object sizes are computed. */ if (! bitmap_empty_p (osi.reexamine)) { bitmap reexamine = BITMAP_ALLOC (NULL); /* If looking for minimum instead of maximum object size, detect cases where a pointer is increased in a loop. Although even without this detection pass 2 would eventually terminate, it could take a long time. If a pointer is increasing this way, we need to assume 0 object size. E.g. p = &buf[0]; while (cond) p = p + 4; */ if (object_size_type & 2) { osi.depths = xcalloc (num_ssa_names, sizeof (unsigned int)); osi.stack = xmalloc (num_ssa_names * sizeof (unsigned int)); osi.tos = osi.stack; osi.pass = 1; /* collect_object_sizes_for is changing osi.reexamine bitmap, so iterate over a copy. */ bitmap_copy (reexamine, osi.reexamine); EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi) if (bitmap_bit_p (osi.reexamine, i)) check_for_plus_in_loops (&osi, ssa_name (i)); free (osi.depths); osi.depths = NULL; free (osi.stack); osi.stack = NULL; osi.tos = NULL; } do { osi.pass = 2; osi.changed = false; /* collect_object_sizes_for is changing osi.reexamine bitmap, so iterate over a copy. */ bitmap_copy (reexamine, osi.reexamine); EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi) if (bitmap_bit_p (osi.reexamine, i)) { collect_object_sizes_for (&osi, ssa_name (i)); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Reexamining "); print_generic_expr (dump_file, ssa_name (i), dump_flags); fprintf (dump_file, "\n"); } } } while (osi.changed); BITMAP_FREE (reexamine); } EXECUTE_IF_SET_IN_BITMAP (osi.reexamine, 0, i, bi) bitmap_set_bit (computed[object_size_type], i); /* Debugging dumps. */ if (dump_file) { EXECUTE_IF_SET_IN_BITMAP (osi.visited, 0, i, bi) if (object_sizes[object_size_type][i] != unknown[object_size_type]) { print_generic_expr (dump_file, ssa_name (i), dump_flags); fprintf (dump_file, ": %s %sobject size " HOST_WIDE_INT_PRINT_UNSIGNED "\n", (object_size_type & 2) ? "minimum" : "maximum", (object_size_type & 1) ? "sub" : "", object_sizes[object_size_type][i]); } } BITMAP_FREE (osi.reexamine); BITMAP_FREE (osi.visited); }
bool compute_builtin_object_size (tree ptr, int object_size_type, unsigned HOST_WIDE_INT *psize) { gcc_assert (object_size_type >= 0 && object_size_type <= 3); /* Set to unknown and overwrite just before returning if the size could be determined. */ *psize = unknown[object_size_type]; if (! offset_limit) init_offset_limit (); if (TREE_CODE (ptr) == ADDR_EXPR) return addr_object_size (NULL, ptr, object_size_type, psize); if (TREE_CODE (ptr) != SSA_NAME || !POINTER_TYPE_P (TREE_TYPE (ptr))) return false; if (computed[object_size_type] == NULL) { if (optimize || object_size_type & 1) return false; /* When not optimizing, rather than failing, make a small effort to determine the object size without the full benefit of the (costly) computation below. */ gimple *def = SSA_NAME_DEF_STMT (ptr); if (gimple_code (def) == GIMPLE_ASSIGN) { tree_code code = gimple_assign_rhs_code (def); if (code == POINTER_PLUS_EXPR) { tree offset = gimple_assign_rhs2 (def); ptr = gimple_assign_rhs1 (def); if (tree_fits_shwi_p (offset) && compute_builtin_object_size (ptr, object_size_type, psize)) { /* Return zero when the offset is out of bounds. */ unsigned HOST_WIDE_INT off = tree_to_shwi (offset); *psize = off < *psize ? *psize - off : 0; return true; } } } return false; } if (!bitmap_bit_p (computed[object_size_type], SSA_NAME_VERSION (ptr))) { struct object_size_info osi; bitmap_iterator bi; unsigned int i; if (num_ssa_names > object_sizes[object_size_type].length ()) object_sizes[object_size_type].safe_grow (num_ssa_names); if (dump_file) { fprintf (dump_file, "Computing %s %sobject size for ", (object_size_type & 2) ? "minimum" : "maximum", (object_size_type & 1) ? "sub" : ""); print_generic_expr (dump_file, ptr, dump_flags); fprintf (dump_file, ":\n"); } osi.visited = BITMAP_ALLOC (NULL); osi.reexamine = BITMAP_ALLOC (NULL); osi.object_size_type = object_size_type; osi.depths = NULL; osi.stack = NULL; osi.tos = NULL; /* First pass: walk UD chains, compute object sizes that can be computed. osi.reexamine bitmap at the end will contain what variables were found in dependency cycles and therefore need to be reexamined. */ osi.pass = 0; osi.changed = false; collect_object_sizes_for (&osi, ptr); /* Second pass: keep recomputing object sizes of variables that need reexamination, until no object sizes are increased or all object sizes are computed. */ if (! bitmap_empty_p (osi.reexamine)) { bitmap reexamine = BITMAP_ALLOC (NULL); /* If looking for minimum instead of maximum object size, detect cases where a pointer is increased in a loop. Although even without this detection pass 2 would eventually terminate, it could take a long time. If a pointer is increasing this way, we need to assume 0 object size. E.g. p = &buf[0]; while (cond) p = p + 4; */ if (object_size_type & 2) { osi.depths = XCNEWVEC (unsigned int, num_ssa_names); osi.stack = XNEWVEC (unsigned int, num_ssa_names); osi.tos = osi.stack; osi.pass = 1; /* collect_object_sizes_for is changing osi.reexamine bitmap, so iterate over a copy. */ bitmap_copy (reexamine, osi.reexamine); EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi) if (bitmap_bit_p (osi.reexamine, i)) check_for_plus_in_loops (&osi, ssa_name (i)); free (osi.depths); osi.depths = NULL; free (osi.stack); osi.stack = NULL; osi.tos = NULL; } do { osi.pass = 2; osi.changed = false; /* collect_object_sizes_for is changing osi.reexamine bitmap, so iterate over a copy. */ bitmap_copy (reexamine, osi.reexamine); EXECUTE_IF_SET_IN_BITMAP (reexamine, 0, i, bi) if (bitmap_bit_p (osi.reexamine, i)) { collect_object_sizes_for (&osi, ssa_name (i)); if (dump_file && (dump_flags & TDF_DETAILS)) { fprintf (dump_file, "Reexamining "); print_generic_expr (dump_file, ssa_name (i), dump_flags); fprintf (dump_file, "\n"); } } } while (osi.changed); BITMAP_FREE (reexamine); }