mirror of git://gcc.gnu.org/git/gcc.git
Merge commit 'f3f6ff7b16861cd0651eccff14689536550762ae^' into HEAD
This commit is contained in:
commit
b2ccc44dfb
113
ChangeLog
113
ChangeLog
|
|
@ -1,3 +1,116 @@
|
|||
2023-08-29 Tsukasa OI <research_trasio@irq.a4lg.com>
|
||||
|
||||
* MAINTAINERS: Add myself.
|
||||
|
||||
2023-08-22 Filip Kastl <fkastl@suse.cz>
|
||||
|
||||
* MAINTAINERS: Update my email address.
|
||||
|
||||
2023-08-11 Eric Feng <ef2648@columbia.edu>
|
||||
|
||||
* MAINTAINERS: Add myself.
|
||||
|
||||
2023-08-07 Indu Bhagat <indu.bhagat@oracle.com>
|
||||
|
||||
* Makefile.def: Reflect that libsframe needs to installed before
|
||||
libbfd. Reorder a bit to better track libsframe dependencies.
|
||||
* Makefile.in: Regenerate.
|
||||
|
||||
2023-08-07 Indu Bhagat <indu.bhagat@oracle.com>
|
||||
|
||||
* Makefile.def: Add install dependency on libsframe for libbfd.
|
||||
* Makefile.in: Regenerated.
|
||||
|
||||
2023-08-07 Nick Alcock <nick.alcock@oracle.com>
|
||||
|
||||
* libtool.m4 (lt_cv_sys_global_symbol_pipe): Augment symcode for
|
||||
Solaris 11.
|
||||
|
||||
2023-08-07 Alexander von Gluck IV <kallisti5@unixzen.com>
|
||||
|
||||
* configure: Regenerate.
|
||||
* configure.ac: Add Haiku to list of ELF OSes
|
||||
* libtool.m4: Update sys_lib_dlsearch_path_spec on Haiku.
|
||||
|
||||
2023-08-07 Nick Alcock <nick.alcock@oracle.com>
|
||||
|
||||
* libtool.m4 (LT_PATH_NM): Handle user-specified NM with
|
||||
options, including options containing paths.
|
||||
|
||||
2023-08-07 Nick Alcock <nick.alcock@oracle.com>
|
||||
|
||||
* libtool.m4 (LT_PATH_NM): Try BSDization flags with a user-provided
|
||||
NM, if there is one. Run nm on itself, not on /dev/null, to avoid
|
||||
errors from nms that refuse to work on non-regular files. Remove
|
||||
other workarounds for this problem. Strip out blank lines from the
|
||||
nm output.
|
||||
|
||||
2023-08-07 Arsen Arsenović <arsen@aarsen.me>
|
||||
|
||||
* configure.ac: Reinstate 32b PA-RISC HP-UX targets
|
||||
* configure: Regenerate.
|
||||
|
||||
2023-08-07 Simon Marchi <simon.marchi@efficios.com>
|
||||
|
||||
* configure.ac: Add AC_SUBST(PKG_CONFIG_PATH).
|
||||
* configure: Re-generate.
|
||||
* Makefile.tpl (HOST_EXPORTS): Pass PKG_CONFIG_PATH.
|
||||
(PKG_CONFIG_PATH): New.
|
||||
* Makefile.in: Re-generate.
|
||||
|
||||
2023-08-07 Luis Machado <luis.machado@arm.com>
|
||||
|
||||
* configure.ac: Disable year2038 by default on 32-bit hosts.
|
||||
* configure: Regenerate.
|
||||
|
||||
2023-08-07 Vladimir Mezentsev <vladimir.mezentsev@oracle.com>
|
||||
|
||||
* Makefile.def: Add gprofng module.
|
||||
* configure.ac: Add --enable-gprofng option.
|
||||
* Makefile.in: Regenerate.
|
||||
* configure: Regenerate.
|
||||
|
||||
2023-08-07 Martin Liska <mliska@suse.cz>
|
||||
|
||||
* configure.ac: Add --enable-default-compressed-debug-sections-algorithm.
|
||||
* configure: Regenerate.
|
||||
|
||||
2023-08-07 Fangrui Song <maskray@google.com>
|
||||
|
||||
* configure: Regenerate.
|
||||
* configure.ac: Add --with-zstd.
|
||||
|
||||
2023-08-07 Arsen Arsenović <arsen@aarsen.me>
|
||||
|
||||
* configure: Regenerate.
|
||||
* configure.ac: Recover tilegx/tilepro targets.
|
||||
|
||||
2023-08-07 H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
* configure: Regenerated.
|
||||
* libtool.m4 (_LT_CMD_OLD_ARCHIVE): Check if AR works with
|
||||
--plugin and rc before enabling --plugin.
|
||||
|
||||
2023-08-07 H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
* Makefile.tpl (AR): Add @AR_PLUGIN_OPTION@
|
||||
(RANLIB): Add @RANLIB_PLUGIN_OPTION@.
|
||||
* configure.ac: Include config/gcc-plugin.m4.
|
||||
AC_SUBST AR_PLUGIN_OPTION and RANLIB_PLUGIN_OPTION.
|
||||
* libtool.m4 (_LT_CMD_OLD_ARCHIVE): Pass --plugin to AR and
|
||||
RANLIB if possible.
|
||||
* Makefile.in: Regenerated.
|
||||
* configure: Likewise.
|
||||
|
||||
2023-08-07 Arsen Arsenović <arsen@aarsen.me>
|
||||
|
||||
* Makefile.tpl: Substitute @GDCFLAGS@ instead of using
|
||||
$(CFLAGS).
|
||||
|
||||
2023-08-07 David Faust <david.faust@oracle.com>
|
||||
|
||||
* MAINTAINERS: Add the BPF port to my reviewer listing.
|
||||
|
||||
2023-08-02 Jan Beulich <jbeulich@suse.com>
|
||||
|
||||
* MAINTAINERS: Correct my email address.
|
||||
|
|
|
|||
|
|
@ -412,6 +412,7 @@ Chris Fairles <cfairles@gcc.gnu.org>
|
|||
Alessandro Fanfarillo <fanfarillo.gcc@gmail.com>
|
||||
Changpeng Fang <changpeng.fang@amd.com>
|
||||
Sam Feifer <sfeifer@redhat.com>
|
||||
Eric Feng <ef2648@columbia.edu>
|
||||
Li Feng <nemokingdom@gmail.com>
|
||||
Thomas Fitzsimmons <fitzsim@redhat.com>
|
||||
Alexander Fomin <afomin.mailbox@gmail.com>
|
||||
|
|
@ -490,7 +491,7 @@ Kean Johnston <jkj@sco.com>
|
|||
Phillip Jordan <phillip.m.jordan@gmail.com>
|
||||
Tim Josling <tej@melbpc.org.au>
|
||||
Victor Kaplansky <victork@il.ibm.com>
|
||||
Filip Kastl <filip.kastl@gmail.com>
|
||||
Filip Kastl <fkastl@suse.cz>
|
||||
Geoffrey Keating <geoffk@geoffk.org>
|
||||
Brendan Kehoe <brendan@zen.org>
|
||||
Andi Kleen <andi@firstfloor.org>
|
||||
|
|
@ -592,6 +593,7 @@ Carlos O'Donell <carlos@redhat.com>
|
|||
Peter O'Gorman <pogma@thewrittenword.com>
|
||||
Patrick O'Neill <patrick@rivosinc.com>
|
||||
Braden Obrzut <admin@maniacsvault.net>
|
||||
Tsukasa Oi <research_trasio@irq.a4lg.com>
|
||||
Andrea Ornstein <andrea.ornstein@st.com>
|
||||
Maxim Ostapenko <m.ostapenko@samsung.com>
|
||||
Jeevitha Palanisamy <jeevitha@linux.ibm.com>
|
||||
|
|
|
|||
|
|
@ -462,11 +462,14 @@ dependencies = { module=all-gdbsupport; on=all-gnulib; };
|
|||
dependencies = { module=all-gdbsupport; on=all-intl; };
|
||||
|
||||
// Host modules specific to binutils.
|
||||
// build libsframe before bfd for encoder/decoder support for linking
|
||||
// SFrame sections
|
||||
dependencies = { module=configure-bfd; on=configure-libiberty; hard=true; };
|
||||
dependencies = { module=configure-bfd; on=configure-intl; };
|
||||
dependencies = { module=all-bfd; on=all-libiberty; };
|
||||
dependencies = { module=all-bfd; on=all-intl; };
|
||||
dependencies = { module=all-bfd; on=all-zlib; };
|
||||
dependencies = { module=all-bfd; on=all-libsframe; };
|
||||
dependencies = { module=configure-opcodes; on=configure-libiberty; hard=true; };
|
||||
dependencies = { module=all-opcodes; on=all-libiberty; };
|
||||
|
||||
|
|
@ -499,6 +502,10 @@ dependencies = { module=install-strip-libctf; on=install-strip-bfd; };
|
|||
dependencies = { module=install-strip-ld; on=install-strip-bfd; };
|
||||
dependencies = { module=install-strip-ld; on=install-strip-libctf; };
|
||||
|
||||
// libbfd depends on libsframe
|
||||
dependencies = { module=install-bfd; on=install-libsframe; };
|
||||
dependencies = { module=install-strip-bfd; on=install-strip-libsframe; };
|
||||
|
||||
// libopcodes depends on libbfd
|
||||
dependencies = { module=configure-opcodes; on=configure-bfd; hard=true; };
|
||||
dependencies = { module=install-opcodes; on=install-bfd; };
|
||||
|
|
|
|||
12
Makefile.in
12
Makefile.in
|
|
@ -68138,6 +68138,16 @@ all-stagetrain-bfd: maybe-all-stagetrain-zlib
|
|||
all-stagefeedback-bfd: maybe-all-stagefeedback-zlib
|
||||
all-stageautoprofile-bfd: maybe-all-stageautoprofile-zlib
|
||||
all-stageautofeedback-bfd: maybe-all-stageautofeedback-zlib
|
||||
all-bfd: maybe-all-libsframe
|
||||
all-stage1-bfd: maybe-all-stage1-libsframe
|
||||
all-stage2-bfd: maybe-all-stage2-libsframe
|
||||
all-stage3-bfd: maybe-all-stage3-libsframe
|
||||
all-stage4-bfd: maybe-all-stage4-libsframe
|
||||
all-stageprofile-bfd: maybe-all-stageprofile-libsframe
|
||||
all-stagetrain-bfd: maybe-all-stagetrain-libsframe
|
||||
all-stagefeedback-bfd: maybe-all-stagefeedback-libsframe
|
||||
all-stageautoprofile-bfd: maybe-all-stageautoprofile-libsframe
|
||||
all-stageautofeedback-bfd: maybe-all-stageautofeedback-libsframe
|
||||
configure-opcodes: configure-libiberty
|
||||
configure-stage1-opcodes: configure-stage1-libiberty
|
||||
configure-stage2-opcodes: configure-stage2-libiberty
|
||||
|
|
@ -68276,6 +68286,8 @@ install-ld: maybe-install-libctf
|
|||
install-strip-libctf: maybe-install-strip-bfd
|
||||
install-strip-ld: maybe-install-strip-bfd
|
||||
install-strip-ld: maybe-install-strip-libctf
|
||||
install-bfd: maybe-install-libsframe
|
||||
install-strip-bfd: maybe-install-strip-libsframe
|
||||
configure-opcodes: configure-bfd
|
||||
configure-stage1-opcodes: configure-stage1-bfd
|
||||
configure-stage2-opcodes: configure-stage2-bfd
|
||||
|
|
|
|||
|
|
@ -1,3 +1,50 @@
|
|||
2023-08-11 Joseph Myers <joseph@codesourcery.com>
|
||||
|
||||
* gcc-plugin.m4 (GCC_ENABLE_PLUGINS): Use
|
||||
export_sym_check="$ac_cv_prog_OBJDUMP -T" also when host is not
|
||||
build or target.
|
||||
|
||||
2023-08-07 H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
* pkg.m4 (PKG_CHECK_MODULES): Use AC_TRY_LINK only if
|
||||
$pkg_failed = no.
|
||||
|
||||
2023-08-07 H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
* pkg.m4 (PKG_CHECK_MODULES): Add AC_TRY_LINK to check if
|
||||
$pkg_cv_[]$1[]_LIBS works.
|
||||
|
||||
2023-08-07 John Ericson <git@JohnEricson.me>
|
||||
|
||||
* picflag.m4: Simplify SHmedia NetBSD match by presuming ELF.
|
||||
|
||||
2023-08-07 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* override.m4: Correct comment grammar.
|
||||
|
||||
2023-08-07 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* lib-ld.m4 (AC_LIB_PROG_LD_GNU): Require AC_PROG_EGREP and
|
||||
invoke $EGREP.
|
||||
(AC_LIB_PROG_LD): Likewise.
|
||||
|
||||
2023-08-07 Christophe Lyon <christophe.lyon@arm.com>
|
||||
|
||||
* zstd.m4: Add minimum version requirement of 1.4.0.
|
||||
|
||||
2023-08-07 Fangrui Song <maskray@google.com>
|
||||
|
||||
* zstd.m4: New file.
|
||||
|
||||
2023-08-07 H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
* gcc-plugin.m4 (GCC_PLUGIN_OPTION): Check if AR works with
|
||||
--plugin and rc before enabling --plugin.
|
||||
|
||||
2023-08-07 H.J. Lu <hjl.tools@gmail.com>
|
||||
|
||||
* gcc-plugin.m4 (GCC_PLUGIN_OPTION): New.
|
||||
|
||||
2023-07-21 Sergei Trofimovich <siarheit@google.com>
|
||||
|
||||
* mh-mingw: Drop assignment of unused BOOT_CXXFLAGS variable.
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ AC_DEFUN([GCC_ENABLE_PLUGINS],
|
|||
elif test x$host = x$target; then
|
||||
export_sym_check="$gcc_cv_objdump -T"
|
||||
else
|
||||
export_sym_check=
|
||||
export_sym_check="$ac_cv_prog_OBJDUMP -T"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
|
|
|||
|
|
@ -1,3 +1,11 @@
|
|||
2023-08-29 Lehua Ding <lehua.ding@rivai.ai>
|
||||
|
||||
* mklog.py: Fix bugs.
|
||||
|
||||
2023-08-16 Andrew Pinski <apinski@marvell.com>
|
||||
|
||||
* gcc_update: Add libstdc++-v3/include/bits/version.h.
|
||||
|
||||
2023-07-13 Lehua Ding <lehua.ding@rivai.ai>
|
||||
|
||||
* mklog.py: Add --append option.
|
||||
|
|
|
|||
|
|
@ -182,6 +182,7 @@ libphobos/config.h.in: libphobos/configure.ac libphobos/aclocal.m4
|
|||
libphobos/configure: libphobos/configure.ac libphobos/aclocal.m4
|
||||
libphobos/src/Makefile.in: libphobos/src/Makefile.am libphobos/aclocal.m4
|
||||
libphobos/testsuite/Makefile.in: libphobos/testsuite/Makefile.am libphobos/aclocal.m4
|
||||
libstdc++-v3/include/bits/version.h: libstdc++-v3/include/bits/version.def libstdc++-v3/include/bits/version.tpl
|
||||
# Top level
|
||||
Makefile.in: Makefile.tpl Makefile.def
|
||||
configure: configure.ac config/acx.m4
|
||||
|
|
|
|||
|
|
@ -398,7 +398,8 @@ if __name__ == '__main__':
|
|||
args.fill_up_bug_titles, args.pr_numbers)
|
||||
if args.append:
|
||||
if (not args.input):
|
||||
raise Exception("`-a or --append` option not support standard input")
|
||||
raise Exception("`-a or --append` option not support standard "
|
||||
"input")
|
||||
lines = []
|
||||
with open(args.input, 'r', newline='\n') as f:
|
||||
# 1 -> not find the possible start of diff log
|
||||
|
|
@ -408,13 +409,14 @@ if __name__ == '__main__':
|
|||
for line in f:
|
||||
if maybe_diff_log == 1 and line == "---\n":
|
||||
maybe_diff_log = 2
|
||||
elif maybe_diff_log == 2 and \
|
||||
re.match("\s[^\s]+\s+\|\s\d+\s[+\-]+\n", line):
|
||||
elif (maybe_diff_log == 2 and
|
||||
re.match(r"\s[^\s]+\s+\|\s+\d+\s[+\-]+\n", line)):
|
||||
lines += [output, "---\n", line]
|
||||
maybe_diff_log = 3
|
||||
else:
|
||||
# the possible start is not the true start.
|
||||
if maybe_diff_log == 2:
|
||||
lines.append("---\n")
|
||||
maybe_diff_log = 1
|
||||
lines.append(line)
|
||||
with open(args.input, "w") as f:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,14 @@
|
|||
2023-08-17 Rainer Orth <ro@CeBiTec.Uni-Bielefeld.DE>
|
||||
|
||||
* inclhack.def (darwin_flt_eval_method): Handle macOS 14 guard
|
||||
variant.
|
||||
* fixincl.x: Regenerate.
|
||||
* tests/base/math.h [DARWIN_FLT_EVAL_METHOD_CHECK]: Update test.
|
||||
|
||||
2023-08-07 Nick Alcock <nick.alcock@oracle.com>
|
||||
|
||||
* configure: Regenerate.
|
||||
|
||||
2023-06-15 Marek Polacek <polacek@redhat.com>
|
||||
|
||||
* Makefile.in: Set and use PICFLAG and LD_PICFLAG. Use the "pic"
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@
|
|||
*
|
||||
* DO NOT EDIT THIS FILE (fixincl.x)
|
||||
*
|
||||
* It has been AutoGen-ed January 22, 2023 at 09:03:29 PM by AutoGen 5.18.12
|
||||
* It has been AutoGen-ed August 17, 2023 at 10:16:38 AM by AutoGen 5.18.12
|
||||
* From the definitions inclhack.def
|
||||
* and the template file fixincl
|
||||
*/
|
||||
/* DO NOT SVN-MERGE THIS FILE, EITHER Sun Jan 22 21:03:29 CET 2023
|
||||
/* DO NOT SVN-MERGE THIS FILE, EITHER Thu Aug 17 10:16:38 CEST 2023
|
||||
*
|
||||
* You must regenerate it. Use the ./genfixes script.
|
||||
*
|
||||
|
|
@ -3674,7 +3674,7 @@ tSCC* apzDarwin_Flt_Eval_MethodMachs[] = {
|
|||
* content selection pattern - do fix if pattern found
|
||||
*/
|
||||
tSCC zDarwin_Flt_Eval_MethodSelect0[] =
|
||||
"^#if __FLT_EVAL_METHOD__ == 0$";
|
||||
"^#if __FLT_EVAL_METHOD__ == 0( \\|\\| __FLT_EVAL_METHOD__ == -1)?$";
|
||||
|
||||
#define DARWIN_FLT_EVAL_METHOD_TEST_CT 1
|
||||
static tTestDesc aDarwin_Flt_Eval_MethodTests[] = {
|
||||
|
|
@ -3685,7 +3685,7 @@ static tTestDesc aDarwin_Flt_Eval_MethodTests[] = {
|
|||
*/
|
||||
static const char* apzDarwin_Flt_Eval_MethodPatch[] = {
|
||||
"format",
|
||||
"#if __FLT_EVAL_METHOD__ == 0 || __FLT_EVAL_METHOD__ == 16",
|
||||
"%0 || __FLT_EVAL_METHOD__ == 16",
|
||||
(char*)NULL };
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
|
|
|
|||
|
|
@ -1819,10 +1819,11 @@ fix = {
|
|||
hackname = darwin_flt_eval_method;
|
||||
mach = "*-*-darwin*";
|
||||
files = math.h;
|
||||
select = "^#if __FLT_EVAL_METHOD__ == 0$";
|
||||
select = "^#if __FLT_EVAL_METHOD__ == 0( \\|\\| __FLT_EVAL_METHOD__ == -1)?$";
|
||||
c_fix = format;
|
||||
c_fix_arg = "#if __FLT_EVAL_METHOD__ == 0 || __FLT_EVAL_METHOD__ == 16";
|
||||
test_text = "#if __FLT_EVAL_METHOD__ == 0";
|
||||
c_fix_arg = "%0 || __FLT_EVAL_METHOD__ == 16";
|
||||
test_text = "#if __FLT_EVAL_METHOD__ == 0\n"
|
||||
"#if __FLT_EVAL_METHOD__ == 0 || __FLT_EVAL_METHOD__ == -1";
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
|
||||
#if defined( DARWIN_FLT_EVAL_METHOD_CHECK )
|
||||
#if __FLT_EVAL_METHOD__ == 0 || __FLT_EVAL_METHOD__ == 16
|
||||
#if __FLT_EVAL_METHOD__ == 0 || __FLT_EVAL_METHOD__ == -1 || __FLT_EVAL_METHOD__ == 16
|
||||
#endif /* DARWIN_FLT_EVAL_METHOD_CHECK */
|
||||
|
||||
|
||||
|
|
|
|||
3862
gcc/ChangeLog
3862
gcc/ChangeLog
File diff suppressed because it is too large
Load Diff
|
|
@ -1 +1 @@
|
|||
20230807
|
||||
20230904
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ MAKEOVERRIDES =
|
|||
# -------------------------------
|
||||
|
||||
build=@build@
|
||||
build_os=@build_os@
|
||||
host=@host@
|
||||
host_noncanonical=@host_noncanonical@
|
||||
host_os=@host_os@
|
||||
|
|
@ -469,6 +470,7 @@ USER_H = $(srcdir)/ginclude/float.h \
|
|||
$(srcdir)/ginclude/stdnoreturn.h \
|
||||
$(srcdir)/ginclude/stdalign.h \
|
||||
$(srcdir)/ginclude/stdatomic.h \
|
||||
$(srcdir)/ginclude/stdckdint.h \
|
||||
$(EXTRA_HEADERS)
|
||||
|
||||
USER_H_INC_NEXT_PRE = @user_headers_inc_next_pre@
|
||||
|
|
@ -895,7 +897,8 @@ OPTIONS_C_EXTRA = $(PRETTY_PRINT_H)
|
|||
BCONFIG_H = bconfig.h $(build_xm_file_list)
|
||||
CONFIG_H = config.h $(host_xm_file_list)
|
||||
TCONFIG_H = tconfig.h $(xm_file_list)
|
||||
TM_P_H = tm_p.h $(tm_p_file_list)
|
||||
# Some $(target)-protos.h depends on tree.h
|
||||
TM_P_H = tm_p.h $(tm_p_file_list) $(TREE_H)
|
||||
TM_D_H = tm_d.h $(tm_d_file_list)
|
||||
TM_RUST_H = tm_rust.h $(tm_rust_file_list)
|
||||
GTM_H = tm.h $(tm_file_list) insn-constants.h
|
||||
|
|
@ -996,7 +999,7 @@ GIMPLE_H = gimple.h gimple.def gsstruct.def $(VEC_H) \
|
|||
$(GGC_H) $(BASIC_BLOCK_H) $(TREE_H) tree-ssa-operands.h \
|
||||
tree-ssa-alias.h $(INTERNAL_FN_H) $(HASH_TABLE_H) is-a.h
|
||||
GCOV_IO_H = gcov-io.h version.h auto-host.h gcov-counter.def
|
||||
RECOG_H = recog.h
|
||||
RECOG_H = recog.h $(TREE_H)
|
||||
EMIT_RTL_H = emit-rtl.h
|
||||
FLAGS_H = flags.h flag-types.h $(OPTIONS_H)
|
||||
OPTIONS_H = options.h flag-types.h $(OPTIONS_H_EXTRA)
|
||||
|
|
@ -1318,6 +1321,7 @@ ANALYZER_OBJS = \
|
|||
analyzer/pending-diagnostic.o \
|
||||
analyzer/program-point.o \
|
||||
analyzer/program-state.o \
|
||||
analyzer/ranges.o \
|
||||
analyzer/region.o \
|
||||
analyzer/region-model.o \
|
||||
analyzer/region-model-asm.o \
|
||||
|
|
@ -3036,8 +3040,8 @@ build/genhooks.o : genhooks.cc $(TARGET_DEF) $(C_TARGET_DEF) \
|
|||
$(SYSTEM_H) errors.h
|
||||
build/genmddump.o : genmddump.cc $(RTL_BASE_H) $(BCONFIG_H) $(SYSTEM_H) \
|
||||
$(CORETYPES_H) $(GTM_H) errors.h $(READ_MD_H) $(GENSUPPORT_H)
|
||||
build/genmatch.o : genmatch.cc $(BCONFIG_H) $(SYSTEM_H) \
|
||||
$(CORETYPES_H) errors.h $(HASH_TABLE_H) hash-map.h $(GGC_H) is-a.h \
|
||||
build/genmatch.o : genmatch.cc $(BCONFIG_H) $(SYSTEM_H) $(CORETYPES_H) \
|
||||
errors.h $(HASH_TABLE_H) hash-map.h $(GGC_H) is-a.h ordered-hash-map.h \
|
||||
tree.def builtins.def internal-fn.def case-cfn-macros.h $(CPPLIB_H)
|
||||
build/gencfn-macros.o : gencfn-macros.cc $(BCONFIG_H) $(SYSTEM_H) \
|
||||
$(CORETYPES_H) errors.h $(HASH_TABLE_H) hash-set.h builtins.def \
|
||||
|
|
@ -3363,8 +3367,13 @@ stmp-fixinc: gsyslimits.h macro_list fixinc_list \
|
|||
multi_dir=`echo $${ml} | sed -e 's/^[^;]*;//'`; \
|
||||
fix_dir=include-fixed$${multi_dir}; \
|
||||
if ! $(inhibit_libc) && test ! -d ${BUILD_SYSTEM_HEADER_DIR}; then \
|
||||
echo The directory that should contain system headers does not exist: >&2 ; \
|
||||
echo "The directory (BUILD_SYSTEM_HEADER_DIR) that should contain system headers does not exist:" >&2 ; \
|
||||
echo " ${BUILD_SYSTEM_HEADER_DIR}" >&2 ; \
|
||||
case ${build_os} in \
|
||||
darwin*) \
|
||||
echo "(on Darwin this usually means you need to pass the --with-sysroot= flag to point to a valid MacOS SDK)" >&2; \
|
||||
;; \
|
||||
esac; \
|
||||
tooldir_sysinc=`echo "${gcc_tooldir}/sys-include" | sed -e :a -e "s,[^/]*/\.\.\/,," -e ta`; \
|
||||
if test "x${BUILD_SYSTEM_HEADER_DIR}" = "x$${tooldir_sysinc}"; \
|
||||
then sleep 1; else exit 1; fi; \
|
||||
|
|
|
|||
|
|
@ -1,3 +1,23 @@
|
|||
2023-08-07 Sheri Bernstein <bernstein@adacore.com>
|
||||
|
||||
* libgnat/s-parame__qnx.adb: Refactor multiple returns.
|
||||
|
||||
2023-08-07 Piotr Trojanek <trojanek@adacore.com>
|
||||
|
||||
* libgnat/i-cstrin.ads (Value): Extend preconditions; adapt comment for
|
||||
the package.
|
||||
|
||||
2023-08-07 Yannick Moy <moy@adacore.com>
|
||||
|
||||
* sem_res.adb (Resolve_Call): Always call Cannot_Inline so that
|
||||
subprogram called is marked as not always inlined.
|
||||
|
||||
2023-08-07 Javier Miranda <miranda@adacore.com>
|
||||
|
||||
* sem_res.adb (Resolve_Type_Conversion): Do not warn on conversion
|
||||
to class-wide type on internally build helpers of class-wide
|
||||
preconditions.
|
||||
|
||||
2023-08-03 Sheri Bernstein <bernstein@adacore.com>
|
||||
|
||||
* libgnat/s-aridou.adb: Add pragma to exempt Improper_Returns.
|
||||
|
|
|
|||
|
|
@ -1,3 +1,403 @@
|
|||
2023-09-01 benjamin priour <priour.be@gmail.com>
|
||||
|
||||
PR analyzer/105948
|
||||
PR analyzer/94355
|
||||
* analyzer.h (is_placement_new_p): New declaration.
|
||||
* call-details.cc
|
||||
(call_details::deref_ptr_arg): New function.
|
||||
Dereference the argument at given index if possible.
|
||||
* call-details.h: Declaration of the above function.
|
||||
* kf-lang-cp.cc (is_placement_new_p): Returns true if the gcall
|
||||
is recognized as a placement new.
|
||||
(kf_operator_delete::impl_call_post): Unbinding a region and its
|
||||
descendents now poisons with POISON_KIND_DELETED.
|
||||
(register_known_functions_lang_cp): Known function "operator
|
||||
delete" is now registered only once independently of its number of
|
||||
arguments.
|
||||
* region-model.cc (region_model::eval_condition): Now
|
||||
recursively calls itself if any of the operand is wrapped in a
|
||||
cast.
|
||||
* sm-malloc.cc (malloc_state_machine::on_stmt):
|
||||
Add placement new recognition.
|
||||
* svalue.cc (poison_kind_to_str): Wording for the new PK.
|
||||
* svalue.h (enum poison_kind): Add value POISON_KIND_DELETED.
|
||||
|
||||
2023-08-31 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
|
||||
|
||||
* kf.cc: Change spelling to macOS.
|
||||
|
||||
2023-08-30 Eric Feng <ef2648@columbia.edu>
|
||||
|
||||
PR analyzer/107646
|
||||
* engine.cc (impl_region_model_context::warn): New optional
|
||||
parameter.
|
||||
* exploded-graph.h (class impl_region_model_context): Likewise.
|
||||
* region-model.cc (region_model::pop_frame): New callback
|
||||
feature for region_model::pop_frame.
|
||||
* region-model.h (struct append_regions_cb_data): Likewise.
|
||||
(class region_model): Likewise.
|
||||
(class region_model_context): New optional parameter.
|
||||
(class region_model_context_decorator): Likewise.
|
||||
|
||||
2023-08-30 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
|
||||
|
||||
* region-model.cc: Define INCLUDE_ALGORITHM.
|
||||
|
||||
2023-08-29 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/99860
|
||||
* analyzer-selftests.cc (selftest::run_analyzer_selftests): Call
|
||||
selftest::analyzer_ranges_cc_tests.
|
||||
* analyzer-selftests.h (selftest::run_analyzer_selftests): New
|
||||
decl.
|
||||
* analyzer.opt (Wanalyzer-overlapping-buffers): New option.
|
||||
* call-details.cc: Include "analyzer/ranges.h" and "make-unique.h".
|
||||
(class overlapping_buffers): New.
|
||||
(call_details::complain_about_overlap): New.
|
||||
* call-details.h (call_details::complain_about_overlap): New decl.
|
||||
* kf.cc (kf_memcpy_memmove::impl_call_pre): Call
|
||||
cd.complain_about_overlap for memcpy and memcpy_chk.
|
||||
(kf_strcat::impl_call_pre): Call cd.complain_about_overlap.
|
||||
(kf_strcpy::impl_call_pre): Likewise.
|
||||
* ranges.cc: New file.
|
||||
* ranges.h: New file.
|
||||
|
||||
2023-08-29 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* kf.cc (kf_strdup::impl_call_pre): Set size of
|
||||
dynamically-allocated buffer. Simulate copying the string from
|
||||
the source region to the new buffer.
|
||||
|
||||
2023-08-27 benjamin priour <vultkayn@gcc.gnu.org>
|
||||
|
||||
PR analyzer/96395
|
||||
* analyzer.h (class known_function): Add virtual casts
|
||||
to builtin_known_function.
|
||||
(class builtin_known_function): New subclass of known_function
|
||||
for builtins.
|
||||
* kf.cc (class kf_alloca): Now derived from
|
||||
builtin_known_function.
|
||||
(class kf_calloc): Likewise.
|
||||
(class kf_free): Likewise.
|
||||
(class kf_malloc): Likewise.
|
||||
(class kf_memcpy_memmove): Likewise.
|
||||
(class kf_memset): Likewise.
|
||||
(class kf_realloc): Likewise.
|
||||
(class kf_strchr): Likewise.
|
||||
(class kf_sprintf): Likewise.
|
||||
(class kf_strcat): Likewise.
|
||||
(class kf_strcpy): Likewise.
|
||||
(class kf_strdup): Likewise.
|
||||
(class kf_strlen): Likewise.
|
||||
(class kf_strndup): Likewise.
|
||||
(register_known_functions): Builtins are now registered as
|
||||
known_functions by name rather than by their BUILTIN_CODE.
|
||||
* known-function-manager.cc (get_normal_builtin): New overload.
|
||||
* known-function-manager.h: New overload declaration.
|
||||
* region-model.cc (region_model::get_builtin_kf): New function.
|
||||
* region-model.h (class region_model): Add declaration of
|
||||
get_builtin_kf.
|
||||
* sm-fd.cc: For called recognized as builtins, use the
|
||||
attributes of that builtin as defined in gcc/builtins.def
|
||||
rather than the user's.
|
||||
* sm-malloc.cc (malloc_state_machine::on_stmt): Likewise.
|
||||
|
||||
2023-08-25 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* access-diagram.cc (class string_region_spatial_item): Remove
|
||||
assumption that the string is written to the start of the cluster.
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* call-details.cc
|
||||
(call_details::check_for_null_terminated_string_arg): Split into
|
||||
overloads, one taking just an arg_idx, the other a new
|
||||
"include_terminator" param.
|
||||
* call-details.h: Likewise.
|
||||
* kf.cc (class kf_strcat): New.
|
||||
(kf_strcpy::impl_call_pre): Update for change to
|
||||
check_for_null_terminated_string_arg.
|
||||
(register_known_functions): Register kf_strcat.
|
||||
* region-model.cc
|
||||
(region_model::check_for_null_terminated_string_arg): Split into
|
||||
overloads, one taking just an arg_idx, the other a new
|
||||
"include_terminator" param. When returning an svalue, handle
|
||||
"include_terminator" being false by subtracting one.
|
||||
* region-model.h
|
||||
(region_model::check_for_null_terminated_string_arg): Split into
|
||||
overloads, one taking just an arg_idx, the other a new
|
||||
"include_terminator" param.
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* region-model.cc (fragment::has_null_terminator): Handle
|
||||
SK_BITS_WITHIN.
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* region-model-manager.cc
|
||||
(region_model_manager::get_or_create_initial_value): Simplify
|
||||
INIT_VAL(ELEMENT_REG(STRING_REG), CONSTANT_SVAL) to
|
||||
CONSTANT_SVAL(STRING[N]).
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* region-model.cc (fragment::has_null_terminator): Move STRING_CST
|
||||
handling to fragment::string_cst_has_null_terminator; also use it to
|
||||
handle INIT_VAL(STRING_REG).
|
||||
(fragment::string_cst_has_null_terminator): New, from above.
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* kf.cc (kf_memcpy_memmove::impl_call_pre): Reimplement using
|
||||
region_model::copy_bytes.
|
||||
* region-model.cc (region_model::read_bytes): New.
|
||||
(region_model::copy_bytes): New.
|
||||
* region-model.h (region_model::read_bytes): New decl.
|
||||
(region_model::copy_bytes): New decl.
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* region-model.cc (region_model::get_string_size): Delete both.
|
||||
* region-model.h (region_model::get_string_size): Delete both
|
||||
decls.
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* kf.cc (kf_strcpy::impl_call_pre): Reimplement using
|
||||
check_for_null_terminated_string_arg.
|
||||
* region-model.cc (region_model::get_store_bytes): Shortcut
|
||||
reading all of a string_region.
|
||||
(region_model::scan_for_null_terminator): Use get_store_value for
|
||||
the bytes rather than "unknown" when returning an unknown length.
|
||||
(region_model::write_bytes): New.
|
||||
* region-model.h (region_model::write_bytes): New decl.
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* region-model.cc (iterable_cluster::iterable_cluster): Add
|
||||
symbolic binding keys to m_symbolic_bindings.
|
||||
(iterable_cluster::has_symbolic_bindings_p): New.
|
||||
(iterable_cluster::m_symbolic_bindings): New field.
|
||||
(region_model::scan_for_null_terminator): Treat clusters with
|
||||
symbolic bindings as having unknown strlen.
|
||||
|
||||
2023-08-24 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* engine.cc (impl_path_context::impl_path_context): Add logger
|
||||
param.
|
||||
(impl_path_context::bifurcate): Add log message.
|
||||
(impl_path_context::terminate_path): Likewise.
|
||||
(impl_path_context::m_logger): New field.
|
||||
(exploded_graph::process_node): Pass logger to path_ctxt ctor.
|
||||
|
||||
2023-08-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* kf-analyzer.cc (class kf_analyzer_get_strlen): Move to kf.cc.
|
||||
(register_known_analyzer_functions): Use make_kf_strlen.
|
||||
* kf.cc (class kf_strlen::impl_call_pre): Replace with
|
||||
implementation of kf_analyzer_get_strlen from kf-analyzer.cc.
|
||||
Handle "UNKNOWN" return from check_for_null_terminated_string_arg
|
||||
by falling back to a conjured svalue.
|
||||
(make_kf_strlen): New.
|
||||
(register_known_functions): Use make_kf_strlen.
|
||||
* known-function-manager.h (make_kf_strlen): New decl.
|
||||
|
||||
2023-08-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* call-details.cc (call_details::call_details): New ctor.
|
||||
* call-details.h (call_details::call_details): New ctor decl.
|
||||
(struct call_arg_details): Move here from region-model.cc.
|
||||
* region-model.cc (region_model::check_call_format_attr): New.
|
||||
(region_model::check_call_args): Call it.
|
||||
(struct call_arg_details): Move it to call-details.h.
|
||||
* region-model.h (region_model::check_call_format_attr): New decl.
|
||||
|
||||
2023-08-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* kf.cc (class kf_fopen): New.
|
||||
(register_known_functions): Register it.
|
||||
|
||||
2023-08-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* analyzer.opt (Wanalyzer-unterminated-string): Delete.
|
||||
* call-details.cc
|
||||
(call_details::check_for_null_terminated_string_arg): Convert
|
||||
return type from void to const svalue *. Add param "out_sval".
|
||||
* call-details.h
|
||||
(call_details::check_for_null_terminated_string_arg): Likewise.
|
||||
* kf-analyzer.cc (kf_analyzer_get_strlen::impl_call_pre): Wire up
|
||||
to result of check_for_null_terminated_string_arg.
|
||||
* region-model.cc (get_strlen): Delete.
|
||||
(class unterminated_string_arg): Delete.
|
||||
(struct fragment): New.
|
||||
(class iterable_cluster): New.
|
||||
(region_model::get_store_bytes): New.
|
||||
(get_tree_for_byte_offset): New.
|
||||
(region_model::scan_for_null_terminator): New.
|
||||
(region_model::check_for_null_terminated_string_arg): Convert
|
||||
return type from void to const svalue *. Add param "out_sval".
|
||||
Reimplement in terms of scan_for_null_terminator, dropping the
|
||||
special-case for -Wanalyzer-unterminated-string.
|
||||
* region-model.h (region_model::get_store_bytes): New decl.
|
||||
(region_model::scan_for_null_terminator): New decl.
|
||||
(region_model::check_for_null_terminated_string_arg): Convert
|
||||
return type from void to const svalue *. Add param "out_sval".
|
||||
* store.cc (concrete_binding::get_byte_range): New.
|
||||
* store.h (concrete_binding::get_byte_range): New decl.
|
||||
(store_manager::get_concrete_binding): New overload.
|
||||
|
||||
2023-08-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* region-model.cc (region_model_context_decorator::add_event):
|
||||
Handle m_inner being NULL.
|
||||
* region-model.h (class region_model_context_decorator): Likewise.
|
||||
(annotating_context::warn): Likewise.
|
||||
|
||||
2023-08-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* diagnostic-manager.cc (saved_diagnostic::add_event): New.
|
||||
(saved_diagnostic::add_any_saved_events): New.
|
||||
(diagnostic_manager::add_event): New.
|
||||
(dedupe_winners::emit_best): New.
|
||||
(diagnostic_manager::emit_saved_diagnostic): Make "sd" param
|
||||
non-const. Call saved_diagnostic::add_any_saved_events.
|
||||
* diagnostic-manager.h (saved_diagnostic::add_event): New decl.
|
||||
(saved_diagnostic::add_any_saved_events): New decl.
|
||||
(saved_diagnostic::m_saved_events): New field.
|
||||
(diagnostic_manager::add_event): New decl.
|
||||
(diagnostic_manager::emit_saved_diagnostic): Make "sd" param
|
||||
non-const.
|
||||
* engine.cc (impl_region_model_context::add_event): New.
|
||||
* exploded-graph.h (impl_region_model_context::add_event): New decl.
|
||||
* region-model.cc
|
||||
(noop_region_model_context::add_event): New.
|
||||
(region_model_context_decorator::add_event): New.
|
||||
* region-model.h (region_model_context::add_event): New vfunc.
|
||||
(noop_region_model_context::add_event): New decl.
|
||||
(region_model_context_decorator::add_event): New decl.
|
||||
|
||||
2023-08-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* region-model.cc
|
||||
(class check_external_function_for_access_attr::annotating_ctxt):
|
||||
Convert to an annotating_context.
|
||||
* region-model.h (class note_adding_context): Rename to...
|
||||
(class annotating_context): ...this, updating the "warn" method.
|
||||
(note_adding_context::make_note): Replace with...
|
||||
(annotating_context::add_annotations): ...this.
|
||||
|
||||
2023-08-14 benjamin priour <vultkayn@gcc.gnu.org>
|
||||
|
||||
PR analyzer/110543
|
||||
* analyzer.opt: Add new option.
|
||||
* diagnostic-manager.cc
|
||||
(diagnostic_manager::prune_path): Call prune_system_headers.
|
||||
(prune_frame): New function that deletes all events in a frame.
|
||||
(diagnostic_manager::prune_system_headers): New function.
|
||||
* diagnostic-manager.h: Add prune_system_headers declaration.
|
||||
|
||||
2023-08-11 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/105899
|
||||
* analyzer.opt (Wanalyzer-unterminated-string): New.
|
||||
* call-details.cc
|
||||
(call_details::check_for_null_terminated_string_arg): New.
|
||||
* call-details.h
|
||||
(call_details::check_for_null_terminated_string_arg): New decl.
|
||||
* kf-analyzer.cc (class kf_analyzer_get_strlen): New.
|
||||
(register_known_analyzer_functions): Register it.
|
||||
* kf.cc (kf_error::impl_call_pre): Check that format arg is a
|
||||
valid null-terminated string.
|
||||
(kf_putenv::impl_call_pre): Likewise for the sole param.
|
||||
(kf_strchr::impl_call_pre): Likewise for the first param.
|
||||
(kf_strcpy::impl_call_pre): Likewise for the second param.
|
||||
(kf_strdup::impl_call_pre): Likewise for the sole param.
|
||||
* region-model.cc (get_strlen): New.
|
||||
(struct call_arg_details): New.
|
||||
(inform_about_expected_null_terminated_string_arg): New.
|
||||
(class unterminated_string_arg): New.
|
||||
(region_model::check_for_null_terminated_string_arg): New.
|
||||
* region-model.h
|
||||
(region_model::check_for_null_terminated_string_arg): New decl.
|
||||
|
||||
2023-08-11 Eric Feng <ef2648@columbia.edu>
|
||||
|
||||
PR analyzer/107646
|
||||
* call-details.h: New function.
|
||||
* region-model.cc (region_model::get_or_create_region_for_heap_alloc):
|
||||
New optional parameters.
|
||||
* region-model.h (class region_model): New optional parameters.
|
||||
* sm-malloc.cc (on_realloc_with_move): New function.
|
||||
(region_model::transition_ptr_sval_non_null): New function.
|
||||
|
||||
2023-08-09 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* analyzer.h (class pure_known_function_with_default_return): New
|
||||
subclass.
|
||||
* call-details.cc (const_fn_p): Move here from region-model.cc.
|
||||
(maybe_get_const_fn_result): Likewise.
|
||||
(get_result_size_in_bytes): Likewise.
|
||||
(call_details::set_any_lhs_with_defaults): New function, based on
|
||||
code in region_model::on_call_pre.
|
||||
* call-details.h (call_details::set_any_lhs_with_defaults): New
|
||||
decl.
|
||||
* diagnostic-manager.cc
|
||||
(diagnostic_manager::emit_saved_diagnostic): Log the index of the
|
||||
saved_diagnostic.
|
||||
* kf.cc (pure_known_function_with_default_return::impl_call_pre):
|
||||
New.
|
||||
(kf_memset::impl_call_pre): Set the LHS to the first param.
|
||||
(kf_putenv::impl_call_pre): Call cd.set_any_lhs_with_defaults.
|
||||
(kf_sprintf::impl_call_pre): Call cd.set_any_lhs_with_defaults.
|
||||
(class kf_stack_restore): Derive from
|
||||
pure_known_function_with_default_return.
|
||||
(class kf_stack_save): Likewise.
|
||||
(kf_strlen::impl_call_pre): Call cd.set_any_lhs_with_defaults.
|
||||
* region-model-reachability.cc (reachable_regions::handle_sval):
|
||||
Remove logic for symbolic regions for pointers.
|
||||
* region-model.cc (region_model::canonicalize): Remove purging of
|
||||
dynamic extents workaround for surplus values from
|
||||
region_model::on_call_pre's default LHS code.
|
||||
(const_fn_p): Move to call-details.cc.
|
||||
(maybe_get_const_fn_result): Likewise.
|
||||
(get_result_size_in_bytes): Likewise.
|
||||
(region_model::update_for_nonzero_return): Call
|
||||
cd.set_any_lhs_with_defaults.
|
||||
(region_model::on_call_pre): Remove the assignment to the LHS of a
|
||||
default return value, instead requiring all known_function
|
||||
implementations to write to any LHS of the call. Use
|
||||
cd.set_any_lhs_with_defaults on the non-kf paths.
|
||||
* sm-fd.cc (kf_socket::outcome_of_socket::update_model): Use
|
||||
cd.set_any_lhs_with_defaults when failing to get at fd state.
|
||||
(kf_bind::outcome_of_bind::update_model): Likewise.
|
||||
(kf_listen::outcome_of_listen::update_model): Likewise.
|
||||
(kf_accept::outcome_of_accept::update_model): Likewise.
|
||||
(kf_connect::outcome_of_connect::update_model): Likewise.
|
||||
(kf_read::impl_call_pre): Use cd.set_any_lhs_with_defaults.
|
||||
* sm-file.cc (class kf_stdio_output_fn): Derive from
|
||||
pure_known_function_with_default_return.
|
||||
(class kf_ferror): Likewise.
|
||||
(class kf_fileno): Likewise.
|
||||
(kf_fgets::impl_call_pre): Use cd.set_any_lhs_with_defaults.
|
||||
(kf_read::impl_call_pre): Likewise.
|
||||
(class kf_getc): Derive from
|
||||
pure_known_function_with_default_return.
|
||||
(class kf_getchar): Likewise.
|
||||
* varargs.cc (kf_va_arg::impl_call_pre): Use
|
||||
cd.set_any_lhs_with_defaults.
|
||||
|
||||
2023-08-04 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR analyzer/110426
|
||||
|
|
|
|||
|
|
@ -1509,10 +1509,16 @@ public:
|
|||
out.add_all_bytes_in_range (m_actual_bits);
|
||||
else
|
||||
{
|
||||
byte_range head_of_string (0, m_ellipsis_head_len);
|
||||
byte_range bytes (0, 0);
|
||||
bool valid = m_actual_bits.as_concrete_byte_range (&bytes);
|
||||
gcc_assert (valid);
|
||||
byte_range head_of_string (bytes.get_start_byte_offset (),
|
||||
m_ellipsis_head_len);
|
||||
out.add_all_bytes_in_range (head_of_string);
|
||||
byte_range tail_of_string
|
||||
(TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len,
|
||||
((bytes.get_start_byte_offset ()
|
||||
+ TREE_STRING_LENGTH (string_cst)
|
||||
- m_ellipsis_tail_len),
|
||||
m_ellipsis_tail_len);
|
||||
out.add_all_bytes_in_range (tail_of_string);
|
||||
/* Adding the above pair of ranges will also effectively add
|
||||
|
|
@ -1535,11 +1541,14 @@ public:
|
|||
tree string_cst = get_string_cst ();
|
||||
if (m_show_full_string)
|
||||
{
|
||||
for (byte_offset_t byte_idx = bytes.get_start_byte_offset ();
|
||||
byte_idx < bytes.get_next_byte_offset ();
|
||||
byte_idx = byte_idx + 1)
|
||||
add_column_for_byte (t, btm, sm, byte_idx,
|
||||
byte_idx_table_y, byte_val_table_y);
|
||||
for (byte_offset_t byte_idx_within_cluster
|
||||
= bytes.get_start_byte_offset ();
|
||||
byte_idx_within_cluster < bytes.get_next_byte_offset ();
|
||||
byte_idx_within_cluster = byte_idx_within_cluster + 1)
|
||||
add_column_for_byte
|
||||
(t, btm, sm, byte_idx_within_cluster,
|
||||
byte_idx_within_cluster - bytes.get_start_byte_offset (),
|
||||
byte_idx_table_y, byte_val_table_y);
|
||||
|
||||
if (m_show_utf8)
|
||||
{
|
||||
|
|
@ -1566,10 +1575,13 @@ public:
|
|||
= decoded_char.m_start_byte - TREE_STRING_POINTER (string_cst);
|
||||
byte_size_t size_in_bytes
|
||||
= decoded_char.m_next_byte - decoded_char.m_start_byte;
|
||||
byte_range bytes (start_byte_idx, size_in_bytes);
|
||||
byte_range cluster_bytes_for_codepoint
|
||||
(start_byte_idx + bytes.get_start_byte_offset (),
|
||||
size_in_bytes);
|
||||
|
||||
const table::rect_t code_point_table_rect
|
||||
= btm.get_table_rect (&m_string_reg, bytes,
|
||||
= btm.get_table_rect (&m_string_reg,
|
||||
cluster_bytes_for_codepoint,
|
||||
utf8_code_point_table_y, 1);
|
||||
char buf[100];
|
||||
sprintf (buf, "U+%04x", decoded_char.m_ch);
|
||||
|
|
@ -1579,7 +1591,8 @@ public:
|
|||
if (show_unichars)
|
||||
{
|
||||
const table::rect_t character_table_rect
|
||||
= btm.get_table_rect (&m_string_reg, bytes,
|
||||
= btm.get_table_rect (&m_string_reg,
|
||||
cluster_bytes_for_codepoint,
|
||||
utf8_character_table_y, 1);
|
||||
if (cpp_is_printable_char (decoded_char.m_ch))
|
||||
t.set_cell_span (character_table_rect,
|
||||
|
|
@ -1598,12 +1611,14 @@ public:
|
|||
{
|
||||
/* Head of string. */
|
||||
for (int byte_idx = 0; byte_idx < m_ellipsis_head_len; byte_idx++)
|
||||
add_column_for_byte (t, btm, sm, byte_idx,
|
||||
add_column_for_byte (t, btm, sm,
|
||||
byte_idx + bytes.get_start_byte_offset (),
|
||||
byte_idx,
|
||||
byte_idx_table_y, byte_val_table_y);
|
||||
|
||||
/* Ellipsis (two rows high). */
|
||||
const byte_range ellipsis_bytes
|
||||
(m_ellipsis_head_len,
|
||||
(m_ellipsis_head_len + bytes.get_start_byte_offset (),
|
||||
TREE_STRING_LENGTH (string_cst)
|
||||
- (m_ellipsis_head_len + m_ellipsis_tail_len));
|
||||
const table::rect_t table_rect
|
||||
|
|
@ -1616,7 +1631,9 @@ public:
|
|||
= (TREE_STRING_LENGTH (string_cst) - m_ellipsis_tail_len);
|
||||
byte_idx < TREE_STRING_LENGTH (string_cst);
|
||||
byte_idx++)
|
||||
add_column_for_byte (t, btm, sm, byte_idx,
|
||||
add_column_for_byte (t, btm, sm,
|
||||
byte_idx + bytes.get_start_byte_offset (),
|
||||
byte_idx,
|
||||
byte_idx_table_y, byte_val_table_y);
|
||||
}
|
||||
|
||||
|
|
@ -1660,25 +1677,27 @@ private:
|
|||
|
||||
void add_column_for_byte (table &t, const bit_to_table_map &btm,
|
||||
style_manager &sm,
|
||||
const byte_offset_t byte_idx,
|
||||
const byte_offset_t byte_idx_within_cluster,
|
||||
const byte_offset_t byte_idx_within_string,
|
||||
const int byte_idx_table_y,
|
||||
const int byte_val_table_y) const
|
||||
{
|
||||
tree string_cst = get_string_cst ();
|
||||
gcc_assert (byte_idx >= 0);
|
||||
gcc_assert (byte_idx < TREE_STRING_LENGTH (string_cst));
|
||||
gcc_assert (byte_idx_within_string >= 0);
|
||||
gcc_assert (byte_idx_within_string < TREE_STRING_LENGTH (string_cst));
|
||||
|
||||
const byte_range bytes (byte_idx, 1);
|
||||
const byte_range bytes (byte_idx_within_cluster, 1);
|
||||
if (1) // show_byte_indices
|
||||
{
|
||||
const table::rect_t idx_table_rect
|
||||
= btm.get_table_rect (&m_string_reg, bytes, byte_idx_table_y, 1);
|
||||
t.set_cell_span (idx_table_rect,
|
||||
fmt_styled_string (sm, "[%li]",
|
||||
byte_idx.ulow ()));
|
||||
byte_idx_within_string.ulow ()));
|
||||
}
|
||||
|
||||
char byte_val = TREE_STRING_POINTER (string_cst)[byte_idx.ulow ()];
|
||||
char byte_val
|
||||
= TREE_STRING_POINTER (string_cst)[byte_idx_within_string.ulow ()];
|
||||
const table::rect_t val_table_rect
|
||||
= btm.get_table_rect (&m_string_reg, bytes, byte_val_table_y, 1);
|
||||
table_cell_content content (make_cell_content_for_byte (sm, byte_val));
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ run_analyzer_selftests ()
|
|||
analyzer_function_set_cc_tests ();
|
||||
analyzer_program_point_cc_tests ();
|
||||
analyzer_program_state_cc_tests ();
|
||||
analyzer_ranges_cc_tests ();
|
||||
analyzer_region_model_cc_tests ();
|
||||
analyzer_sm_file_cc_tests ();
|
||||
analyzer_sm_signal_cc_tests ();
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ extern void analyzer_constraint_manager_cc_tests ();
|
|||
extern void analyzer_function_set_cc_tests ();
|
||||
extern void analyzer_program_point_cc_tests ();
|
||||
extern void analyzer_program_state_cc_tests ();
|
||||
extern void analyzer_ranges_cc_tests ();
|
||||
extern void analyzer_region_model_cc_tests ();
|
||||
extern void analyzer_sm_file_cc_tests ();
|
||||
extern void analyzer_sm_signal_cc_tests ();
|
||||
|
|
|
|||
|
|
@ -128,6 +128,10 @@ struct interesting_t;
|
|||
|
||||
class feasible_node;
|
||||
|
||||
class known_function;
|
||||
class builtin_known_function;
|
||||
class internal_known_function;
|
||||
|
||||
/* Forward decls of functions. */
|
||||
|
||||
extern void dump_tree (pretty_printer *pp, tree t);
|
||||
|
|
@ -279,6 +283,24 @@ public:
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
virtual const builtin_known_function *
|
||||
dyn_cast_builtin_kf () const { return NULL; }
|
||||
};
|
||||
|
||||
/* Subclass of known_function for builtin functions. */
|
||||
|
||||
class builtin_known_function : public known_function
|
||||
{
|
||||
public:
|
||||
virtual enum built_in_function builtin_code () const = 0;
|
||||
tree builtin_decl () const {
|
||||
gcc_assert (builtin_code () < END_BUILTINS);
|
||||
return builtin_info[builtin_code ()].decl;
|
||||
}
|
||||
|
||||
const builtin_known_function *
|
||||
dyn_cast_builtin_kf () const final override { return this; }
|
||||
};
|
||||
|
||||
/* Subclass of known_function for IFN_* functions. */
|
||||
|
|
@ -293,6 +315,16 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/* Abstract subclass of known_function that merely sets the return
|
||||
value of the function (based on function attributes), and assumes
|
||||
it has no side-effects. */
|
||||
|
||||
class pure_known_function_with_default_return : public known_function
|
||||
{
|
||||
public:
|
||||
void impl_call_pre (const call_details &cd) const override;
|
||||
};
|
||||
|
||||
extern void register_known_functions (known_function_manager &mgr);
|
||||
extern void register_known_analyzer_functions (known_function_manager &kfm);
|
||||
extern void register_known_fd_functions (known_function_manager &kfm);
|
||||
|
|
@ -391,6 +423,7 @@ extern bool is_std_named_call_p (const_tree fndecl, const char *funcname,
|
|||
const gcall *call, unsigned int num_args);
|
||||
extern bool is_setjmp_call_p (const gcall *call);
|
||||
extern bool is_longjmp_call_p (const gcall *call);
|
||||
extern bool is_placement_new_p (const gcall *call);
|
||||
|
||||
extern const char *get_user_facing_name (const gcall *call);
|
||||
|
||||
|
|
|
|||
|
|
@ -154,6 +154,10 @@ Wanalyzer-out-of-bounds
|
|||
Common Var(warn_analyzer_out_of_bounds) Init(1) Warning
|
||||
Warn about code paths in which a write or read to a buffer is out-of-bounds.
|
||||
|
||||
Wanalyzer-overlapping-buffers
|
||||
Common Var(warn_analyzer_overlapping_buffers) Init(1) Warning
|
||||
Warn about code paths in which undefined behavior would occur due to overlapping buffers.
|
||||
|
||||
Wanalyzer-possible-null-argument
|
||||
Common Var(warn_analyzer_possible_null_argument) Init(1) Warning
|
||||
Warn about code paths in which a possibly-NULL value is passed to a must-not-be-NULL function argument.
|
||||
|
|
@ -290,6 +294,10 @@ fanalyzer-transitivity
|
|||
Common Var(flag_analyzer_transitivity) Init(0)
|
||||
Enable transitivity of constraints during analysis.
|
||||
|
||||
fanalyzer-show-events-in-system-headers
|
||||
Common Var(flag_analyzer_show_events_in_system_headers) Init(0)
|
||||
Show events within system headers in analyzer execution paths.
|
||||
|
||||
fanalyzer-call-summaries
|
||||
Common Var(flag_analyzer_call_summaries) Init(0)
|
||||
Approximate the effect of function calls to simplify analysis.
|
||||
|
|
|
|||
|
|
@ -34,8 +34,10 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "gimple-pretty-print.h"
|
||||
#include "analyzer/region-model.h"
|
||||
#include "analyzer/call-details.h"
|
||||
#include "analyzer/ranges.h"
|
||||
#include "stringpool.h"
|
||||
#include "attribs.h"
|
||||
#include "make-unique.h"
|
||||
|
||||
#if ENABLE_ANALYZER
|
||||
|
||||
|
|
@ -58,6 +60,16 @@ call_details::call_details (const gcall *call, region_model *model,
|
|||
}
|
||||
}
|
||||
|
||||
/* call_details's ctor: copy CD, but override the context,
|
||||
using CTXT instead. */
|
||||
|
||||
call_details::call_details (const call_details &cd,
|
||||
region_model_context *ctxt)
|
||||
{
|
||||
*this = cd;
|
||||
m_ctxt = ctxt;
|
||||
}
|
||||
|
||||
/* Get the manager from m_model. */
|
||||
|
||||
region_model_manager *
|
||||
|
|
@ -105,6 +117,135 @@ call_details::maybe_set_lhs (const svalue *result) const
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Return true if CD is known to be a call to a function with
|
||||
__attribute__((const)). */
|
||||
|
||||
static bool
|
||||
const_fn_p (const call_details &cd)
|
||||
{
|
||||
tree fndecl = cd.get_fndecl_for_call ();
|
||||
if (!fndecl)
|
||||
return false;
|
||||
gcc_assert (DECL_P (fndecl));
|
||||
return TREE_READONLY (fndecl);
|
||||
}
|
||||
|
||||
/* If this CD is known to be a call to a function with
|
||||
__attribute__((const)), attempt to get a const_fn_result_svalue
|
||||
based on the arguments, or return NULL otherwise. */
|
||||
|
||||
static const svalue *
|
||||
maybe_get_const_fn_result (const call_details &cd)
|
||||
{
|
||||
if (!const_fn_p (cd))
|
||||
return NULL;
|
||||
|
||||
unsigned num_args = cd.num_args ();
|
||||
if (num_args > const_fn_result_svalue::MAX_INPUTS)
|
||||
/* Too many arguments. */
|
||||
return NULL;
|
||||
|
||||
auto_vec<const svalue *> inputs (num_args);
|
||||
for (unsigned arg_idx = 0; arg_idx < num_args; arg_idx++)
|
||||
{
|
||||
const svalue *arg_sval = cd.get_arg_svalue (arg_idx);
|
||||
if (!arg_sval->can_have_associated_state_p ())
|
||||
return NULL;
|
||||
inputs.quick_push (arg_sval);
|
||||
}
|
||||
|
||||
region_model_manager *mgr = cd.get_manager ();
|
||||
const svalue *sval
|
||||
= mgr->get_or_create_const_fn_result_svalue (cd.get_lhs_type (),
|
||||
cd.get_fndecl_for_call (),
|
||||
inputs);
|
||||
return sval;
|
||||
}
|
||||
|
||||
/* Look for attribute "alloc_size" on the called function and, if found,
|
||||
return a symbolic value of type size_type_node for the allocation size
|
||||
based on the call's parameters.
|
||||
Otherwise, return null. */
|
||||
|
||||
static const svalue *
|
||||
get_result_size_in_bytes (const call_details &cd)
|
||||
{
|
||||
const tree attr = cd.lookup_function_attribute ("alloc_size");
|
||||
if (!attr)
|
||||
return nullptr;
|
||||
|
||||
const tree atval_1 = TREE_VALUE (attr);
|
||||
if (!atval_1)
|
||||
return nullptr;
|
||||
|
||||
unsigned argidx1 = TREE_INT_CST_LOW (TREE_VALUE (atval_1)) - 1;
|
||||
if (cd.num_args () <= argidx1)
|
||||
return nullptr;
|
||||
|
||||
const svalue *sval_arg1 = cd.get_arg_svalue (argidx1);
|
||||
|
||||
if (const tree atval_2 = TREE_CHAIN (atval_1))
|
||||
{
|
||||
/* Two arguments. */
|
||||
unsigned argidx2 = TREE_INT_CST_LOW (TREE_VALUE (atval_2)) - 1;
|
||||
if (cd.num_args () <= argidx2)
|
||||
return nullptr;
|
||||
const svalue *sval_arg2 = cd.get_arg_svalue (argidx2);
|
||||
/* TODO: ideally we shouldn't need this cast here;
|
||||
see PR analyzer/110902. */
|
||||
return cd.get_manager ()->get_or_create_cast
|
||||
(size_type_node,
|
||||
cd.get_manager ()->get_or_create_binop (size_type_node,
|
||||
MULT_EXPR,
|
||||
sval_arg1, sval_arg2));
|
||||
}
|
||||
else
|
||||
/* Single argument. */
|
||||
return cd.get_manager ()->get_or_create_cast (size_type_node, sval_arg1);
|
||||
}
|
||||
|
||||
/* If this call has an LHS, assign a value to it based on attributes
|
||||
of the function:
|
||||
- if __attribute__((const)), use a const_fn_result_svalue,
|
||||
- if __attribute__((malloc)), use a heap-allocated region with
|
||||
unknown content
|
||||
- otherwise, use a conjured_svalue.
|
||||
|
||||
If __attribute__((alloc_size), set the dynamic extents on the region
|
||||
pointed to. */
|
||||
|
||||
void
|
||||
call_details::set_any_lhs_with_defaults () const
|
||||
{
|
||||
if (!m_lhs_region)
|
||||
return;
|
||||
|
||||
const svalue *sval = maybe_get_const_fn_result (*this);
|
||||
if (!sval)
|
||||
{
|
||||
region_model_manager *mgr = get_manager ();
|
||||
if (lookup_function_attribute ("malloc"))
|
||||
{
|
||||
const region *new_reg
|
||||
= m_model->get_or_create_region_for_heap_alloc (NULL, m_ctxt);
|
||||
m_model->mark_region_as_unknown (new_reg, NULL);
|
||||
sval = mgr->get_ptr_svalue (get_lhs_type (), new_reg);
|
||||
}
|
||||
else
|
||||
/* For the common case of functions without __attribute__((const)),
|
||||
use a conjured value, and purge any prior state involving that
|
||||
value (in case this is in a loop). */
|
||||
sval = get_or_create_conjured_svalue (m_lhs_region);
|
||||
if (const svalue *size_in_bytes = get_result_size_in_bytes (*this))
|
||||
{
|
||||
const region *reg
|
||||
= m_model->deref_rvalue (sval, NULL_TREE, m_ctxt, false);
|
||||
m_model->set_dynamic_extents (reg, size_in_bytes, m_ctxt);
|
||||
}
|
||||
}
|
||||
maybe_set_lhs (sval);
|
||||
}
|
||||
|
||||
/* Return the number of arguments used by the call statement. */
|
||||
|
||||
unsigned
|
||||
|
|
@ -154,6 +295,17 @@ call_details::get_arg_svalue (unsigned idx) const
|
|||
return m_model->get_rvalue (arg, m_ctxt);
|
||||
}
|
||||
|
||||
/* If argument IDX's svalue at the callsite is of pointer type,
|
||||
return the region it points to.
|
||||
Otherwise return NULL. */
|
||||
|
||||
const region *
|
||||
call_details::deref_ptr_arg (unsigned idx) const
|
||||
{
|
||||
const svalue *ptr_sval = get_arg_svalue (idx);
|
||||
return m_model->deref_rvalue (ptr_sval, get_arg_tree (idx), m_ctxt);
|
||||
}
|
||||
|
||||
/* Attempt to get the string literal for argument IDX, or return NULL
|
||||
otherwise.
|
||||
For use when implementing "__analyzer_*" functions that take
|
||||
|
|
@ -247,6 +399,129 @@ call_details::lookup_function_attribute (const char *attr_name) const
|
|||
return lookup_attribute (attr_name, TYPE_ATTRIBUTES (allocfntype));
|
||||
}
|
||||
|
||||
void
|
||||
call_details::check_for_null_terminated_string_arg (unsigned arg_idx) const
|
||||
{
|
||||
check_for_null_terminated_string_arg (arg_idx, false, nullptr);
|
||||
}
|
||||
|
||||
const svalue *
|
||||
call_details::
|
||||
check_for_null_terminated_string_arg (unsigned arg_idx,
|
||||
bool include_terminator,
|
||||
const svalue **out_sval) const
|
||||
{
|
||||
region_model *model = get_model ();
|
||||
return model->check_for_null_terminated_string_arg (*this,
|
||||
arg_idx,
|
||||
include_terminator,
|
||||
out_sval);
|
||||
}
|
||||
|
||||
/* A subclass of pending_diagnostic for complaining about overlapping
|
||||
buffers. */
|
||||
|
||||
class overlapping_buffers
|
||||
: public pending_diagnostic_subclass<overlapping_buffers>
|
||||
{
|
||||
public:
|
||||
overlapping_buffers (tree fndecl)
|
||||
: m_fndecl (fndecl)
|
||||
{
|
||||
}
|
||||
|
||||
const char *get_kind () const final override
|
||||
{
|
||||
return "overlapping_buffers";
|
||||
}
|
||||
|
||||
bool operator== (const overlapping_buffers &other) const
|
||||
{
|
||||
return m_fndecl == other.m_fndecl;
|
||||
}
|
||||
|
||||
int get_controlling_option () const final override
|
||||
{
|
||||
return OPT_Wanalyzer_overlapping_buffers;
|
||||
}
|
||||
|
||||
bool emit (rich_location *rich_loc, logger *) final override
|
||||
{
|
||||
auto_diagnostic_group d;
|
||||
|
||||
bool warned;
|
||||
warned = warning_at (rich_loc, get_controlling_option (),
|
||||
"overlapping buffers passed as arguments to %qD",
|
||||
m_fndecl);
|
||||
|
||||
// TODO: draw a picture?
|
||||
|
||||
if (warned)
|
||||
inform (DECL_SOURCE_LOCATION (m_fndecl),
|
||||
"the behavior of %qD is undefined for overlapping buffers",
|
||||
m_fndecl);
|
||||
|
||||
return warned;
|
||||
}
|
||||
|
||||
label_text describe_final_event (const evdesc::final_event &ev) final override
|
||||
{
|
||||
return ev.formatted_print
|
||||
("overlapping buffers passed as arguments to %qD",
|
||||
m_fndecl);
|
||||
}
|
||||
|
||||
private:
|
||||
tree m_fndecl;
|
||||
};
|
||||
|
||||
|
||||
/* Check if the buffers pointed to by arguments ARG_IDX_A and ARG_IDX_B
|
||||
(zero-based) overlap, when considering them both to be of size
|
||||
NUM_BYTES_READ_SVAL.
|
||||
|
||||
If they do overlap, complain to the context. */
|
||||
|
||||
void
|
||||
call_details::complain_about_overlap (unsigned arg_idx_a,
|
||||
unsigned arg_idx_b,
|
||||
const svalue *num_bytes_read_sval) const
|
||||
{
|
||||
region_model_context *ctxt = get_ctxt ();
|
||||
if (!ctxt)
|
||||
return;
|
||||
|
||||
region_model *model = get_model ();
|
||||
region_model_manager *mgr = model->get_manager ();
|
||||
|
||||
const svalue *arg_a_ptr_sval = get_arg_svalue (arg_idx_a);
|
||||
if (arg_a_ptr_sval->get_kind () == SK_UNKNOWN)
|
||||
return;
|
||||
const region *arg_a_reg = model->deref_rvalue (arg_a_ptr_sval,
|
||||
get_arg_tree (arg_idx_a),
|
||||
ctxt);
|
||||
const svalue *arg_b_ptr_sval = get_arg_svalue (arg_idx_b);
|
||||
if (arg_b_ptr_sval->get_kind () == SK_UNKNOWN)
|
||||
return;
|
||||
const region *arg_b_reg = model->deref_rvalue (arg_b_ptr_sval,
|
||||
get_arg_tree (arg_idx_b),
|
||||
ctxt);
|
||||
if (arg_a_reg->get_base_region () != arg_b_reg->get_base_region ())
|
||||
return;
|
||||
|
||||
/* Are they within NUM_BYTES_READ_SVAL of each other? */
|
||||
symbolic_byte_range byte_range_a (arg_a_reg->get_offset (mgr),
|
||||
num_bytes_read_sval,
|
||||
*mgr);
|
||||
symbolic_byte_range byte_range_b (arg_b_reg->get_offset (mgr),
|
||||
num_bytes_read_sval,
|
||||
*mgr);
|
||||
if (!byte_range_a.intersection (byte_range_b, *model).is_true ())
|
||||
return;
|
||||
|
||||
ctxt->warn (make_unique<overlapping_buffers> (get_fndecl_for_call ()));
|
||||
}
|
||||
|
||||
} // namespace ana
|
||||
|
||||
#endif /* #if ENABLE_ANALYZER */
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ class call_details
|
|||
public:
|
||||
call_details (const gcall *call, region_model *model,
|
||||
region_model_context *ctxt);
|
||||
call_details (const call_details &cd, region_model_context *ctxt);
|
||||
|
||||
region_model *get_model () const { return m_model; }
|
||||
region_model_manager *get_manager () const;
|
||||
|
|
@ -41,6 +42,7 @@ public:
|
|||
const region *get_lhs_region () const { return m_lhs_region; }
|
||||
|
||||
bool maybe_set_lhs (const svalue *result) const;
|
||||
void set_any_lhs_with_defaults () const;
|
||||
|
||||
unsigned num_args () const;
|
||||
bool arg_is_pointer_p (unsigned idx) const
|
||||
|
|
@ -48,6 +50,10 @@ public:
|
|||
return POINTER_TYPE_P (get_arg_type (idx));
|
||||
}
|
||||
bool arg_is_size_p (unsigned idx) const;
|
||||
bool arg_is_integral_p (unsigned idx) const
|
||||
{
|
||||
return INTEGRAL_TYPE_P (get_arg_type (idx));
|
||||
}
|
||||
|
||||
const gcall *get_call_stmt () const { return m_call; }
|
||||
location_t get_location () const;
|
||||
|
|
@ -55,6 +61,7 @@ public:
|
|||
tree get_arg_tree (unsigned idx) const;
|
||||
tree get_arg_type (unsigned idx) const;
|
||||
const svalue *get_arg_svalue (unsigned idx) const;
|
||||
const region *deref_ptr_arg (unsigned idx) const;
|
||||
const char *get_arg_string_literal (unsigned idx) const;
|
||||
|
||||
tree get_fndecl_for_call () const;
|
||||
|
|
@ -66,6 +73,18 @@ public:
|
|||
|
||||
tree lookup_function_attribute (const char *attr_name) const;
|
||||
|
||||
void
|
||||
check_for_null_terminated_string_arg (unsigned arg_idx) const;
|
||||
const svalue *
|
||||
check_for_null_terminated_string_arg (unsigned arg_idx,
|
||||
bool include_terminator,
|
||||
const svalue **out_sval) const;
|
||||
|
||||
void
|
||||
complain_about_overlap (unsigned arg_idx_a,
|
||||
unsigned arg_idx_b,
|
||||
const svalue *num_bytes_read_sval) const;
|
||||
|
||||
private:
|
||||
const gcall *m_call;
|
||||
region_model *m_model;
|
||||
|
|
@ -74,6 +93,35 @@ private:
|
|||
const region *m_lhs_region;
|
||||
};
|
||||
|
||||
/* A bundle of information about a problematic argument at a callsite
|
||||
for use by pending_diagnostic subclasses for reporting and
|
||||
for deduplication. */
|
||||
|
||||
struct call_arg_details
|
||||
{
|
||||
public:
|
||||
call_arg_details (const call_details &cd, unsigned arg_idx)
|
||||
: m_call (cd.get_call_stmt ()),
|
||||
m_called_fndecl (cd.get_fndecl_for_call ()),
|
||||
m_arg_idx (arg_idx),
|
||||
m_arg_expr (cd.get_arg_tree (arg_idx))
|
||||
{
|
||||
}
|
||||
|
||||
bool operator== (const call_arg_details &other) const
|
||||
{
|
||||
return (m_call == other.m_call
|
||||
&& m_called_fndecl == other.m_called_fndecl
|
||||
&& m_arg_idx == other.m_arg_idx
|
||||
&& pending_diagnostic::same_tree_p (m_arg_expr, other.m_arg_expr));
|
||||
}
|
||||
|
||||
const gcall *m_call;
|
||||
tree m_called_fndecl;
|
||||
unsigned m_arg_idx; // 0-based
|
||||
tree m_arg_expr;
|
||||
};
|
||||
|
||||
} // namespace ana
|
||||
|
||||
#endif /* GCC_ANALYZER_CALL_DETAILS_H */
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tree.h"
|
||||
#include "input.h"
|
||||
#include "pretty-print.h"
|
||||
#include "gcc-rich-location.h"
|
||||
#include "gimple-pretty-print.h"
|
||||
|
|
@ -720,6 +721,15 @@ saved_diagnostic::add_note (std::unique_ptr<pending_note> pn)
|
|||
m_notes.safe_push (pn.release ());
|
||||
}
|
||||
|
||||
/* Add EVENT to this diagnostic. */
|
||||
|
||||
void
|
||||
saved_diagnostic::add_event (std::unique_ptr<checker_event> event)
|
||||
{
|
||||
gcc_assert (event);
|
||||
m_saved_events.safe_push (event.release ());
|
||||
}
|
||||
|
||||
/* Return a new json::object of the form
|
||||
{"sm": optional str,
|
||||
"enode": int,
|
||||
|
|
@ -889,6 +899,19 @@ saved_diagnostic::supercedes_p (const saved_diagnostic &other) const
|
|||
return m_d->supercedes_p (*other.m_d);
|
||||
}
|
||||
|
||||
/* Move any saved checker_events from this saved_diagnostic to
|
||||
the end of DST_PATH. */
|
||||
|
||||
void
|
||||
saved_diagnostic::add_any_saved_events (checker_path &dst_path)
|
||||
{
|
||||
for (auto &event : m_saved_events)
|
||||
{
|
||||
dst_path.add_event (std::unique_ptr<checker_event> (event));
|
||||
event = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit any pending notes owned by this diagnostic. */
|
||||
|
||||
void
|
||||
|
|
@ -1056,6 +1079,20 @@ diagnostic_manager::add_note (std::unique_ptr<pending_note> pn)
|
|||
sd->add_note (std::move (pn));
|
||||
}
|
||||
|
||||
/* Add EVENT to the most recent saved_diagnostic. */
|
||||
|
||||
void
|
||||
diagnostic_manager::add_event (std::unique_ptr<checker_event> event)
|
||||
{
|
||||
LOG_FUNC (get_logger ());
|
||||
gcc_assert (event);
|
||||
|
||||
/* Get most recent saved_diagnostic. */
|
||||
gcc_assert (m_saved_diagnostics.length () > 0);
|
||||
saved_diagnostic *sd = m_saved_diagnostics[m_saved_diagnostics.length () - 1];
|
||||
sd->add_event (std::move (event));
|
||||
}
|
||||
|
||||
/* Return a new json::object of the form
|
||||
{"diagnostics" : [obj for saved_diagnostic]}. */
|
||||
|
||||
|
|
@ -1307,7 +1344,7 @@ public:
|
|||
{
|
||||
saved_diagnostic **slot = m_map.get (key);
|
||||
gcc_assert (*slot);
|
||||
const saved_diagnostic *sd = *slot;
|
||||
saved_diagnostic *sd = *slot;
|
||||
dm->emit_saved_diagnostic (eg, *sd);
|
||||
}
|
||||
}
|
||||
|
|
@ -1369,10 +1406,11 @@ diagnostic_manager::emit_saved_diagnostics (const exploded_graph &eg)
|
|||
|
||||
void
|
||||
diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
|
||||
const saved_diagnostic &sd)
|
||||
saved_diagnostic &sd)
|
||||
{
|
||||
LOG_SCOPE (get_logger ());
|
||||
log ("sd: %qs at SN: %i", sd.m_d->get_kind (), sd.m_snode->m_index);
|
||||
log ("sd[%i]: %qs at SN: %i",
|
||||
sd.get_index (), sd.m_d->get_kind (), sd.m_snode->m_index);
|
||||
log ("num dupes: %i", sd.get_num_dupes ());
|
||||
|
||||
pretty_printer *pp = global_dc->printer->clone ();
|
||||
|
|
@ -1393,6 +1431,11 @@ diagnostic_manager::emit_saved_diagnostic (const exploded_graph &eg,
|
|||
/* Now prune it to just cover the most pertinent events. */
|
||||
prune_path (&emission_path, sd.m_sm, sd.m_sval, sd.m_state);
|
||||
|
||||
/* Add any saved events to the path, giving contextual information
|
||||
about what the analyzer was simulating as the diagnostic was
|
||||
generated. These don't get pruned, as they are probably pertinent. */
|
||||
sd.add_any_saved_events (emission_path);
|
||||
|
||||
/* Add a final event to the path, covering the diagnostic itself.
|
||||
We use the final enode from the epath, which might be different from
|
||||
the sd.m_enode, as the dedupe code doesn't care about enodes, just
|
||||
|
|
@ -2281,6 +2324,8 @@ diagnostic_manager::prune_path (checker_path *path,
|
|||
path->maybe_log (get_logger (), "path");
|
||||
prune_for_sm_diagnostic (path, sm, sval, state);
|
||||
prune_interproc_events (path);
|
||||
if (! flag_analyzer_show_events_in_system_headers)
|
||||
prune_system_headers (path);
|
||||
consolidate_conditions (path);
|
||||
finish_pruning (path);
|
||||
path->maybe_log (get_logger (), "pruned");
|
||||
|
|
@ -2667,6 +2712,99 @@ diagnostic_manager::prune_interproc_events (checker_path *path) const
|
|||
while (changed);
|
||||
}
|
||||
|
||||
/* Remove everything within [call point, IDX]. For consistency,
|
||||
IDX should represent the return event of the frame to delete,
|
||||
or if there is none it should be the last event of the frame.
|
||||
After this function, IDX designates the event prior to calling
|
||||
this frame. */
|
||||
|
||||
static void
|
||||
prune_frame (checker_path *path, int &idx)
|
||||
{
|
||||
gcc_assert (idx >= 0);
|
||||
int nesting = 1;
|
||||
if (path->get_checker_event (idx)->is_return_p ())
|
||||
nesting = 0;
|
||||
do
|
||||
{
|
||||
if (path->get_checker_event (idx)->is_call_p ())
|
||||
nesting--;
|
||||
else if (path->get_checker_event (idx)->is_return_p ())
|
||||
nesting++;
|
||||
|
||||
path->delete_event (idx--);
|
||||
} while (idx >= 0 && nesting != 0);
|
||||
}
|
||||
|
||||
/* This function is called when fanalyzer-show-events-in-system-headers
|
||||
is disabled and will prune the diagnostic of all events within a
|
||||
system header, only keeping the entry and exit events to the header.
|
||||
This should be called after diagnostic_manager::prune_interproc_events
|
||||
so that sucessive events [system header call, system header return]
|
||||
are preserved thereafter.
|
||||
|
||||
Given a diagnostics path diving into a system header in the form
|
||||
[
|
||||
prefix events...,
|
||||
system header call,
|
||||
system header entry,
|
||||
events within system headers...,
|
||||
system header return,
|
||||
suffix events...
|
||||
]
|
||||
|
||||
then transforms it into
|
||||
[
|
||||
prefix events...,
|
||||
system header call,
|
||||
system header return,
|
||||
suffix events...
|
||||
]. */
|
||||
|
||||
void
|
||||
diagnostic_manager::prune_system_headers (checker_path *path) const
|
||||
{
|
||||
int idx = (signed)path->num_events () - 1;
|
||||
while (idx >= 0)
|
||||
{
|
||||
const checker_event *event = path->get_checker_event (idx);
|
||||
/* Prune everything between
|
||||
[..., system entry, (...), system return, ...]. */
|
||||
if (event->is_return_p ()
|
||||
&& in_system_header_at (event->get_location ()))
|
||||
{
|
||||
int ret_idx = idx;
|
||||
prune_frame (path, idx);
|
||||
|
||||
if (get_logger ())
|
||||
{
|
||||
log ("filtering system headers events %i-%i:",
|
||||
idx, ret_idx);
|
||||
}
|
||||
// Delete function entry within system headers.
|
||||
if (idx >= 0)
|
||||
{
|
||||
event = path->get_checker_event (idx);
|
||||
if (event->is_function_entry_p ()
|
||||
&& in_system_header_at (event->get_location ()))
|
||||
{
|
||||
if (get_logger ())
|
||||
{
|
||||
label_text desc (event->get_desc (false));
|
||||
log ("filtering event %i:"
|
||||
"system header entry event: %s",
|
||||
idx, desc.get ());
|
||||
}
|
||||
|
||||
path->delete_event (idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idx--;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true iff event IDX within PATH is on the same line as REF_EXP_LOC. */
|
||||
|
||||
static bool
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public:
|
|||
bool operator== (const saved_diagnostic &other) const;
|
||||
|
||||
void add_note (std::unique_ptr<pending_note> pn);
|
||||
void add_event (std::unique_ptr<checker_event> event);
|
||||
|
||||
json::object *to_json () const;
|
||||
|
||||
|
|
@ -64,6 +65,8 @@ public:
|
|||
|
||||
bool supercedes_p (const saved_diagnostic &other) const;
|
||||
|
||||
void add_any_saved_events (checker_path &dst_path);
|
||||
|
||||
void emit_any_notes () const;
|
||||
|
||||
//private:
|
||||
|
|
@ -87,6 +90,12 @@ private:
|
|||
|
||||
auto_vec<const saved_diagnostic *> m_duplicates;
|
||||
auto_delete_vec <pending_note> m_notes;
|
||||
|
||||
/* Optionally: additional context-dependent events to be emitted
|
||||
immediately before the warning_event, giving more details of what
|
||||
operation was being simulated when a diagnostic was saved
|
||||
e.g. "looking for null terminator in param 2 of 'foo'". */
|
||||
auto_delete_vec <checker_event> m_saved_events;
|
||||
};
|
||||
|
||||
class path_builder;
|
||||
|
|
@ -124,11 +133,12 @@ public:
|
|||
std::unique_ptr<pending_diagnostic> d);
|
||||
|
||||
void add_note (std::unique_ptr<pending_note> pn);
|
||||
void add_event (std::unique_ptr<checker_event> event);
|
||||
|
||||
void emit_saved_diagnostics (const exploded_graph &eg);
|
||||
|
||||
void emit_saved_diagnostic (const exploded_graph &eg,
|
||||
const saved_diagnostic &sd);
|
||||
saved_diagnostic &sd);
|
||||
|
||||
unsigned get_num_diagnostics () const
|
||||
{
|
||||
|
|
@ -180,6 +190,7 @@ private:
|
|||
state_machine::state_t state) const;
|
||||
void update_for_unsuitable_sm_exprs (tree *expr) const;
|
||||
void prune_interproc_events (checker_path *path) const;
|
||||
void prune_system_headers (checker_path *path) const;
|
||||
void consolidate_conditions (checker_path *path) const;
|
||||
void finish_pruning (checker_path *path) const;
|
||||
|
||||
|
|
|
|||
|
|
@ -115,10 +115,12 @@ impl_region_model_context (program_state *state,
|
|||
}
|
||||
|
||||
bool
|
||||
impl_region_model_context::warn (std::unique_ptr<pending_diagnostic> d)
|
||||
impl_region_model_context::warn (std::unique_ptr<pending_diagnostic> d,
|
||||
const stmt_finder *custom_finder)
|
||||
{
|
||||
LOG_FUNC (get_logger ());
|
||||
if (m_stmt == NULL && m_stmt_finder == NULL)
|
||||
auto curr_stmt_finder = custom_finder ? custom_finder : m_stmt_finder;
|
||||
if (m_stmt == NULL && curr_stmt_finder == NULL)
|
||||
{
|
||||
if (get_logger ())
|
||||
get_logger ()->log ("rejecting diagnostic: no stmt");
|
||||
|
|
@ -129,7 +131,7 @@ impl_region_model_context::warn (std::unique_ptr<pending_diagnostic> d)
|
|||
bool terminate_path = d->terminate_path_p ();
|
||||
if (m_eg->get_diagnostic_manager ().add_diagnostic
|
||||
(m_enode_for_diag, m_enode_for_diag->get_supernode (),
|
||||
m_stmt, m_stmt_finder, std::move (d)))
|
||||
m_stmt, curr_stmt_finder, std::move (d)))
|
||||
{
|
||||
if (m_path_ctxt
|
||||
&& terminate_path
|
||||
|
|
@ -149,6 +151,14 @@ impl_region_model_context::add_note (std::unique_ptr<pending_note> pn)
|
|||
m_eg->get_diagnostic_manager ().add_note (std::move (pn));
|
||||
}
|
||||
|
||||
void
|
||||
impl_region_model_context::add_event (std::unique_ptr<checker_event> event)
|
||||
{
|
||||
LOG_FUNC (get_logger ());
|
||||
if (m_eg)
|
||||
m_eg->get_diagnostic_manager ().add_event (std::move (event));
|
||||
}
|
||||
|
||||
void
|
||||
impl_region_model_context::on_svalue_leak (const svalue *sval)
|
||||
|
||||
|
|
@ -3840,8 +3850,10 @@ exploded_graph::maybe_create_dynamic_call (const gcall *call,
|
|||
class impl_path_context : public path_context
|
||||
{
|
||||
public:
|
||||
impl_path_context (const program_state *cur_state)
|
||||
impl_path_context (const program_state *cur_state,
|
||||
logger *logger)
|
||||
: m_cur_state (cur_state),
|
||||
m_logger (logger),
|
||||
m_terminate_path (false)
|
||||
{
|
||||
}
|
||||
|
|
@ -3860,6 +3872,9 @@ public:
|
|||
void
|
||||
bifurcate (std::unique_ptr<custom_edge_info> info) final override
|
||||
{
|
||||
if (m_logger)
|
||||
m_logger->log ("bifurcating path");
|
||||
|
||||
if (m_state_at_bifurcation)
|
||||
/* Verify that the state at bifurcation is consistent when we
|
||||
split into multiple out-edges. */
|
||||
|
|
@ -3876,6 +3891,8 @@ public:
|
|||
|
||||
void terminate_path () final override
|
||||
{
|
||||
if (m_logger)
|
||||
m_logger->log ("terminating path");
|
||||
m_terminate_path = true;
|
||||
}
|
||||
|
||||
|
|
@ -3892,6 +3909,8 @@ public:
|
|||
private:
|
||||
const program_state *m_cur_state;
|
||||
|
||||
logger *m_logger;
|
||||
|
||||
/* Lazily-created copy of the state before the split. */
|
||||
std::unique_ptr<program_state> m_state_at_bifurcation;
|
||||
|
||||
|
|
@ -4036,7 +4055,7 @@ exploded_graph::process_node (exploded_node *node)
|
|||
exactly one stmt, the one that caused the change. */
|
||||
program_state next_state (state);
|
||||
|
||||
impl_path_context path_ctxt (&next_state);
|
||||
impl_path_context path_ctxt (&next_state, logger);
|
||||
|
||||
uncertainty_t uncertainty;
|
||||
const supernode *snode = point.get_supernode ();
|
||||
|
|
|
|||
|
|
@ -56,8 +56,10 @@ class impl_region_model_context : public region_model_context
|
|||
uncertainty_t *uncertainty,
|
||||
logger *logger = NULL);
|
||||
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d) final override;
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d,
|
||||
const stmt_finder *custom_finder = NULL) final override;
|
||||
void add_note (std::unique_ptr<pending_note> pn) final override;
|
||||
void add_event (std::unique_ptr<checker_event> event) final override;
|
||||
void on_svalue_leak (const svalue *) override;
|
||||
void on_liveness_change (const svalue_set &live_svalues,
|
||||
const region_model *model) final override;
|
||||
|
|
@ -106,6 +108,7 @@ class impl_region_model_context : public region_model_context
|
|||
std::unique_ptr<sm_context> *out_sm_context) override;
|
||||
|
||||
const gimple *get_stmt () const override { return m_stmt; }
|
||||
const exploded_graph *get_eg () const override { return m_eg; }
|
||||
|
||||
exploded_graph *m_eg;
|
||||
log_user m_logger;
|
||||
|
|
|
|||
|
|
@ -379,6 +379,7 @@ register_known_analyzer_functions (known_function_manager &kfm)
|
|||
kfm.add ("__analyzer_eval", make_unique<kf_analyzer_eval> ());
|
||||
kfm.add ("__analyzer_get_unknown_ptr",
|
||||
make_unique<kf_analyzer_get_unknown_ptr> ());
|
||||
kfm.add ("__analyzer_get_strlen", make_kf_strlen ());
|
||||
}
|
||||
|
||||
} // namespace ana
|
||||
|
|
|
|||
|
|
@ -35,6 +35,38 @@ along with GCC; see the file COPYING3. If not see
|
|||
|
||||
#if ENABLE_ANALYZER
|
||||
|
||||
/* Return true if CALL is a non-allocating operator new or operator new []
|
||||
that contains no user-defined args, i.e. having any signature of:
|
||||
|
||||
- void* operator new (std::size_t count, void* ptr);
|
||||
- void* operator new[] (std::size_t count, void* ptr);
|
||||
|
||||
See https://en.cppreference.com/w/cpp/memory/new/operator_new. */
|
||||
|
||||
bool is_placement_new_p (const gcall *call)
|
||||
{
|
||||
gcc_assert (call);
|
||||
tree fndecl = gimple_call_fndecl (call);
|
||||
|
||||
if (!fndecl || TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
|
||||
/* Give up on overloaded operator new. */
|
||||
return false;
|
||||
|
||||
if (!is_named_call_p (fndecl, "operator new", call, 2)
|
||||
&& !is_named_call_p (fndecl, "operator new []", call, 2))
|
||||
return false;
|
||||
|
||||
/* We must distinguish between an allocating non-throwing new
|
||||
and a non-allocating new.
|
||||
|
||||
The former might have one of the following signatures :
|
||||
void* operator new (std::size_t count, const std::nothrow_t& tag);
|
||||
void* operator new[] (std::size_t count, const std::nothrow_t& tag);
|
||||
Whereas a placement new would take a pointer. */
|
||||
tree arg1_type = TREE_CHAIN (TYPE_ARG_TYPES (TREE_TYPE (fndecl)));
|
||||
return TREE_CODE (TREE_VALUE (arg1_type)) == POINTER_TYPE;
|
||||
}
|
||||
|
||||
namespace ana {
|
||||
|
||||
/* Implementations of specific functions. */
|
||||
|
|
@ -46,7 +78,11 @@ class kf_operator_new : public known_function
|
|||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return cd.num_args () == 1;
|
||||
return (cd.num_args () == 1
|
||||
&& cd.arg_is_size_p (0))
|
||||
|| (cd.num_args () == 2
|
||||
&& cd.arg_is_size_p (0)
|
||||
&& POINTER_TYPE_P (cd.get_arg_type (1)));
|
||||
}
|
||||
|
||||
void impl_call_pre (const call_details &cd) const final override
|
||||
|
|
@ -54,28 +90,74 @@ public:
|
|||
region_model *model = cd.get_model ();
|
||||
region_model_manager *mgr = cd.get_manager ();
|
||||
const svalue *size_sval = cd.get_arg_svalue (0);
|
||||
const region *new_reg
|
||||
= model->get_or_create_region_for_heap_alloc (size_sval, cd.get_ctxt ());
|
||||
if (cd.get_lhs_type ())
|
||||
region_model_context *ctxt = cd.get_ctxt ();
|
||||
const gcall *call = cd.get_call_stmt ();
|
||||
|
||||
/* If the call was actually a placement new, check that accessing
|
||||
the buffer lhs is placed into does not result in out-of-bounds. */
|
||||
if (is_placement_new_p (call))
|
||||
{
|
||||
const svalue *ptr_sval
|
||||
= mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
|
||||
cd.maybe_set_lhs (ptr_sval);
|
||||
const region *ptr_reg = cd.deref_ptr_arg (1);
|
||||
if (ptr_reg && cd.get_lhs_type ())
|
||||
{
|
||||
const svalue *num_bytes_sval = cd.get_arg_svalue (0);
|
||||
const region *sized_new_reg
|
||||
= mgr->get_sized_region (ptr_reg,
|
||||
cd.get_lhs_type (),
|
||||
num_bytes_sval);
|
||||
model->check_region_for_write (sized_new_reg,
|
||||
nullptr,
|
||||
ctxt);
|
||||
const svalue *ptr_sval
|
||||
= mgr->get_ptr_svalue (cd.get_lhs_type (), sized_new_reg);
|
||||
cd.maybe_set_lhs (ptr_sval);
|
||||
}
|
||||
}
|
||||
/* If the call is an allocating new, then create a heap allocated
|
||||
region. */
|
||||
else
|
||||
{
|
||||
const region *new_reg
|
||||
= model->get_or_create_region_for_heap_alloc (size_sval, ctxt);
|
||||
if (cd.get_lhs_type ())
|
||||
{
|
||||
const svalue *ptr_sval
|
||||
= mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
|
||||
cd.maybe_set_lhs (ptr_sval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void impl_call_post (const call_details &cd) const final override
|
||||
{
|
||||
region_model *model = cd.get_model ();
|
||||
region_model_manager *mgr = cd.get_manager ();
|
||||
tree callee_fndecl = cd.get_fndecl_for_call ();
|
||||
region_model_context *ctxt = cd.get_ctxt ();
|
||||
|
||||
/* If the call is guaranteed to return nonnull
|
||||
then add a nonnull constraint to the allocated region. */
|
||||
if (!TREE_NOTHROW (callee_fndecl) && flag_exceptions)
|
||||
{
|
||||
const svalue *null_sval
|
||||
= mgr->get_or_create_null_ptr (cd.get_lhs_type ());
|
||||
const svalue *result
|
||||
= model->get_store_value (cd.get_lhs_region (), ctxt);
|
||||
model->add_constraint (result, NE_EXPR, null_sval, ctxt);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Handler for "operator delete", both the sized and unsized variants
|
||||
(2 arguments and 1 argument respectively), and for "operator delete []" */
|
||||
/* Handler for "operator delete" and for "operator delete []",
|
||||
both the sized and unsized variants
|
||||
(2 arguments and 1 argument respectively). */
|
||||
|
||||
class kf_operator_delete : public known_function
|
||||
{
|
||||
public:
|
||||
kf_operator_delete (unsigned num_args) : m_num_args (num_args) {}
|
||||
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return cd.num_args () == m_num_args;
|
||||
return cd.num_args () == 1 or cd.num_args () == 2;
|
||||
}
|
||||
|
||||
void impl_call_post (const call_details &cd) const final override
|
||||
|
|
@ -86,12 +168,11 @@ public:
|
|||
{
|
||||
/* If the ptr points to an underlying heap region, delete it,
|
||||
poisoning pointers. */
|
||||
model->unbind_region_and_descendents (freed_reg, POISON_KIND_FREED);
|
||||
model->unbind_region_and_descendents (freed_reg,
|
||||
POISON_KIND_DELETED);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned m_num_args;
|
||||
};
|
||||
|
||||
/* Populate KFM with instances of known functions relating to C++. */
|
||||
|
|
@ -101,9 +182,8 @@ register_known_functions_lang_cp (known_function_manager &kfm)
|
|||
{
|
||||
kfm.add ("operator new", make_unique<kf_operator_new> ());
|
||||
kfm.add ("operator new []", make_unique<kf_operator_new> ());
|
||||
kfm.add ("operator delete", make_unique<kf_operator_delete> (1));
|
||||
kfm.add ("operator delete", make_unique<kf_operator_delete> (2));
|
||||
kfm.add ("operator delete []", make_unique<kf_operator_delete> (1));
|
||||
kfm.add ("operator delete", make_unique<kf_operator_delete> ());
|
||||
kfm.add ("operator delete []", make_unique<kf_operator_delete> ());
|
||||
}
|
||||
|
||||
} // namespace ana
|
||||
|
|
|
|||
|
|
@ -40,17 +40,30 @@ along with GCC; see the file COPYING3. If not see
|
|||
|
||||
namespace ana {
|
||||
|
||||
/* class pure_known_function_with_default_return : public known_function. */
|
||||
|
||||
void
|
||||
pure_known_function_with_default_return::
|
||||
impl_call_pre (const call_details &cd) const
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
}
|
||||
|
||||
/* Implementations of specific functions. */
|
||||
|
||||
/* Handler for "alloca". */
|
||||
|
||||
class kf_alloca : public known_function
|
||||
class kf_alloca : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return cd.num_args () == 1;
|
||||
}
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_ALLOCA;
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override;
|
||||
};
|
||||
|
||||
|
|
@ -313,7 +326,7 @@ public:
|
|||
|
||||
/* Handler for "calloc". */
|
||||
|
||||
class kf_calloc : public known_function
|
||||
class kf_calloc : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
|
|
@ -322,6 +335,11 @@ public:
|
|||
&& cd.arg_is_size_p (0)
|
||||
&& cd.arg_is_size_p (1));
|
||||
}
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_CALLOC;
|
||||
}
|
||||
|
||||
void impl_call_pre (const call_details &cd) const final override;
|
||||
};
|
||||
|
||||
|
|
@ -405,8 +423,39 @@ kf_error::impl_call_pre (const call_details &cd) const
|
|||
if (!model->add_constraint (status, EQ_EXPR, integer_zero_node, ctxt))
|
||||
if (ctxt)
|
||||
ctxt->terminate_path ();
|
||||
|
||||
/* Check "format" arg. */
|
||||
const int fmt_arg_idx = (m_min_args == 3) ? 2 : 4;
|
||||
model->check_for_null_terminated_string_arg (cd, fmt_arg_idx);
|
||||
}
|
||||
|
||||
/* Handler for fopen.
|
||||
FILE *fopen (const char *filename, const char *mode);
|
||||
See e.g. https://en.cppreference.com/w/c/io/fopen
|
||||
https://www.man7.org/linux/man-pages/man3/fopen.3.html
|
||||
https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/fopen-wfopen?view=msvc-170 */
|
||||
|
||||
class kf_fopen : public known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == 2
|
||||
&& cd.arg_is_pointer_p (0)
|
||||
&& cd.arg_is_pointer_p (1));
|
||||
}
|
||||
|
||||
void impl_call_pre (const call_details &cd) const final override
|
||||
{
|
||||
cd.check_for_null_terminated_string_arg (0);
|
||||
cd.check_for_null_terminated_string_arg (1);
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
|
||||
/* fopen's mode param is effectively a mini-DSL, but there are various
|
||||
non-standard extensions, so we don't bother to check it. */
|
||||
}
|
||||
};
|
||||
|
||||
/* Handler for "free", after sm-handling.
|
||||
|
||||
If the ptr points to an underlying heap region, delete the region,
|
||||
|
|
@ -422,12 +471,16 @@ kf_error::impl_call_pre (const call_details &cd) const
|
|||
all pointers to the region to the "freed" state together, regardless
|
||||
of casts. */
|
||||
|
||||
class kf_free : public known_function
|
||||
class kf_free : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == 0 && cd.arg_is_pointer_p (0));
|
||||
return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
|
||||
}
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_FREE;
|
||||
}
|
||||
void impl_call_post (const call_details &cd) const final override;
|
||||
};
|
||||
|
|
@ -448,7 +501,7 @@ kf_free::impl_call_post (const call_details &cd) const
|
|||
|
||||
/* Handle the on_call_pre part of "malloc". */
|
||||
|
||||
class kf_malloc : public known_function
|
||||
class kf_malloc : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
|
|
@ -456,6 +509,10 @@ public:
|
|||
return (cd.num_args () == 1
|
||||
&& cd.arg_is_size_p (0));
|
||||
}
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_MALLOC;
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override;
|
||||
};
|
||||
|
||||
|
|
@ -477,12 +534,19 @@ kf_malloc::impl_call_pre (const call_details &cd) const
|
|||
|
||||
/* Handler for "memcpy" and "__builtin_memcpy",
|
||||
"memmove", and "__builtin_memmove". */
|
||||
/* TODO: complain about overlapping src and dest for the memcpy
|
||||
variants. */
|
||||
|
||||
class kf_memcpy_memmove : public known_function
|
||||
class kf_memcpy_memmove : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
enum kf_memcpy_memmove_variant
|
||||
{
|
||||
KF_MEMCPY,
|
||||
KF_MEMCPY_CHK,
|
||||
KF_MEMMOVE,
|
||||
KF_MEMMOVE_CHK,
|
||||
};
|
||||
kf_memcpy_memmove (enum kf_memcpy_memmove_variant variant)
|
||||
: m_variant (variant) {};
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == 3
|
||||
|
|
@ -490,7 +554,25 @@ public:
|
|||
&& cd.arg_is_pointer_p (1)
|
||||
&& cd.arg_is_size_p (2));
|
||||
}
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
switch (m_variant)
|
||||
{
|
||||
case KF_MEMCPY:
|
||||
return BUILT_IN_MEMCPY;
|
||||
case KF_MEMCPY_CHK:
|
||||
return BUILT_IN_MEMCPY_CHK;
|
||||
case KF_MEMMOVE:
|
||||
return BUILT_IN_MEMMOVE;
|
||||
case KF_MEMMOVE_CHK:
|
||||
return BUILT_IN_MEMMOVE_CHK;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override;
|
||||
private:
|
||||
const enum kf_memcpy_memmove_variant m_variant;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
@ -501,7 +583,6 @@ kf_memcpy_memmove::impl_call_pre (const call_details &cd) const
|
|||
const svalue *num_bytes_sval = cd.get_arg_svalue (2);
|
||||
|
||||
region_model *model = cd.get_model ();
|
||||
region_model_manager *mgr = cd.get_manager ();
|
||||
|
||||
const region *dest_reg
|
||||
= model->deref_rvalue (dest_ptr_sval, cd.get_arg_tree (0), cd.get_ctxt ());
|
||||
|
|
@ -509,29 +590,45 @@ kf_memcpy_memmove::impl_call_pre (const call_details &cd) const
|
|||
= model->deref_rvalue (src_ptr_sval, cd.get_arg_tree (1), cd.get_ctxt ());
|
||||
|
||||
cd.maybe_set_lhs (dest_ptr_sval);
|
||||
/* Check for overlap. */
|
||||
switch (m_variant)
|
||||
{
|
||||
case KF_MEMCPY:
|
||||
case KF_MEMCPY_CHK:
|
||||
cd.complain_about_overlap (0, 1, num_bytes_sval);
|
||||
break;
|
||||
|
||||
const region *sized_src_reg
|
||||
= mgr->get_sized_region (src_reg, NULL_TREE, num_bytes_sval);
|
||||
const region *sized_dest_reg
|
||||
= mgr->get_sized_region (dest_reg, NULL_TREE, num_bytes_sval);
|
||||
const svalue *src_contents_sval
|
||||
= model->get_store_value (sized_src_reg, cd.get_ctxt ());
|
||||
model->check_for_poison (src_contents_sval, cd.get_arg_tree (1),
|
||||
sized_src_reg, cd.get_ctxt ());
|
||||
model->set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
|
||||
case KF_MEMMOVE:
|
||||
case KF_MEMMOVE_CHK:
|
||||
/* It's OK for memmove's arguments to overlap. */
|
||||
break;
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
model->copy_bytes (dest_reg,
|
||||
src_reg, cd.get_arg_tree (1),
|
||||
num_bytes_sval,
|
||||
cd.get_ctxt ());
|
||||
}
|
||||
|
||||
/* Handler for "memset" and "__builtin_memset". */
|
||||
|
||||
class kf_memset : public known_function
|
||||
class kf_memset : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
kf_memset (bool chk_variant) : m_chk_variant (chk_variant) {}
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == 3 && cd.arg_is_pointer_p (0));
|
||||
}
|
||||
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return m_chk_variant ? BUILT_IN_MEMSET_CHK : BUILT_IN_MEMSET;
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override;
|
||||
private:
|
||||
const bool m_chk_variant;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
@ -557,6 +654,8 @@ kf_memset::impl_call_pre (const call_details &cd) const
|
|||
nullptr,
|
||||
cd.get_ctxt ());
|
||||
model->fill_region (sized_dest_reg, fill_value_u8);
|
||||
|
||||
cd.maybe_set_lhs (dest_sval);
|
||||
}
|
||||
|
||||
/* A subclass of pending_diagnostic for complaining about 'putenv'
|
||||
|
|
@ -663,6 +762,7 @@ public:
|
|||
gcc_assert (fndecl);
|
||||
region_model_context *ctxt = cd.get_ctxt ();
|
||||
region_model *model = cd.get_model ();
|
||||
model->check_for_null_terminated_string_arg (cd, 0);
|
||||
const svalue *ptr_sval = cd.get_arg_svalue (0);
|
||||
const region *reg
|
||||
= model->deref_rvalue (ptr_sval, cd.get_arg_tree (0), ctxt);
|
||||
|
|
@ -683,6 +783,7 @@ public:
|
|||
ctxt->warn (make_unique<putenv_of_auto_var> (fndecl, reg));
|
||||
break;
|
||||
}
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -709,7 +810,7 @@ public:
|
|||
Each of these has a custom_edge_info subclass, which updates
|
||||
the region_model and sm-state of the destination state. */
|
||||
|
||||
class kf_realloc : public known_function
|
||||
class kf_realloc : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
|
|
@ -718,6 +819,12 @@ public:
|
|||
&& cd.arg_is_pointer_p (0)
|
||||
&& cd.arg_is_size_p (1));
|
||||
}
|
||||
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_REALLOC;
|
||||
}
|
||||
|
||||
void impl_call_post (const call_details &cd) const final override;
|
||||
};
|
||||
|
||||
|
|
@ -930,13 +1037,22 @@ kf_realloc::impl_call_post (const call_details &cd) const
|
|||
|
||||
/* Handler for "strchr" and "__builtin_strchr". */
|
||||
|
||||
class kf_strchr : public known_function
|
||||
class kf_strchr : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override
|
||||
{
|
||||
cd.check_for_null_terminated_string_arg (0);
|
||||
}
|
||||
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_STRCHR;
|
||||
}
|
||||
void impl_call_post (const call_details &cd) const final override;
|
||||
};
|
||||
|
||||
|
|
@ -1013,7 +1129,7 @@ kf_strchr::impl_call_post (const call_details &cd) const
|
|||
int sprintf(char *str, const char *format, ...);
|
||||
*/
|
||||
|
||||
class kf_sprintf : public known_function
|
||||
class kf_sprintf : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
|
|
@ -1023,6 +1139,11 @@ public:
|
|||
&& cd.arg_is_pointer_p (1));
|
||||
}
|
||||
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_SPRINTF;
|
||||
}
|
||||
|
||||
void impl_call_pre (const call_details &cd) const final override
|
||||
{
|
||||
/* For now, merely assume that the destination buffer gets set to a
|
||||
|
|
@ -1034,12 +1155,13 @@ public:
|
|||
= model->deref_rvalue (dst_ptr, cd.get_arg_tree (0), ctxt);
|
||||
const svalue *content = cd.get_or_create_conjured_svalue (dst_reg);
|
||||
model->set_value (dst_reg, content, ctxt);
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
}
|
||||
};
|
||||
|
||||
/* Handler for "__builtin_stack_restore". */
|
||||
|
||||
class kf_stack_restore : public known_function
|
||||
class kf_stack_restore : public pure_known_function_with_default_return
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &) const final override
|
||||
|
|
@ -1052,7 +1174,7 @@ public:
|
|||
|
||||
/* Handler for "__builtin_stack_save". */
|
||||
|
||||
class kf_stack_save : public known_function
|
||||
class kf_stack_save : public pure_known_function_with_default_return
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &) const final override
|
||||
|
|
@ -1063,12 +1185,14 @@ public:
|
|||
/* Currently a no-op. */
|
||||
};
|
||||
|
||||
/* Handler for "strcpy" and "__builtin_strcpy_chk". */
|
||||
/* Handler for "strcat" and "__builtin_strcat_chk". */
|
||||
|
||||
class kf_strcpy : public known_function
|
||||
class kf_strcat : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
kf_strcpy (unsigned int num_args) : m_num_args (num_args) {}
|
||||
kf_strcat (unsigned int num_args, bool chk_variant)
|
||||
: m_num_args (num_args),
|
||||
m_chk_variant (chk_variant) {}
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == m_num_args
|
||||
|
|
@ -1076,116 +1200,197 @@ public:
|
|||
&& cd.arg_is_pointer_p (1));
|
||||
}
|
||||
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return m_chk_variant ? BUILT_IN_STRCAT_CHK : BUILT_IN_STRCAT;
|
||||
}
|
||||
|
||||
void impl_call_pre (const call_details &cd) const final override
|
||||
{
|
||||
region_model *model = cd.get_model ();
|
||||
region_model_manager *mgr = cd.get_manager ();
|
||||
|
||||
const svalue *dest_sval = cd.get_arg_svalue (0);
|
||||
const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0),
|
||||
cd.get_ctxt ());
|
||||
|
||||
const svalue *dst_strlen_sval
|
||||
= cd.check_for_null_terminated_string_arg (0, false, nullptr);
|
||||
if (!dst_strlen_sval)
|
||||
{
|
||||
if (cd.get_ctxt ())
|
||||
cd.get_ctxt ()->terminate_path ();
|
||||
return;
|
||||
}
|
||||
|
||||
const svalue *bytes_to_copy;
|
||||
const svalue *num_src_bytes_read_sval
|
||||
= cd.check_for_null_terminated_string_arg (1, true, &bytes_to_copy);
|
||||
if (!num_src_bytes_read_sval)
|
||||
{
|
||||
if (cd.get_ctxt ())
|
||||
cd.get_ctxt ()->terminate_path ();
|
||||
return;
|
||||
}
|
||||
|
||||
cd.maybe_set_lhs (dest_sval);
|
||||
cd.complain_about_overlap (0, 1, num_src_bytes_read_sval);
|
||||
|
||||
const region *offset_reg
|
||||
= mgr->get_offset_region (dest_reg, NULL_TREE, dst_strlen_sval);
|
||||
model->write_bytes (offset_reg,
|
||||
num_src_bytes_read_sval,
|
||||
bytes_to_copy,
|
||||
cd.get_ctxt ());
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned int m_num_args;
|
||||
const bool m_chk_variant;
|
||||
};
|
||||
|
||||
/* Handler for "strcpy" and "__builtin_strcpy_chk". */
|
||||
|
||||
class kf_strcpy : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
kf_strcpy (unsigned int num_args, bool chk_variant)
|
||||
: m_num_args (num_args),
|
||||
m_chk_variant (chk_variant) {}
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == m_num_args
|
||||
&& cd.arg_is_pointer_p (0)
|
||||
&& cd.arg_is_pointer_p (1));
|
||||
}
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return m_chk_variant ? BUILT_IN_STRCPY_CHK : BUILT_IN_STRCPY;
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override;
|
||||
|
||||
private:
|
||||
unsigned int m_num_args;
|
||||
const bool m_chk_variant;
|
||||
};
|
||||
|
||||
void
|
||||
kf_strcpy::impl_call_pre (const call_details &cd) const
|
||||
{
|
||||
region_model *model = cd.get_model ();
|
||||
region_model_manager *mgr = cd.get_manager ();
|
||||
region_model_context *ctxt = cd.get_ctxt ();
|
||||
|
||||
const svalue *dest_sval = cd.get_arg_svalue (0);
|
||||
const region *dest_reg = model->deref_rvalue (dest_sval, cd.get_arg_tree (0),
|
||||
cd.get_ctxt ());
|
||||
const svalue *src_sval = cd.get_arg_svalue (1);
|
||||
const region *src_reg = model->deref_rvalue (src_sval, cd.get_arg_tree (1),
|
||||
cd.get_ctxt ());
|
||||
const svalue *src_contents_sval = model->get_store_value (src_reg,
|
||||
cd.get_ctxt ());
|
||||
|
||||
ctxt);
|
||||
/* strcpy returns the initial param. */
|
||||
cd.maybe_set_lhs (dest_sval);
|
||||
|
||||
/* Try to get the string size if SRC_REG is a string_region. */
|
||||
const svalue *copied_bytes_sval = model->get_string_size (src_reg);
|
||||
/* Otherwise, check if the contents of SRC_REG is a string. */
|
||||
if (copied_bytes_sval->get_kind () == SK_UNKNOWN)
|
||||
copied_bytes_sval = model->get_string_size (src_contents_sval);
|
||||
|
||||
const region *sized_dest_reg
|
||||
= mgr->get_sized_region (dest_reg, NULL_TREE, copied_bytes_sval);
|
||||
model->set_value (sized_dest_reg, src_contents_sval, cd.get_ctxt ());
|
||||
const svalue *bytes_to_copy;
|
||||
if (const svalue *num_bytes_read_sval
|
||||
= cd.check_for_null_terminated_string_arg (1, true, &bytes_to_copy))
|
||||
{
|
||||
cd.complain_about_overlap (0, 1, num_bytes_read_sval);
|
||||
model->write_bytes (dest_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cd.get_ctxt ())
|
||||
cd.get_ctxt ()->terminate_path ();
|
||||
}
|
||||
}
|
||||
|
||||
/* Handler for "strdup" and "__builtin_strdup". */
|
||||
|
||||
class kf_strdup : public known_function
|
||||
class kf_strdup : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
|
||||
}
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_STRDUP;
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override
|
||||
{
|
||||
region_model *model = cd.get_model ();
|
||||
region_model_context *ctxt = cd.get_ctxt ();
|
||||
region_model_manager *mgr = cd.get_manager ();
|
||||
/* Ideally we'd get the size here, and simulate copying the bytes. */
|
||||
const region *new_reg
|
||||
= model->get_or_create_region_for_heap_alloc (NULL, cd.get_ctxt ());
|
||||
model->mark_region_as_unknown (new_reg, NULL);
|
||||
if (cd.get_lhs_type ())
|
||||
const svalue *bytes_to_copy;
|
||||
if (const svalue *num_bytes_read_sval
|
||||
= cd.check_for_null_terminated_string_arg (0, true, &bytes_to_copy))
|
||||
{
|
||||
const svalue *ptr_sval
|
||||
= mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
|
||||
cd.maybe_set_lhs (ptr_sval);
|
||||
const region *new_reg
|
||||
= model->get_or_create_region_for_heap_alloc (num_bytes_read_sval,
|
||||
ctxt);
|
||||
model->write_bytes (new_reg, num_bytes_read_sval, bytes_to_copy, ctxt);
|
||||
if (cd.get_lhs_type ())
|
||||
{
|
||||
const svalue *ptr_sval
|
||||
= mgr->get_ptr_svalue (cd.get_lhs_type (), new_reg);
|
||||
cd.maybe_set_lhs (ptr_sval);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ctxt)
|
||||
ctxt->terminate_path ();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/* Handle the on_call_pre part of "strlen". */
|
||||
/* Handler for "strlen" and for "__analyzer_get_strlen". */
|
||||
|
||||
class kf_strlen : public known_function
|
||||
class kf_strlen : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == 1 && cd.arg_is_pointer_p (0));
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override;
|
||||
};
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_STRLEN;
|
||||
}
|
||||
|
||||
void
|
||||
kf_strlen::impl_call_pre (const call_details &cd) const
|
||||
{
|
||||
region_model_context *ctxt = cd.get_ctxt ();
|
||||
region_model *model = cd.get_model ();
|
||||
region_model_manager *mgr = cd.get_manager ();
|
||||
|
||||
const svalue *arg_sval = cd.get_arg_svalue (0);
|
||||
const region *buf_reg
|
||||
= model->deref_rvalue (arg_sval, cd.get_arg_tree (0), ctxt);
|
||||
if (const string_region *str_reg
|
||||
= buf_reg->dyn_cast_string_region ())
|
||||
{
|
||||
tree str_cst = str_reg->get_string_cst ();
|
||||
/* TREE_STRING_LENGTH is sizeof, not strlen. */
|
||||
int sizeof_cst = TREE_STRING_LENGTH (str_cst);
|
||||
int strlen_cst = sizeof_cst - 1;
|
||||
if (cd.get_lhs_type ())
|
||||
void impl_call_pre (const call_details &cd) const final override
|
||||
{
|
||||
if (const svalue *strlen_sval
|
||||
= cd.check_for_null_terminated_string_arg (0, false, nullptr))
|
||||
if (strlen_sval->get_kind () != SK_UNKNOWN)
|
||||
{
|
||||
tree t_cst = build_int_cst (cd.get_lhs_type (), strlen_cst);
|
||||
const svalue *result_sval
|
||||
= mgr->get_or_create_constant_svalue (t_cst);
|
||||
cd.maybe_set_lhs (result_sval);
|
||||
cd.maybe_set_lhs (strlen_sval);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* Otherwise a conjured value. */
|
||||
|
||||
/* Use a conjured svalue. */
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
}
|
||||
};
|
||||
|
||||
/* Factory function, so that kf-analyzer.cc can use this class. */
|
||||
|
||||
std::unique_ptr<known_function>
|
||||
make_kf_strlen ()
|
||||
{
|
||||
return make_unique<kf_strlen> ();
|
||||
}
|
||||
|
||||
/* Handler for "strndup" and "__builtin_strndup". */
|
||||
|
||||
class kf_strndup : public known_function
|
||||
class kf_strndup : public builtin_known_function
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
{
|
||||
return (cd.num_args () == 2 && cd.arg_is_pointer_p (0));
|
||||
}
|
||||
enum built_in_function builtin_code () const final override
|
||||
{
|
||||
return BUILT_IN_STRNDUP;
|
||||
}
|
||||
void impl_call_pre (const call_details &cd) const final override
|
||||
{
|
||||
region_model *model = cd.get_model ();
|
||||
|
|
@ -1358,45 +1563,78 @@ register_known_functions (known_function_manager &kfm)
|
|||
kfm.add (IFN_UBSAN_BOUNDS, make_unique<kf_ubsan_bounds> ());
|
||||
}
|
||||
|
||||
/* Built-ins the analyzer has known_functions for. */
|
||||
/* GCC built-ins that do not correspond to a function
|
||||
in the standard library. */
|
||||
{
|
||||
kfm.add (BUILT_IN_ALLOCA, make_unique<kf_alloca> ());
|
||||
kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, make_unique<kf_alloca> ());
|
||||
kfm.add (BUILT_IN_CALLOC, make_unique<kf_calloc> ());
|
||||
kfm.add (BUILT_IN_EXPECT, make_unique<kf_expect> ());
|
||||
kfm.add (BUILT_IN_EXPECT_WITH_PROBABILITY, make_unique<kf_expect> ());
|
||||
kfm.add (BUILT_IN_FREE, make_unique<kf_free> ());
|
||||
kfm.add (BUILT_IN_MALLOC, make_unique<kf_malloc> ());
|
||||
kfm.add (BUILT_IN_MEMCPY, make_unique<kf_memcpy_memmove> ());
|
||||
kfm.add (BUILT_IN_MEMCPY_CHK, make_unique<kf_memcpy_memmove> ());
|
||||
kfm.add (BUILT_IN_MEMMOVE, make_unique<kf_memcpy_memmove> ());
|
||||
kfm.add (BUILT_IN_MEMMOVE_CHK, make_unique<kf_memcpy_memmove> ());
|
||||
kfm.add (BUILT_IN_MEMSET, make_unique<kf_memset> ());
|
||||
kfm.add (BUILT_IN_MEMSET_CHK, make_unique<kf_memset> ());
|
||||
kfm.add (BUILT_IN_REALLOC, make_unique<kf_realloc> ());
|
||||
kfm.add (BUILT_IN_SPRINTF, make_unique<kf_sprintf> ());
|
||||
kfm.add (BUILT_IN_ALLOCA_WITH_ALIGN, make_unique<kf_alloca> ());
|
||||
kfm.add (BUILT_IN_STACK_RESTORE, make_unique<kf_stack_restore> ());
|
||||
kfm.add (BUILT_IN_STACK_SAVE, make_unique<kf_stack_save> ());
|
||||
kfm.add (BUILT_IN_STRCHR, make_unique<kf_strchr> ());
|
||||
kfm.add (BUILT_IN_STRCPY, make_unique<kf_strcpy> (2));
|
||||
kfm.add (BUILT_IN_STRCPY_CHK, make_unique<kf_strcpy> (3));
|
||||
kfm.add (BUILT_IN_STRDUP, make_unique<kf_strdup> ());
|
||||
kfm.add (BUILT_IN_STRNDUP, make_unique<kf_strndup> ());
|
||||
kfm.add (BUILT_IN_STRLEN, make_unique<kf_strlen> ());
|
||||
|
||||
register_atomic_builtins (kfm);
|
||||
register_varargs_builtins (kfm);
|
||||
}
|
||||
|
||||
/* Known builtins and C standard library functions. */
|
||||
/* Known builtins and C standard library functions
|
||||
the analyzer has known functions for. */
|
||||
{
|
||||
kfm.add ("memset", make_unique<kf_memset> ());
|
||||
kfm.add ("alloca", make_unique<kf_alloca> ());
|
||||
kfm.add ("__builtin_alloca", make_unique<kf_alloca> ());
|
||||
kfm.add ("calloc", make_unique<kf_calloc> ());
|
||||
kfm.add ("__builtin_calloc", make_unique<kf_calloc> ());
|
||||
kfm.add ("free", make_unique<kf_free> ());
|
||||
kfm.add ("__builtin_free", make_unique<kf_free> ());
|
||||
kfm.add ("malloc", make_unique<kf_malloc> ());
|
||||
kfm.add ("__builtin_malloc", make_unique<kf_malloc> ());
|
||||
kfm.add ("memcpy",
|
||||
make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
|
||||
kfm.add ("__builtin_memcpy",
|
||||
make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMCPY));
|
||||
kfm.add ("__memcpy_chk", make_unique<kf_memcpy_memmove>
|
||||
(kf_memcpy_memmove::KF_MEMCPY_CHK));
|
||||
kfm.add ("__builtin___memcpy_chk", make_unique<kf_memcpy_memmove>
|
||||
(kf_memcpy_memmove::KF_MEMCPY_CHK));
|
||||
kfm.add ("memmove",
|
||||
make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
|
||||
kfm.add ("__builtin_memmove",
|
||||
make_unique<kf_memcpy_memmove> (kf_memcpy_memmove::KF_MEMMOVE));
|
||||
kfm.add ("__memmove_chk", make_unique<kf_memcpy_memmove>
|
||||
(kf_memcpy_memmove::KF_MEMMOVE_CHK));
|
||||
kfm.add ("__builtin___memmove_chk", make_unique<kf_memcpy_memmove>
|
||||
(kf_memcpy_memmove::KF_MEMMOVE_CHK));
|
||||
kfm.add ("memset", make_unique<kf_memset> (false));
|
||||
kfm.add ("__builtin_memset", make_unique<kf_memset> (false));
|
||||
kfm.add ("__memset_chk", make_unique<kf_memset> (true));
|
||||
kfm.add ("__builtin___memset_chk", make_unique<kf_memset> (true));
|
||||
kfm.add ("realloc", make_unique<kf_realloc> ());
|
||||
kfm.add ("__builtin_realloc", make_unique<kf_realloc> ());
|
||||
kfm.add ("sprintf", make_unique<kf_sprintf> ());
|
||||
kfm.add ("__builtin_sprintf", make_unique<kf_sprintf> ());
|
||||
kfm.add ("strchr", make_unique<kf_strchr> ());
|
||||
kfm.add ("__builtin_strchr", make_unique<kf_strchr> ());
|
||||
kfm.add ("strcpy", make_unique<kf_strcpy> (2, false));
|
||||
kfm.add ("__builtin_strcpy", make_unique<kf_strcpy> (2, false));
|
||||
kfm.add ("__strcpy_chk", make_unique<kf_strcpy> (3, true));
|
||||
kfm.add ("__builtin___strcpy_chk", make_unique<kf_strcpy> (3, true));
|
||||
kfm.add ("strcat", make_unique<kf_strcat> (2, false));
|
||||
kfm.add ("__builtin_strcat", make_unique<kf_strcat> (2, false));
|
||||
kfm.add ("__strcat_chk", make_unique<kf_strcat> (3, true));
|
||||
kfm.add ("__builtin___strcat_chk", make_unique<kf_strcat> (3, true));
|
||||
kfm.add ("strdup", make_unique<kf_strdup> ());
|
||||
kfm.add ("__builtin_strdup", make_unique<kf_strdup> ());
|
||||
kfm.add ("strndup", make_unique<kf_strndup> ());
|
||||
kfm.add ("__builtin_strndup", make_unique<kf_strndup> ());
|
||||
kfm.add ("strlen", make_unique<kf_strlen> ());
|
||||
kfm.add ("__builtin_strlen", make_unique<kf_strlen> ());
|
||||
|
||||
register_atomic_builtins (kfm);
|
||||
register_varargs_builtins (kfm);
|
||||
}
|
||||
|
||||
/* Known POSIX functions, and some non-standard extensions. */
|
||||
{
|
||||
kfm.add ("fopen", make_unique<kf_fopen> ());
|
||||
kfm.add ("putenv", make_unique<kf_putenv> ());
|
||||
|
||||
register_known_fd_functions (kfm);
|
||||
|
|
@ -1416,7 +1654,7 @@ register_known_functions (known_function_manager &kfm)
|
|||
like this:
|
||||
extern int *___errno(void) __attribute__((__const__));
|
||||
#define errno (*(___errno()))
|
||||
and OS X like this:
|
||||
and macOS like this:
|
||||
extern int * __error(void);
|
||||
#define errno (*__error())
|
||||
and similarly __errno for newlib.
|
||||
|
|
|
|||
|
|
@ -137,6 +137,13 @@ known_function_manager::get_normal_builtin (enum built_in_function name) const
|
|||
return m_combined_fns_arr[name];
|
||||
}
|
||||
|
||||
const known_function *
|
||||
known_function_manager::
|
||||
get_normal_builtin (const builtin_known_function *builtin_kf) const
|
||||
{
|
||||
return get_normal_builtin (builtin_kf->builtin_code ());
|
||||
}
|
||||
|
||||
/* Get any known_function matching IDENTIFIER, without type-checking.
|
||||
Return NULL if there isn't one. */
|
||||
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ private:
|
|||
DISABLE_COPY_AND_ASSIGN (known_function_manager);
|
||||
|
||||
const known_function *get_normal_builtin (enum built_in_function name) const;
|
||||
const known_function *
|
||||
get_normal_builtin (const builtin_known_function *builtin_kf) const;
|
||||
const known_function *get_by_identifier (tree identifier) const;
|
||||
|
||||
/* Map from identifier to known_function instance.
|
||||
|
|
@ -64,6 +66,8 @@ private:
|
|||
known_function *m_combined_fns_arr[CFN_LAST];
|
||||
};
|
||||
|
||||
extern std::unique_ptr<known_function> make_kf_strlen ();
|
||||
|
||||
} // namespace ana
|
||||
|
||||
#endif /* GCC_ANALYZER_KNOWN_FUNCTION_MANAGER_H */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,324 @@
|
|||
/* Symbolic offsets and ranges.
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "config.h"
|
||||
#define INCLUDE_MEMORY
|
||||
#include "system.h"
|
||||
#include "coretypes.h"
|
||||
#include "tree.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "gimple-pretty-print.h"
|
||||
#include "function.h"
|
||||
#include "basic-block.h"
|
||||
#include "gimple.h"
|
||||
#include "gimple-iterator.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "graphviz.h"
|
||||
#include "options.h"
|
||||
#include "cgraph.h"
|
||||
#include "tree-dfa.h"
|
||||
#include "stringpool.h"
|
||||
#include "convert.h"
|
||||
#include "target.h"
|
||||
#include "fold-const.h"
|
||||
#include "tree-pretty-print.h"
|
||||
#include "bitmap.h"
|
||||
#include "analyzer/analyzer.h"
|
||||
#include "analyzer/analyzer-logging.h"
|
||||
#include "ordered-hash-map.h"
|
||||
#include "options.h"
|
||||
#include "analyzer/supergraph.h"
|
||||
#include "sbitmap.h"
|
||||
#include "analyzer/call-string.h"
|
||||
#include "analyzer/program-point.h"
|
||||
#include "analyzer/store.h"
|
||||
#include "analyzer/region-model.h"
|
||||
#include "analyzer/constraint-manager.h"
|
||||
#include "analyzer/analyzer-selftests.h"
|
||||
#include "analyzer/ranges.h"
|
||||
|
||||
#if ENABLE_ANALYZER
|
||||
|
||||
namespace ana {
|
||||
|
||||
/* class symbolic_byte_offset. */
|
||||
|
||||
symbolic_byte_offset::symbolic_byte_offset (int i, region_model_manager &mgr)
|
||||
: m_num_bytes_sval (mgr.get_or_create_int_cst (size_type_node, i))
|
||||
{
|
||||
}
|
||||
|
||||
symbolic_byte_offset::symbolic_byte_offset (const svalue *num_bytes_sval)
|
||||
: m_num_bytes_sval (num_bytes_sval)
|
||||
{
|
||||
}
|
||||
|
||||
symbolic_byte_offset::symbolic_byte_offset (region_offset offset,
|
||||
region_model_manager &mgr)
|
||||
{
|
||||
if (offset.concrete_p ())
|
||||
{
|
||||
bit_offset_t num_bits = offset.get_bit_offset ();
|
||||
gcc_assert (num_bits % BITS_PER_UNIT == 0);
|
||||
byte_offset_t num_bytes = num_bits / BITS_PER_UNIT;
|
||||
m_num_bytes_sval = mgr.get_or_create_int_cst (size_type_node, num_bytes);
|
||||
}
|
||||
else
|
||||
m_num_bytes_sval = offset.get_symbolic_byte_offset ();
|
||||
}
|
||||
|
||||
void
|
||||
symbolic_byte_offset::dump_to_pp (pretty_printer *pp, bool simple) const
|
||||
{
|
||||
pp_string (pp, "byte ");
|
||||
m_num_bytes_sval->dump_to_pp (pp, simple);
|
||||
}
|
||||
|
||||
void
|
||||
symbolic_byte_offset::dump (bool simple) const
|
||||
{
|
||||
pretty_printer pp;
|
||||
pp_format_decoder (&pp) = default_tree_printer;
|
||||
pp_show_color (&pp) = pp_show_color (global_dc->printer);
|
||||
pp.buffer->stream = stderr;
|
||||
dump_to_pp (&pp, simple);
|
||||
pp_newline (&pp);
|
||||
pp_flush (&pp);
|
||||
}
|
||||
|
||||
tree
|
||||
symbolic_byte_offset::maybe_get_constant () const
|
||||
{
|
||||
return m_num_bytes_sval->maybe_get_constant ();
|
||||
}
|
||||
|
||||
/* class symbolic_byte_range. */
|
||||
|
||||
symbolic_byte_range::symbolic_byte_range (region_offset start,
|
||||
const svalue *num_bytes,
|
||||
region_model_manager &mgr)
|
||||
: m_start (start, mgr),
|
||||
m_size (num_bytes)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
symbolic_byte_range::dump_to_pp (pretty_printer *pp,
|
||||
bool simple,
|
||||
region_model_manager &mgr) const
|
||||
{
|
||||
if (empty_p ())
|
||||
{
|
||||
pp_string (pp, "empty");
|
||||
return;
|
||||
}
|
||||
|
||||
if (tree size_cst = m_size.maybe_get_constant ())
|
||||
if (integer_onep (size_cst))
|
||||
{
|
||||
pp_string (pp, "byte ");
|
||||
m_start.get_svalue ()->dump_to_pp (pp, simple);
|
||||
return;
|
||||
}
|
||||
|
||||
pp_string (pp, "bytes ");
|
||||
m_start.get_svalue ()->dump_to_pp (pp, simple);
|
||||
pp_string (pp, " to ");
|
||||
get_last_byte_offset (mgr).get_svalue ()->dump_to_pp (pp, simple);
|
||||
}
|
||||
|
||||
void
|
||||
symbolic_byte_range::dump (bool simple, region_model_manager &mgr) const
|
||||
{
|
||||
pretty_printer pp;
|
||||
pp_format_decoder (&pp) = default_tree_printer;
|
||||
pp_show_color (&pp) = pp_show_color (global_dc->printer);
|
||||
pp.buffer->stream = stderr;
|
||||
dump_to_pp (&pp, simple, mgr);
|
||||
pp_newline (&pp);
|
||||
pp_flush (&pp);
|
||||
}
|
||||
|
||||
bool
|
||||
symbolic_byte_range::empty_p () const
|
||||
{
|
||||
tree cst = m_size.maybe_get_constant ();
|
||||
if (!cst)
|
||||
return false;
|
||||
return zerop (cst);
|
||||
}
|
||||
|
||||
symbolic_byte_offset
|
||||
symbolic_byte_range::get_last_byte_offset (region_model_manager &mgr) const
|
||||
{
|
||||
gcc_assert (!empty_p ());
|
||||
const symbolic_byte_offset one (1, mgr);
|
||||
return symbolic_byte_offset
|
||||
(mgr.get_or_create_binop (size_type_node,
|
||||
MINUS_EXPR,
|
||||
get_next_byte_offset (mgr).get_svalue (),
|
||||
one.get_svalue ()));
|
||||
}
|
||||
|
||||
symbolic_byte_offset
|
||||
symbolic_byte_range::get_next_byte_offset (region_model_manager &mgr) const
|
||||
{
|
||||
return symbolic_byte_offset (mgr.get_or_create_binop (size_type_node,
|
||||
PLUS_EXPR,
|
||||
m_start.get_svalue (),
|
||||
m_size.get_svalue ()));
|
||||
}
|
||||
|
||||
/* Attempt to determine if THIS range intersects OTHER,
|
||||
using constraints from MODEL. */
|
||||
|
||||
tristate
|
||||
symbolic_byte_range::intersection (const symbolic_byte_range &other,
|
||||
const region_model &model) const
|
||||
{
|
||||
/* For brevity, consider THIS to be "range A", and OTHER to be "range B". */
|
||||
|
||||
region_model_manager *mgr = model.get_manager ();
|
||||
|
||||
const svalue *first_sval_a = m_start.get_svalue ();
|
||||
const svalue *first_sval_b = other.m_start.get_svalue ();
|
||||
const svalue *last_sval_a = get_last_byte_offset (*mgr).get_svalue ();
|
||||
const svalue *last_sval_b = other.get_last_byte_offset (*mgr).get_svalue ();
|
||||
|
||||
if (m_size.get_svalue ()->get_kind () == SK_UNKNOWN
|
||||
|| other.m_size.get_svalue ()->get_kind () == SK_UNKNOWN)
|
||||
{
|
||||
if (first_sval_a == first_sval_b)
|
||||
return tristate::TS_TRUE;
|
||||
else
|
||||
return tristate::TS_UNKNOWN;
|
||||
}
|
||||
|
||||
if (first_sval_a == first_sval_b)
|
||||
return tristate::TS_TRUE;
|
||||
|
||||
/* Is B fully before A? */
|
||||
tristate b_fully_before_a = model.eval_condition (last_sval_b,
|
||||
LT_EXPR,
|
||||
first_sval_a);
|
||||
/* Is B fully after A? */
|
||||
tristate b_fully_after_a = model.eval_condition (first_sval_b,
|
||||
GT_EXPR,
|
||||
last_sval_a);
|
||||
|
||||
if (b_fully_before_a.is_true ()
|
||||
|| b_fully_after_a.is_true ())
|
||||
return tristate::TS_FALSE;
|
||||
|
||||
if (b_fully_before_a.is_unknown ()
|
||||
|| b_fully_after_a.is_unknown ())
|
||||
return tristate::TS_UNKNOWN;
|
||||
|
||||
return tristate::TS_TRUE;
|
||||
}
|
||||
|
||||
#if CHECKING_P
|
||||
|
||||
namespace selftest {
|
||||
|
||||
static void test_intersects (void)
|
||||
{
|
||||
region_model_manager mgr;
|
||||
region_model m (&mgr);
|
||||
|
||||
/* Test various concrete ranges. */
|
||||
symbolic_byte_offset zero (0, mgr);
|
||||
symbolic_byte_offset one (1, mgr);
|
||||
symbolic_byte_offset five (5, mgr);
|
||||
symbolic_byte_offset nine (9, mgr);
|
||||
symbolic_byte_offset ten (10, mgr);
|
||||
|
||||
symbolic_byte_range r0_9 (zero, ten);
|
||||
symbolic_byte_range r0 (zero, one);
|
||||
symbolic_byte_range r5_9 (five, five);
|
||||
symbolic_byte_range r9 (nine, one);
|
||||
symbolic_byte_range r10 (ten, one);
|
||||
symbolic_byte_range r10_19 (ten, ten);
|
||||
|
||||
ASSERT_EQ (r0_9.get_start_byte_offset (), zero);
|
||||
ASSERT_EQ (r0_9.get_size_in_bytes (), ten);
|
||||
ASSERT_EQ (r0_9.get_next_byte_offset (mgr), ten);
|
||||
ASSERT_EQ (r0_9.get_last_byte_offset (mgr), nine);
|
||||
|
||||
ASSERT_EQ (r0_9.intersection (r0, m), tristate::TS_TRUE);
|
||||
ASSERT_EQ (r0.intersection (r0_9, m), tristate::TS_TRUE);
|
||||
ASSERT_EQ (r0_9.intersection (r9, m), tristate::TS_TRUE);
|
||||
ASSERT_EQ (r9.intersection (r0_9, m), tristate::TS_TRUE);
|
||||
ASSERT_EQ (r0_9.intersection (r10, m), tristate::TS_FALSE);
|
||||
ASSERT_EQ (r10.intersection (r0_9, m), tristate::TS_FALSE);
|
||||
|
||||
ASSERT_EQ (r5_9.intersection (r0, m), tristate::TS_FALSE);
|
||||
ASSERT_EQ (r0.intersection (r5_9, m), tristate::TS_FALSE);
|
||||
ASSERT_EQ (r9.intersection (r5_9, m), tristate::TS_TRUE);
|
||||
ASSERT_EQ (r10.intersection (r5_9, m), tristate::TS_FALSE);
|
||||
|
||||
/* Test various symbolic ranges. */
|
||||
tree x = build_global_decl ("x", size_type_node);
|
||||
const svalue *x_init_sval = m.get_rvalue (x, nullptr);
|
||||
tree y = build_global_decl ("y", size_type_node);
|
||||
const svalue *y_init_sval = m.get_rvalue (y, nullptr);
|
||||
|
||||
symbolic_byte_range r0_x_minus_1 (zero, x_init_sval);
|
||||
symbolic_byte_range rx (x_init_sval, one);
|
||||
symbolic_byte_range r0_y_minus_1 (zero, y_init_sval);
|
||||
symbolic_byte_range ry (y_init_sval, one);
|
||||
symbolic_byte_range rx_x_plus_y_minus_1 (x_init_sval, y_init_sval);
|
||||
|
||||
ASSERT_EQ (rx_x_plus_y_minus_1.get_start_byte_offset (), x_init_sval);
|
||||
ASSERT_EQ (rx_x_plus_y_minus_1.get_size_in_bytes (), y_init_sval);
|
||||
ASSERT_EQ
|
||||
(rx_x_plus_y_minus_1.get_next_byte_offset (mgr).get_svalue ()->get_kind (),
|
||||
SK_BINOP);
|
||||
ASSERT_EQ
|
||||
(rx_x_plus_y_minus_1.get_last_byte_offset (mgr).get_svalue ()->get_kind (),
|
||||
SK_BINOP);
|
||||
|
||||
ASSERT_EQ (rx.intersection (ry, m), tristate::TS_UNKNOWN);
|
||||
ASSERT_EQ (r0_x_minus_1.intersection (r0, m), tristate::TS_TRUE);
|
||||
#if 0
|
||||
ASSERT_EQ (r0_x_minus_1.intersection (rx, m), tristate::TS_FALSE);
|
||||
/* Fails (with UNKNOWN): b_fully_after_a is UNKNOWN, when it could
|
||||
be TRUE: last of A is (x - 1), but it's not necessarily true that
|
||||
X > (x - 1), for the case where x is (unsigned)0. */
|
||||
#endif
|
||||
ASSERT_EQ (r0_x_minus_1.intersection (r0_y_minus_1, m), tristate::TS_TRUE);
|
||||
// TODO: etc
|
||||
}
|
||||
|
||||
/* Run all of the selftests within this file. */
|
||||
|
||||
void
|
||||
analyzer_ranges_cc_tests ()
|
||||
{
|
||||
test_intersects ();
|
||||
}
|
||||
|
||||
} // namespace selftest
|
||||
|
||||
#endif /* CHECKING_P */
|
||||
|
||||
} // namespace ana
|
||||
|
||||
#endif /* #if ENABLE_ANALYZER */
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
/* Symbolic offsets and ranges.
|
||||
Copyright (C) 2023 Free Software Foundation, Inc.
|
||||
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it
|
||||
under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3, or (at your option)
|
||||
any later version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GCC_ANALYZER_RANGES_H
|
||||
#define GCC_ANALYZER_RANGES_H
|
||||
|
||||
namespace ana {
|
||||
|
||||
/* Wrapper around an svalue for a value measured in bytes. */
|
||||
|
||||
class symbolic_byte_offset
|
||||
{
|
||||
public:
|
||||
explicit symbolic_byte_offset (int i, region_model_manager &mgr);
|
||||
symbolic_byte_offset (const svalue *num_bytes_sval);
|
||||
explicit symbolic_byte_offset (region_offset offset,
|
||||
region_model_manager &mgr);
|
||||
|
||||
const svalue *get_svalue () const { return m_num_bytes_sval; }
|
||||
tree maybe_get_constant () const;
|
||||
|
||||
void dump_to_pp (pretty_printer *pp, bool) const;
|
||||
void dump (bool) const;
|
||||
|
||||
bool operator== (const symbolic_byte_offset &other) const
|
||||
{
|
||||
return m_num_bytes_sval == other.m_num_bytes_sval;
|
||||
}
|
||||
|
||||
private:
|
||||
const svalue *m_num_bytes_sval;
|
||||
};
|
||||
|
||||
/* A range of byte offsets, where both the start and size of the
|
||||
range can be symbolic. */
|
||||
|
||||
class symbolic_byte_range
|
||||
{
|
||||
public:
|
||||
symbolic_byte_range (symbolic_byte_offset start,
|
||||
symbolic_byte_offset size)
|
||||
: m_start (start),
|
||||
m_size (size)
|
||||
{
|
||||
}
|
||||
|
||||
symbolic_byte_range (region_offset start,
|
||||
const svalue *num_bytes,
|
||||
region_model_manager &mgr);
|
||||
|
||||
void dump_to_pp (pretty_printer *pp,
|
||||
bool simple,
|
||||
region_model_manager &mgr) const;
|
||||
void dump (bool, region_model_manager &mgr) const;
|
||||
|
||||
bool empty_p () const;
|
||||
|
||||
symbolic_byte_offset get_start_byte_offset () const
|
||||
{
|
||||
return m_start;
|
||||
}
|
||||
symbolic_byte_offset get_last_byte_offset (region_model_manager &mgr) const;
|
||||
symbolic_byte_offset get_size_in_bytes () const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
symbolic_byte_offset get_next_byte_offset (region_model_manager &mgr) const;
|
||||
|
||||
tristate intersection (const symbolic_byte_range &other,
|
||||
const region_model &model) const;
|
||||
|
||||
private:
|
||||
symbolic_byte_offset m_start;
|
||||
symbolic_byte_offset m_size;
|
||||
};
|
||||
|
||||
} // namespace ana
|
||||
|
||||
#endif /* GCC_ANALYZER_RANGES_H */
|
||||
|
|
@ -310,6 +310,25 @@ region_model_manager::get_or_create_initial_value (const region *reg,
|
|||
get_or_create_initial_value (original_reg));
|
||||
}
|
||||
|
||||
/* Simplify:
|
||||
INIT_VAL(ELEMENT_REG(STRING_REG), CONSTANT_SVAL)
|
||||
to:
|
||||
CONSTANT_SVAL(STRING[N]). */
|
||||
if (const element_region *element_reg = reg->dyn_cast_element_region ())
|
||||
if (tree cst_idx = element_reg->get_index ()->maybe_get_constant ())
|
||||
if (const string_region *string_reg
|
||||
= element_reg->get_parent_region ()->dyn_cast_string_region ())
|
||||
if (tree_fits_shwi_p (cst_idx))
|
||||
{
|
||||
HOST_WIDE_INT idx = tree_to_shwi (cst_idx);
|
||||
tree string_cst = string_reg->get_string_cst ();
|
||||
if (idx >= 0 && idx <= TREE_STRING_LENGTH (string_cst))
|
||||
{
|
||||
int ch = TREE_STRING_POINTER (string_cst)[idx];
|
||||
return get_or_create_int_cst (reg->get_type (), ch);
|
||||
}
|
||||
}
|
||||
|
||||
/* INIT_VAL (*UNKNOWN_PTR) -> UNKNOWN_VAL. */
|
||||
if (reg->symbolic_for_unknown_ptr_p ())
|
||||
return get_or_create_unknown_svalue (reg->get_type ());
|
||||
|
|
|
|||
|
|
@ -184,27 +184,6 @@ reachable_regions::handle_sval (const svalue *sval)
|
|||
}
|
||||
add (pointee, ptr_is_mutable);
|
||||
}
|
||||
else if (sval->get_type ()
|
||||
&& TREE_CODE (sval->get_type ()) == POINTER_TYPE
|
||||
&& sval->get_kind () == SK_CONJURED)
|
||||
{
|
||||
/* Also add symbolic regions for pointers, but only for conjured svalues
|
||||
for the LHS of a stmt. Doing it for more leads to state explosions
|
||||
on chains of calls to external functions, due to each conjured svalue
|
||||
potentially being modified at each successive call, recursively. */
|
||||
const conjured_svalue *conjured_sval = (const conjured_svalue *)sval;
|
||||
if (conjured_sval->lhs_value_p ())
|
||||
{
|
||||
const region *pointee
|
||||
= m_model->get_manager ()->get_symbolic_region (sval);
|
||||
/* Use const-ness of pointer type to affect mutability. */
|
||||
bool ptr_is_mutable = true;
|
||||
if (TYPE_READONLY (TREE_TYPE (sval->get_type ())))
|
||||
ptr_is_mutable = false;
|
||||
add (pointee, ptr_is_mutable);
|
||||
}
|
||||
}
|
||||
|
||||
/* Treat all svalues within a compound_svalue as reachable. */
|
||||
if (const compound_svalue *compound_sval
|
||||
= sval->dyn_cast_compound_svalue ())
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -236,6 +236,11 @@ public:
|
|||
|
||||
struct append_regions_cb_data;
|
||||
|
||||
typedef void (*pop_frame_callback) (const region_model *model,
|
||||
const region_model *prev_model,
|
||||
const svalue *retval,
|
||||
region_model_context *ctxt);
|
||||
|
||||
/* A region_model encapsulates a representation of the state of memory, with
|
||||
a tree of regions, along with their associated values.
|
||||
The representation is graph-like because values can be pointers to
|
||||
|
|
@ -367,6 +372,19 @@ class region_model
|
|||
void purge_region (const region *reg);
|
||||
void fill_region (const region *reg, const svalue *sval);
|
||||
void zero_fill_region (const region *reg);
|
||||
void write_bytes (const region *dest_reg,
|
||||
const svalue *num_bytes_sval,
|
||||
const svalue *sval,
|
||||
region_model_context *ctxt);
|
||||
const svalue *read_bytes (const region *src_reg,
|
||||
tree src_ptr_expr,
|
||||
const svalue *num_bytes_sval,
|
||||
region_model_context *ctxt) const;
|
||||
void copy_bytes (const region *dest_reg,
|
||||
const region *src_reg,
|
||||
tree src_ptr_expr,
|
||||
const svalue *num_bytes_sval,
|
||||
region_model_context *ctxt);
|
||||
void mark_region_as_unknown (const region *reg, uncertainty_t *uncertainty);
|
||||
|
||||
tristate eval_condition (const svalue *lhs,
|
||||
|
|
@ -387,9 +405,12 @@ class region_model
|
|||
region_model_context *ctxt,
|
||||
rejected_constraint **out);
|
||||
|
||||
const region *
|
||||
get_or_create_region_for_heap_alloc (const svalue *size_in_bytes,
|
||||
region_model_context *ctxt);
|
||||
const region *
|
||||
get_or_create_region_for_heap_alloc (const svalue *size_in_bytes,
|
||||
region_model_context *ctxt,
|
||||
bool update_state_machine = false,
|
||||
const call_details *cd = nullptr);
|
||||
|
||||
const region *create_region_for_alloca (const svalue *size_in_bytes,
|
||||
region_model_context *ctxt);
|
||||
void get_referenced_base_regions (auto_bitmap &out_ids) const;
|
||||
|
|
@ -448,6 +469,13 @@ class region_model
|
|||
|
||||
const svalue *get_store_value (const region *reg,
|
||||
region_model_context *ctxt) const;
|
||||
const svalue *get_store_bytes (const region *base_reg,
|
||||
const byte_range &bytes,
|
||||
region_model_context *ctxt) const;
|
||||
const svalue *scan_for_null_terminator (const region *reg,
|
||||
tree expr,
|
||||
const svalue **out_sval,
|
||||
region_model_context *ctxt) const;
|
||||
|
||||
bool region_exists_p (const region *reg) const;
|
||||
|
||||
|
|
@ -455,9 +483,6 @@ class region_model
|
|||
|
||||
const svalue *get_capacity (const region *reg) const;
|
||||
|
||||
const svalue *get_string_size (const svalue *sval) const;
|
||||
const svalue *get_string_size (const region *reg) const;
|
||||
|
||||
bool replay_call_summary (call_summary_replay &r,
|
||||
const region_model &summary);
|
||||
|
||||
|
|
@ -476,6 +501,11 @@ class region_model
|
|||
const svalue *old_ptr_sval,
|
||||
const svalue *new_ptr_sval);
|
||||
|
||||
/* Implemented in sm-malloc.cc. */
|
||||
void
|
||||
transition_ptr_sval_non_null (region_model_context *ctxt,
|
||||
const svalue *new_ptr_sval);
|
||||
|
||||
/* Implemented in sm-taint.cc. */
|
||||
void mark_as_tainted (const svalue *sval,
|
||||
region_model_context *ctxt);
|
||||
|
|
@ -494,6 +524,35 @@ class region_model
|
|||
const svalue *sval_hint,
|
||||
region_model_context *ctxt) const;
|
||||
|
||||
void
|
||||
check_for_null_terminated_string_arg (const call_details &cd,
|
||||
unsigned idx);
|
||||
const svalue *
|
||||
check_for_null_terminated_string_arg (const call_details &cd,
|
||||
unsigned idx,
|
||||
bool include_terminator,
|
||||
const svalue **out_sval);
|
||||
|
||||
const builtin_known_function *
|
||||
get_builtin_kf (const gcall *call,
|
||||
region_model_context *ctxt = NULL) const;
|
||||
|
||||
static void
|
||||
register_pop_frame_callback (const pop_frame_callback &callback)
|
||||
{
|
||||
pop_frame_callbacks.safe_push (callback);
|
||||
}
|
||||
|
||||
static void
|
||||
notify_on_pop_frame (const region_model *model,
|
||||
const region_model *prev_model,
|
||||
const svalue *retval,
|
||||
region_model_context *ctxt)
|
||||
{
|
||||
for (auto &callback : pop_frame_callbacks)
|
||||
callback (model, prev_model, retval, ctxt);
|
||||
}
|
||||
|
||||
private:
|
||||
const region *get_lvalue_1 (path_var pv, region_model_context *ctxt) const;
|
||||
const svalue *get_rvalue_1 (path_var pv, region_model_context *ctxt) const;
|
||||
|
|
@ -577,10 +636,13 @@ private:
|
|||
region_model_context *ctxt) const;
|
||||
|
||||
void check_call_args (const call_details &cd) const;
|
||||
void check_call_format_attr (const call_details &cd,
|
||||
tree format_attr) const;
|
||||
void check_external_function_for_access_attr (const gcall *call,
|
||||
tree callee_fndecl,
|
||||
region_model_context *ctxt) const;
|
||||
|
||||
static auto_vec<pop_frame_callback> pop_frame_callbacks;
|
||||
/* Storing this here to avoid passing it around everywhere. */
|
||||
region_model_manager *const m_mgr;
|
||||
|
||||
|
|
@ -609,13 +671,19 @@ class region_model_context
|
|||
{
|
||||
public:
|
||||
/* Hook for clients to store pending diagnostics.
|
||||
Return true if the diagnostic was stored, or false if it was deleted. */
|
||||
virtual bool warn (std::unique_ptr<pending_diagnostic> d) = 0;
|
||||
Return true if the diagnostic was stored, or false if it was deleted.
|
||||
Optionally provide a custom stmt_finder. */
|
||||
virtual bool warn (std::unique_ptr<pending_diagnostic> d,
|
||||
const stmt_finder *custom_finder = NULL) = 0;
|
||||
|
||||
/* Hook for clients to add a note to the last previously stored
|
||||
pending diagnostic. */
|
||||
virtual void add_note (std::unique_ptr<pending_note> pn) = 0;
|
||||
|
||||
/* Hook for clients to add an event to the last previously stored
|
||||
pending diagnostic. */
|
||||
virtual void add_event (std::unique_ptr<checker_event> event) = 0;
|
||||
|
||||
/* Hook for clients to be notified when an SVAL that was reachable
|
||||
in a previous state is no longer live, so that clients can emit warnings
|
||||
about leaks. */
|
||||
|
|
@ -713,6 +781,8 @@ class region_model_context
|
|||
|
||||
/* Get the current statement, if any. */
|
||||
virtual const gimple *get_stmt () const = 0;
|
||||
|
||||
virtual const exploded_graph *get_eg () const = 0;
|
||||
};
|
||||
|
||||
/* A "do nothing" subclass of region_model_context. */
|
||||
|
|
@ -720,8 +790,10 @@ class region_model_context
|
|||
class noop_region_model_context : public region_model_context
|
||||
{
|
||||
public:
|
||||
bool warn (std::unique_ptr<pending_diagnostic>) override { return false; }
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d,
|
||||
const stmt_finder *custom_finder) override { return false; }
|
||||
void add_note (std::unique_ptr<pending_note>) override;
|
||||
void add_event (std::unique_ptr<checker_event>) override;
|
||||
void on_svalue_leak (const svalue *) override {}
|
||||
void on_liveness_change (const svalue_set &,
|
||||
const region_model *) override {}
|
||||
|
|
@ -767,6 +839,7 @@ public:
|
|||
}
|
||||
|
||||
const gimple *get_stmt () const override { return NULL; }
|
||||
const exploded_graph *get_eg () const override { return NULL; }
|
||||
};
|
||||
|
||||
/* A subclass of region_model_context for determining if operations fail
|
||||
|
|
@ -795,94 +868,121 @@ private:
|
|||
class region_model_context_decorator : public region_model_context
|
||||
{
|
||||
public:
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d) override
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d,
|
||||
const stmt_finder *custom_finder)
|
||||
{
|
||||
return m_inner->warn (std::move (d));
|
||||
if (m_inner)
|
||||
return m_inner->warn (std::move (d), custom_finder);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
void add_note (std::unique_ptr<pending_note> pn) override
|
||||
{
|
||||
m_inner->add_note (std::move (pn));
|
||||
if (m_inner)
|
||||
m_inner->add_note (std::move (pn));
|
||||
}
|
||||
void add_event (std::unique_ptr<checker_event> event) override;
|
||||
|
||||
void on_svalue_leak (const svalue *sval) override
|
||||
{
|
||||
m_inner->on_svalue_leak (sval);
|
||||
if (m_inner)
|
||||
m_inner->on_svalue_leak (sval);
|
||||
}
|
||||
|
||||
void on_liveness_change (const svalue_set &live_svalues,
|
||||
const region_model *model) override
|
||||
{
|
||||
m_inner->on_liveness_change (live_svalues, model);
|
||||
if (m_inner)
|
||||
m_inner->on_liveness_change (live_svalues, model);
|
||||
}
|
||||
|
||||
logger *get_logger () override
|
||||
{
|
||||
return m_inner->get_logger ();
|
||||
if (m_inner)
|
||||
return m_inner->get_logger ();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void on_condition (const svalue *lhs,
|
||||
enum tree_code op,
|
||||
const svalue *rhs) override
|
||||
{
|
||||
m_inner->on_condition (lhs, op, rhs);
|
||||
if (m_inner)
|
||||
m_inner->on_condition (lhs, op, rhs);
|
||||
}
|
||||
|
||||
void on_bounded_ranges (const svalue &sval,
|
||||
const bounded_ranges &ranges) override
|
||||
{
|
||||
m_inner->on_bounded_ranges (sval, ranges);
|
||||
if (m_inner)
|
||||
m_inner->on_bounded_ranges (sval, ranges);
|
||||
}
|
||||
|
||||
void on_pop_frame (const frame_region *frame_reg) override
|
||||
{
|
||||
m_inner->on_pop_frame (frame_reg);
|
||||
if (m_inner)
|
||||
m_inner->on_pop_frame (frame_reg);
|
||||
}
|
||||
|
||||
void on_unknown_change (const svalue *sval, bool is_mutable) override
|
||||
{
|
||||
m_inner->on_unknown_change (sval, is_mutable);
|
||||
if (m_inner)
|
||||
m_inner->on_unknown_change (sval, is_mutable);
|
||||
}
|
||||
|
||||
void on_phi (const gphi *phi, tree rhs) override
|
||||
{
|
||||
m_inner->on_phi (phi, rhs);
|
||||
if (m_inner)
|
||||
m_inner->on_phi (phi, rhs);
|
||||
}
|
||||
|
||||
void on_unexpected_tree_code (tree t,
|
||||
const dump_location_t &loc) override
|
||||
{
|
||||
m_inner->on_unexpected_tree_code (t, loc);
|
||||
if (m_inner)
|
||||
m_inner->on_unexpected_tree_code (t, loc);
|
||||
}
|
||||
|
||||
void on_escaped_function (tree fndecl) override
|
||||
{
|
||||
m_inner->on_escaped_function (fndecl);
|
||||
if (m_inner)
|
||||
m_inner->on_escaped_function (fndecl);
|
||||
}
|
||||
|
||||
uncertainty_t *get_uncertainty () override
|
||||
{
|
||||
return m_inner->get_uncertainty ();
|
||||
if (m_inner)
|
||||
return m_inner->get_uncertainty ();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void purge_state_involving (const svalue *sval) override
|
||||
{
|
||||
m_inner->purge_state_involving (sval);
|
||||
if (m_inner)
|
||||
m_inner->purge_state_involving (sval);
|
||||
}
|
||||
|
||||
void bifurcate (std::unique_ptr<custom_edge_info> info) override
|
||||
{
|
||||
m_inner->bifurcate (std::move (info));
|
||||
if (m_inner)
|
||||
m_inner->bifurcate (std::move (info));
|
||||
}
|
||||
|
||||
void terminate_path () override
|
||||
{
|
||||
m_inner->terminate_path ();
|
||||
if (m_inner)
|
||||
m_inner->terminate_path ();
|
||||
}
|
||||
|
||||
const extrinsic_state *get_ext_state () const override
|
||||
{
|
||||
return m_inner->get_ext_state ();
|
||||
if (m_inner)
|
||||
return m_inner->get_ext_state ();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool get_state_map_by_name (const char *name,
|
||||
|
|
@ -892,47 +992,61 @@ class region_model_context_decorator : public region_model_context
|
|||
std::unique_ptr<sm_context> *out_sm_context)
|
||||
override
|
||||
{
|
||||
return m_inner->get_state_map_by_name (name, out_smap, out_sm, out_sm_idx,
|
||||
out_sm_context);
|
||||
if (m_inner)
|
||||
return m_inner->get_state_map_by_name (name, out_smap, out_sm, out_sm_idx,
|
||||
out_sm_context);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const gimple *get_stmt () const override
|
||||
{
|
||||
return m_inner->get_stmt ();
|
||||
if (m_inner)
|
||||
return m_inner->get_stmt ();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const exploded_graph *get_eg () const override
|
||||
{
|
||||
if (m_inner)
|
||||
return m_inner->get_eg ();
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
region_model_context_decorator (region_model_context *inner)
|
||||
: m_inner (inner)
|
||||
{
|
||||
gcc_assert (m_inner);
|
||||
}
|
||||
|
||||
region_model_context *m_inner;
|
||||
};
|
||||
|
||||
/* Subclass of region_model_context_decorator that adds a note
|
||||
when saving diagnostics. */
|
||||
/* Subclass of region_model_context_decorator with a hook for adding
|
||||
notes/events when saving diagnostics. */
|
||||
|
||||
class note_adding_context : public region_model_context_decorator
|
||||
class annotating_context : public region_model_context_decorator
|
||||
{
|
||||
public:
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d) override
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d,
|
||||
const stmt_finder *custom_finder) override
|
||||
{
|
||||
if (m_inner->warn (std::move (d)))
|
||||
{
|
||||
add_note (make_note ());
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
if (m_inner)
|
||||
if (m_inner->warn (std::move (d), custom_finder))
|
||||
{
|
||||
add_annotations ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Hook to make the new note. */
|
||||
virtual std::unique_ptr<pending_note> make_note () = 0;
|
||||
/* Hook to add new event(s)/note(s) */
|
||||
virtual void add_annotations () = 0;
|
||||
|
||||
protected:
|
||||
note_adding_context (region_model_context *inner)
|
||||
annotating_context (region_model_context *inner)
|
||||
: region_model_context_decorator (inner)
|
||||
{
|
||||
}
|
||||
|
|
@ -1082,7 +1196,8 @@ using namespace ::selftest;
|
|||
class test_region_model_context : public noop_region_model_context
|
||||
{
|
||||
public:
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d) final override
|
||||
bool warn (std::unique_ptr<pending_diagnostic> d,
|
||||
const stmt_finder *custom_finder) final override
|
||||
{
|
||||
m_diagnostics.safe_push (d.release ());
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -1294,8 +1294,19 @@ fd_state_machine::check_for_fd_attrs (
|
|||
const gcall *call, const tree callee_fndecl, const char *attr_name,
|
||||
access_directions fd_attr_access_dir) const
|
||||
{
|
||||
/* Handle interesting fd attributes of the callee_fndecl,
|
||||
or prioritize those of the builtin that callee_fndecl is
|
||||
expected to be.
|
||||
Might want this to be controlled by a flag. */
|
||||
tree fndecl = callee_fndecl;
|
||||
/* If call is recognized as a builtin known_function,
|
||||
use that builtin's function_decl. */
|
||||
if (const region_model *old_model = sm_ctxt->get_old_region_model ())
|
||||
if (const builtin_known_function *builtin_kf
|
||||
= old_model->get_builtin_kf (call))
|
||||
fndecl = builtin_kf->builtin_decl ();
|
||||
|
||||
tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));
|
||||
tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
|
||||
attrs = lookup_attribute (attr_name, attrs);
|
||||
if (!attrs)
|
||||
return;
|
||||
|
|
@ -1325,13 +1336,15 @@ fd_state_machine::check_for_fd_attrs (
|
|||
// attributes
|
||||
{
|
||||
|
||||
/* Do use the fndecl that caused the warning so that the
|
||||
misused attributes are printed and the user not confused. */
|
||||
if (is_closed_fd_p (state))
|
||||
{
|
||||
|
||||
sm_ctxt->warn (node, stmt, arg,
|
||||
make_unique<fd_use_after_close>
|
||||
(*this, diag_arg,
|
||||
callee_fndecl, attr_name,
|
||||
fndecl, attr_name,
|
||||
arg_idx));
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1343,7 +1356,7 @@ fd_state_machine::check_for_fd_attrs (
|
|||
sm_ctxt->warn (node, stmt, arg,
|
||||
make_unique<fd_use_without_check>
|
||||
(*this, diag_arg,
|
||||
callee_fndecl, attr_name,
|
||||
fndecl, attr_name,
|
||||
arg_idx));
|
||||
continue;
|
||||
}
|
||||
|
|
@ -1361,7 +1374,7 @@ fd_state_machine::check_for_fd_attrs (
|
|||
node, stmt, arg,
|
||||
make_unique<fd_access_mode_mismatch> (*this, diag_arg,
|
||||
DIRS_WRITE,
|
||||
callee_fndecl,
|
||||
fndecl,
|
||||
attr_name,
|
||||
arg_idx));
|
||||
}
|
||||
|
|
@ -1375,7 +1388,7 @@ fd_state_machine::check_for_fd_attrs (
|
|||
node, stmt, arg,
|
||||
make_unique<fd_access_mode_mismatch> (*this, diag_arg,
|
||||
DIRS_READ,
|
||||
callee_fndecl,
|
||||
fndecl,
|
||||
attr_name,
|
||||
arg_idx));
|
||||
}
|
||||
|
|
@ -2282,10 +2295,16 @@ public:
|
|||
const fd_state_machine *fd_sm;
|
||||
std::unique_ptr<sm_context> sm_ctxt;
|
||||
if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
const extrinsic_state *ext_state = ctxt->get_ext_state ();
|
||||
if (!ext_state)
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
|
||||
return fd_sm->on_socket (cd, m_success, sm_ctxt.get (), *ext_state);
|
||||
}
|
||||
|
|
@ -2329,10 +2348,16 @@ public:
|
|||
const fd_state_machine *fd_sm;
|
||||
std::unique_ptr<sm_context> sm_ctxt;
|
||||
if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
const extrinsic_state *ext_state = ctxt->get_ext_state ();
|
||||
if (!ext_state)
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
return fd_sm->on_bind (cd, m_success, sm_ctxt.get (), *ext_state);
|
||||
}
|
||||
};
|
||||
|
|
@ -2374,10 +2399,16 @@ class kf_listen : public known_function
|
|||
const fd_state_machine *fd_sm;
|
||||
std::unique_ptr<sm_context> sm_ctxt;
|
||||
if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
const extrinsic_state *ext_state = ctxt->get_ext_state ();
|
||||
if (!ext_state)
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
|
||||
return fd_sm->on_listen (cd, m_success, sm_ctxt.get (), *ext_state);
|
||||
}
|
||||
|
|
@ -2420,10 +2451,16 @@ class kf_accept : public known_function
|
|||
const fd_state_machine *fd_sm;
|
||||
std::unique_ptr<sm_context> sm_ctxt;
|
||||
if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
const extrinsic_state *ext_state = ctxt->get_ext_state ();
|
||||
if (!ext_state)
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
|
||||
return fd_sm->on_accept (cd, m_success, sm_ctxt.get (), *ext_state);
|
||||
}
|
||||
|
|
@ -2469,10 +2506,16 @@ public:
|
|||
const fd_state_machine *fd_sm;
|
||||
std::unique_ptr<sm_context> sm_ctxt;
|
||||
if (!get_fd_state (ctxt, &smap, &fd_sm, NULL, &sm_ctxt))
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
const extrinsic_state *ext_state = ctxt->get_ext_state ();
|
||||
if (!ext_state)
|
||||
return true;
|
||||
{
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
return true;
|
||||
}
|
||||
|
||||
return fd_sm->on_connect (cd, m_success, sm_ctxt.get (), *ext_state);
|
||||
}
|
||||
|
|
@ -2687,6 +2730,7 @@ public:
|
|||
const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
|
||||
model->set_value (base_reg, new_sval, cd.get_ctxt ());
|
||||
}
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -494,7 +494,7 @@ make_fileptr_state_machine (logger *logger)
|
|||
effects that are out of scope for the analyzer: we only want to model
|
||||
the effects on the return value. */
|
||||
|
||||
class kf_stdio_output_fn : public known_function
|
||||
class kf_stdio_output_fn : public pure_known_function_with_default_return
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &) const final override
|
||||
|
|
@ -507,7 +507,7 @@ public:
|
|||
|
||||
/* Handler for "ferror"". */
|
||||
|
||||
class kf_ferror : public known_function
|
||||
class kf_ferror : public pure_known_function_with_default_return
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
|
|
@ -521,7 +521,7 @@ public:
|
|||
|
||||
/* Handler for "fileno"". */
|
||||
|
||||
class kf_fileno : public known_function
|
||||
class kf_fileno : public pure_known_function_with_default_return
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
|
|
@ -557,6 +557,7 @@ public:
|
|||
const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
|
||||
model->set_value (base_reg, new_sval, cd.get_ctxt ());
|
||||
}
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -592,12 +593,13 @@ public:
|
|||
const svalue *new_sval = cd.get_or_create_conjured_svalue (base_reg);
|
||||
model->set_value (base_reg, new_sval, cd.get_ctxt ());
|
||||
}
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
}
|
||||
};
|
||||
|
||||
/* Handler for "getc"". */
|
||||
|
||||
class kf_getc : public known_function
|
||||
class kf_getc : public pure_known_function_with_default_return
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
|
|
@ -605,13 +607,11 @@ public:
|
|||
return (cd.num_args () == 1
|
||||
&& cd.arg_is_pointer_p (0));
|
||||
}
|
||||
|
||||
/* No side effects. */
|
||||
};
|
||||
|
||||
/* Handler for "getchar"". */
|
||||
|
||||
class kf_getchar : public known_function
|
||||
class kf_getchar : public pure_known_function_with_default_return
|
||||
{
|
||||
public:
|
||||
bool matches_call_types_p (const call_details &cd) const final override
|
||||
|
|
|
|||
|
|
@ -434,6 +434,11 @@ public:
|
|||
const svalue *new_ptr_sval,
|
||||
const extrinsic_state &ext_state) const;
|
||||
|
||||
void transition_ptr_sval_non_null (region_model *model,
|
||||
sm_state_map *smap,
|
||||
const svalue *new_ptr_sval,
|
||||
const extrinsic_state &ext_state) const;
|
||||
|
||||
standard_deallocator_set m_free;
|
||||
standard_deallocator_set m_scalar_delete;
|
||||
standard_deallocator_set m_vector_delete;
|
||||
|
|
@ -754,7 +759,7 @@ public:
|
|||
override
|
||||
{
|
||||
if (change.m_old_state == m_sm.get_start_state ()
|
||||
&& unchecked_p (change.m_new_state))
|
||||
&& (unchecked_p (change.m_new_state) || nonnull_p (change.m_new_state)))
|
||||
// TODO: verify that it's the allocation stmt, not a copy
|
||||
return label_text::borrow ("allocated here");
|
||||
if (unchecked_p (change.m_old_state)
|
||||
|
|
@ -1174,6 +1179,21 @@ public:
|
|||
{
|
||||
return ev.formatted_print ("dereference of NULL %qE", ev.m_expr);
|
||||
}
|
||||
|
||||
/* Implementation of pending_diagnostic::supercedes_p for
|
||||
null-deref.
|
||||
|
||||
We want null-deref to supercede use-of-unitialized-value,
|
||||
so that if we have these at the same stmt, we don't emit
|
||||
a use-of-uninitialized, just the null-deref. */
|
||||
|
||||
bool supercedes_p (const pending_diagnostic &other) const final override
|
||||
{
|
||||
if (other.use_of_uninit_p ())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/* Concrete subclass for describing passing a NULL value to a
|
||||
|
|
@ -1910,12 +1930,20 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (is_named_call_p (callee_fndecl, "operator new", call, 1))
|
||||
on_allocator_call (sm_ctxt, call, &m_scalar_delete);
|
||||
else if (is_named_call_p (callee_fndecl, "operator new []", call, 1))
|
||||
on_allocator_call (sm_ctxt, call, &m_vector_delete);
|
||||
else if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
|
||||
|| is_named_call_p (callee_fndecl, "operator delete", call, 2))
|
||||
if (!is_placement_new_p (call))
|
||||
{
|
||||
bool returns_nonnull = !TREE_NOTHROW (callee_fndecl)
|
||||
&& flag_exceptions;
|
||||
if (is_named_call_p (callee_fndecl, "operator new"))
|
||||
on_allocator_call (sm_ctxt, call,
|
||||
&m_scalar_delete, returns_nonnull);
|
||||
else if (is_named_call_p (callee_fndecl, "operator new []"))
|
||||
on_allocator_call (sm_ctxt, call,
|
||||
&m_vector_delete, returns_nonnull);
|
||||
}
|
||||
|
||||
if (is_named_call_p (callee_fndecl, "operator delete", call, 1)
|
||||
|| is_named_call_p (callee_fndecl, "operator delete", call, 2))
|
||||
{
|
||||
on_deallocator_call (sm_ctxt, node, call,
|
||||
&m_scalar_delete.m_deallocator, 0);
|
||||
|
|
@ -1960,71 +1988,88 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
|
|||
malloc_state_machine *mutable_this
|
||||
= const_cast <malloc_state_machine *> (this);
|
||||
|
||||
/* Handle "__attribute__((malloc(FOO)))". */
|
||||
if (const deallocator_set *deallocators
|
||||
/* Handle interesting attributes of the callee_fndecl,
|
||||
or prioritize those of the builtin that callee_fndecl is expected
|
||||
to be.
|
||||
Might want this to be controlled by a flag. */
|
||||
{
|
||||
tree fndecl = callee_fndecl;
|
||||
/* If call is recognized as a builtin known_function, use that
|
||||
builtin's function_decl. */
|
||||
if (const region_model *old_model = sm_ctxt->get_old_region_model ())
|
||||
if (const builtin_known_function *builtin_kf
|
||||
= old_model->get_builtin_kf (call))
|
||||
fndecl = builtin_kf->builtin_decl ();
|
||||
|
||||
/* Handle "__attribute__((malloc(FOO)))". */
|
||||
if (const deallocator_set *deallocators
|
||||
= mutable_this->get_or_create_custom_deallocator_set
|
||||
(callee_fndecl))
|
||||
(fndecl))
|
||||
{
|
||||
tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (fndecl));
|
||||
bool returns_nonnull
|
||||
= lookup_attribute ("returns_nonnull", attrs);
|
||||
on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull);
|
||||
}
|
||||
|
||||
{
|
||||
tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl));
|
||||
bool returns_nonnull
|
||||
= lookup_attribute ("returns_nonnull", attrs);
|
||||
on_allocator_call (sm_ctxt, call, deallocators, returns_nonnull);
|
||||
/* Handle "__attribute__((nonnull))". */
|
||||
tree fntype = TREE_TYPE (fndecl);
|
||||
bitmap nonnull_args = get_nonnull_args (fntype);
|
||||
if (nonnull_args)
|
||||
{
|
||||
for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
|
||||
{
|
||||
tree arg = gimple_call_arg (stmt, i);
|
||||
if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
|
||||
continue;
|
||||
/* If we have a nonnull-args, and either all pointers, or
|
||||
just the specified pointers. */
|
||||
if (bitmap_empty_p (nonnull_args)
|
||||
|| bitmap_bit_p (nonnull_args, i))
|
||||
{
|
||||
state_t state = sm_ctxt->get_state (stmt, arg);
|
||||
/* Can't use a switch as the states are non-const. */
|
||||
/* Do use the fndecl that caused the warning so that the
|
||||
misused attributes are printed and the user not
|
||||
confused. */
|
||||
if (unchecked_p (state))
|
||||
{
|
||||
tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
|
||||
sm_ctxt->warn (node, stmt, arg,
|
||||
make_unique<possible_null_arg>
|
||||
(*this, diag_arg, fndecl, i));
|
||||
const allocation_state *astate
|
||||
= as_a_allocation_state (state);
|
||||
sm_ctxt->set_next_state (stmt, arg,
|
||||
astate->get_nonnull ());
|
||||
}
|
||||
else if (state == m_null)
|
||||
{
|
||||
tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
|
||||
sm_ctxt->warn (node, stmt, arg,
|
||||
make_unique<null_arg>
|
||||
(*this, diag_arg, fndecl, i));
|
||||
sm_ctxt->set_next_state (stmt, arg, m_stop);
|
||||
}
|
||||
else if (state == m_start)
|
||||
maybe_assume_non_null (sm_ctxt, arg, stmt);
|
||||
}
|
||||
}
|
||||
BITMAP_FREE (nonnull_args);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle "__attribute__((nonnull))". */
|
||||
{
|
||||
tree fntype = TREE_TYPE (callee_fndecl);
|
||||
bitmap nonnull_args = get_nonnull_args (fntype);
|
||||
if (nonnull_args)
|
||||
/* Check for this after nonnull, so that if we have both
|
||||
then we transition to "freed", rather than "checked". */
|
||||
unsigned dealloc_argno = fndecl_dealloc_argno (fndecl);
|
||||
if (dealloc_argno != UINT_MAX)
|
||||
{
|
||||
for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
|
||||
{
|
||||
tree arg = gimple_call_arg (stmt, i);
|
||||
if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
|
||||
continue;
|
||||
/* If we have a nonnull-args, and either all pointers, or just
|
||||
the specified pointers. */
|
||||
if (bitmap_empty_p (nonnull_args)
|
||||
|| bitmap_bit_p (nonnull_args, i))
|
||||
{
|
||||
state_t state = sm_ctxt->get_state (stmt, arg);
|
||||
/* Can't use a switch as the states are non-const. */
|
||||
if (unchecked_p (state))
|
||||
{
|
||||
tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
|
||||
sm_ctxt->warn (node, stmt, arg,
|
||||
make_unique<possible_null_arg>
|
||||
(*this, diag_arg, callee_fndecl, i));
|
||||
const allocation_state *astate
|
||||
= as_a_allocation_state (state);
|
||||
sm_ctxt->set_next_state (stmt, arg,
|
||||
astate->get_nonnull ());
|
||||
}
|
||||
else if (state == m_null)
|
||||
{
|
||||
tree diag_arg = sm_ctxt->get_diagnostic_tree (arg);
|
||||
sm_ctxt->warn (node, stmt, arg,
|
||||
make_unique<null_arg>
|
||||
(*this, diag_arg, callee_fndecl, i));
|
||||
sm_ctxt->set_next_state (stmt, arg, m_stop);
|
||||
}
|
||||
else if (state == m_start)
|
||||
maybe_assume_non_null (sm_ctxt, arg, stmt);
|
||||
}
|
||||
}
|
||||
BITMAP_FREE (nonnull_args);
|
||||
const deallocator *d
|
||||
= mutable_this->get_or_create_deallocator (fndecl);
|
||||
on_deallocator_call (sm_ctxt, node, call, d, dealloc_argno);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for this after nonnull, so that if we have both
|
||||
then we transition to "freed", rather than "checked". */
|
||||
unsigned dealloc_argno = fndecl_dealloc_argno (callee_fndecl);
|
||||
if (dealloc_argno != UINT_MAX)
|
||||
{
|
||||
const deallocator *d
|
||||
= mutable_this->get_or_create_deallocator (callee_fndecl);
|
||||
on_deallocator_call (sm_ctxt, node, call, d, dealloc_argno);
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for pointers explicitly being compared against zero
|
||||
|
|
@ -2504,6 +2549,17 @@ on_realloc_with_move (region_model *model,
|
|||
NULL, ext_state);
|
||||
}
|
||||
|
||||
/* Hook for get_or_create_region_for_heap_alloc for the case when we want
|
||||
ptr_sval to mark a newly created region as assumed non null on malloc SM. */
|
||||
void
|
||||
malloc_state_machine::transition_ptr_sval_non_null (region_model *model,
|
||||
sm_state_map *smap,
|
||||
const svalue *new_ptr_sval,
|
||||
const extrinsic_state &ext_state) const
|
||||
{
|
||||
smap->set_state (model, new_ptr_sval, m_free.m_nonnull, NULL, ext_state);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
/* Internal interface to this file. */
|
||||
|
|
@ -2548,6 +2604,32 @@ region_model::on_realloc_with_move (const call_details &cd,
|
|||
*ext_state);
|
||||
}
|
||||
|
||||
/* Moves ptr_sval from start to assumed non-null, for use by
|
||||
region_model::get_or_create_region_for_heap_alloc. */
|
||||
void
|
||||
region_model::transition_ptr_sval_non_null (region_model_context *ctxt,
|
||||
const svalue *ptr_sval)
|
||||
{
|
||||
if (!ctxt)
|
||||
return;
|
||||
const extrinsic_state *ext_state = ctxt->get_ext_state ();
|
||||
if (!ext_state)
|
||||
return;
|
||||
|
||||
sm_state_map *smap;
|
||||
const state_machine *sm;
|
||||
unsigned sm_idx;
|
||||
if (!ctxt->get_malloc_map (&smap, &sm, &sm_idx))
|
||||
return;
|
||||
|
||||
gcc_assert (smap);
|
||||
gcc_assert (sm);
|
||||
|
||||
const malloc_state_machine &malloc_sm = (const malloc_state_machine &)*sm;
|
||||
|
||||
malloc_sm.transition_ptr_sval_non_null (this, smap, ptr_sval, *ext_state);
|
||||
}
|
||||
|
||||
} // namespace ana
|
||||
|
||||
#endif /* #if ENABLE_ANALYZER */
|
||||
|
|
|
|||
|
|
@ -538,6 +538,15 @@ concrete_binding::overlaps_p (const concrete_binding &other) const
|
|||
return false;
|
||||
}
|
||||
|
||||
/* If this is expressible as a concrete byte range, return true
|
||||
and write it to *OUT. Otherwise return false. */
|
||||
|
||||
bool
|
||||
concrete_binding::get_byte_range (byte_range *out) const
|
||||
{
|
||||
return m_bit_range.as_byte_range (out);
|
||||
}
|
||||
|
||||
/* Comparator for use by vec<const concrete_binding *>::qsort. */
|
||||
|
||||
int
|
||||
|
|
|
|||
|
|
@ -399,6 +399,7 @@ public:
|
|||
{ return this; }
|
||||
|
||||
const bit_range &get_bit_range () const { return m_bit_range; }
|
||||
bool get_byte_range (byte_range *out) const;
|
||||
|
||||
bit_offset_t get_start_bit_offset () const
|
||||
{
|
||||
|
|
@ -855,6 +856,12 @@ public:
|
|||
return get_concrete_binding (bits.get_start_bit_offset (),
|
||||
bits.m_size_in_bits);
|
||||
}
|
||||
const concrete_binding *
|
||||
get_concrete_binding (const byte_range &bytes)
|
||||
{
|
||||
bit_range bits = bytes.as_bit_range ();
|
||||
return get_concrete_binding (bits);
|
||||
}
|
||||
const symbolic_binding *
|
||||
get_symbolic_binding (const region *region);
|
||||
|
||||
|
|
|
|||
|
|
@ -970,6 +970,8 @@ poison_kind_to_str (enum poison_kind kind)
|
|||
return "uninit";
|
||||
case POISON_KIND_FREED:
|
||||
return "freed";
|
||||
case POISON_KIND_DELETED:
|
||||
return "deleted";
|
||||
case POISON_KIND_POPPED_STACK:
|
||||
return "popped stack";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,6 +350,9 @@ enum poison_kind
|
|||
/* For use to describe freed memory. */
|
||||
POISON_KIND_FREED,
|
||||
|
||||
/* For use to describe deleted memory. */
|
||||
POISON_KIND_DELETED,
|
||||
|
||||
/* For use on pointers to regions within popped stack frames. */
|
||||
POISON_KIND_POPPED_STACK
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1007,6 +1007,8 @@ kf_va_arg::impl_call_pre (const call_details &cd) const
|
|||
tree va_list_tree = get_va_list_diag_arg (cd.get_arg_tree (0));
|
||||
ap_sval = model->check_for_poison (ap_sval, va_list_tree, ap_reg, ctxt);
|
||||
|
||||
cd.set_any_lhs_with_defaults ();
|
||||
|
||||
if (const region *impl_reg = ap_sval->maybe_get_region ())
|
||||
{
|
||||
const svalue *old_impl_sval = model->get_store_value (impl_reg, ctxt);
|
||||
|
|
|
|||
|
|
@ -1578,6 +1578,7 @@ afdo_annotate_cfg (const stmt_set &promoted_stmts)
|
|||
}
|
||||
update_max_bb_count ();
|
||||
profile_status_for_fn (cfun) = PROFILE_READ;
|
||||
cfun->cfg->full_profile = true;
|
||||
if (flag_value_profile_transformations)
|
||||
{
|
||||
gimple_value_profile_transformations ();
|
||||
|
|
|
|||
|
|
@ -1,3 +1,62 @@
|
|||
2023-09-01 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR c++/111069
|
||||
* c-opts.cc (c_common_post_options): Change latest_abi_version to 19.
|
||||
|
||||
2023-08-31 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
|
||||
|
||||
* c.opt: Change spelling to macOS.
|
||||
|
||||
2023-08-31 Richard Biener <rguenther@suse.de>
|
||||
|
||||
PR middle-end/111253
|
||||
* c-pretty-print.cc (c_pretty_printer::primary_expression):
|
||||
Only dump gimple_assign_single_p SSA def RHS.
|
||||
|
||||
2023-08-25 Sandra Loosemore <sandra@codesourcery.com>
|
||||
|
||||
* c-common.h (c_omp_check_loop_binding_exprs): Declare.
|
||||
* c-omp.cc: Include tree-iterator.h.
|
||||
(find_binding_in_body): New.
|
||||
(check_loop_binding_expr_r): New.
|
||||
(LOCATION_OR): New.
|
||||
(check_looop_binding_expr): New.
|
||||
(c_omp_check_loop_binding_exprs): New.
|
||||
|
||||
2023-08-25 Uros Bizjak <ubizjak@gmail.com>
|
||||
|
||||
* c-format.cc (read_any_format_width):
|
||||
Rename TRUE/FALSE to true/false.
|
||||
|
||||
2023-08-20 Martin Uecker <uecker@tugraz.at>
|
||||
|
||||
* c-format.cc: Fix identation.
|
||||
|
||||
2023-08-20 Tomas Kalibera <tomas.kalibera@gmail.com>
|
||||
|
||||
PR c/95130
|
||||
* c-format.cc: skip default format for printf symbol if
|
||||
explicitly declared by prototype.
|
||||
|
||||
2023-08-17 Jose E. Marchesi <jose.marchesi@oracle.com>
|
||||
|
||||
PR c/106537
|
||||
* c.opt (Wcompare-distinct-pointer-types): New option.
|
||||
|
||||
2023-08-14 Jason Merrill <jason@redhat.com>
|
||||
|
||||
* c-cppbuiltin.cc (c_cpp_builtins): Adjust __cpp_concepts.
|
||||
|
||||
2023-08-11 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
* c-common.cc (c_common_reswords): Add __typeof_unqual
|
||||
and __typeof_unqual__ spellings of typeof_unqual.
|
||||
|
||||
2023-08-11 Martin Uecker <uecker@tugraz.at>
|
||||
|
||||
PR c/84510
|
||||
* c.opt: Enable warning for C and ObjC.
|
||||
|
||||
2023-08-05 Martin Uecker <uecker@tugraz.at>
|
||||
|
||||
PR c/98536
|
||||
|
|
|
|||
|
|
@ -420,6 +420,8 @@ const struct c_common_resword c_common_reswords[] =
|
|||
{ "__transaction_cancel", RID_TRANSACTION_CANCEL, 0 },
|
||||
{ "__typeof", RID_TYPEOF, 0 },
|
||||
{ "__typeof__", RID_TYPEOF, 0 },
|
||||
{ "__typeof_unqual", RID_TYPEOF_UNQUAL, D_CONLY },
|
||||
{ "__typeof_unqual__", RID_TYPEOF_UNQUAL, D_CONLY },
|
||||
{ "__volatile", RID_VOLATILE, 0 },
|
||||
{ "__volatile__", RID_VOLATILE, 0 },
|
||||
{ "__GIMPLE", RID_GIMPLE, D_CONLY },
|
||||
|
|
|
|||
|
|
@ -1299,6 +1299,7 @@ extern tree c_finish_omp_for (location_t, enum tree_code, tree, tree, tree,
|
|||
extern bool c_omp_check_loop_iv (tree, tree, walk_tree_lh);
|
||||
extern bool c_omp_check_loop_iv_exprs (location_t, enum tree_code, tree, int,
|
||||
tree, tree, tree, walk_tree_lh);
|
||||
extern bool c_omp_check_loop_binding_exprs (tree, vec<tree> *);
|
||||
extern tree c_finish_oacc_wait (location_t, tree, tree);
|
||||
extern tree c_oacc_split_loop_clauses (tree, tree *, bool);
|
||||
extern void c_omp_split_clauses (location_t, enum tree_code, omp_clause_mask,
|
||||
|
|
|
|||
|
|
@ -1089,7 +1089,7 @@ c_cpp_builtins (cpp_reader *pfile)
|
|||
}
|
||||
if (flag_concepts)
|
||||
{
|
||||
if (cxx_dialect >= cxx20)
|
||||
if (cxx_dialect >= cxx20 || !flag_concepts_ts)
|
||||
cpp_define (pfile, "__cpp_concepts=202002L");
|
||||
else
|
||||
cpp_define (pfile, "__cpp_concepts=201507L");
|
||||
|
|
|
|||
|
|
@ -1175,6 +1175,7 @@ check_function_format (const_tree fn, tree attrs, int nargs,
|
|||
tree a;
|
||||
|
||||
tree atname = get_identifier ("format");
|
||||
bool skipped_default_format = false;
|
||||
|
||||
/* See if this function has any format attributes. */
|
||||
for (a = attrs; a; a = TREE_CHAIN (a))
|
||||
|
|
@ -1185,6 +1186,38 @@ check_function_format (const_tree fn, tree attrs, int nargs,
|
|||
function_format_info info;
|
||||
decode_format_attr (fn, atname, TREE_VALUE (a), &info,
|
||||
/*validated=*/true);
|
||||
|
||||
/* Mingw32 targets have traditionally used ms_printf format for the
|
||||
printf function, and this format is built in GCC. But nowadays,
|
||||
if mingw-w64 is configured to target UCRT, the printf function
|
||||
uses the gnu_printf format (specified in the stdio.h header). This
|
||||
causes GCC to check both formats, which means that GCC would
|
||||
warn twice about the same issue when both formats are violated,
|
||||
e.g. for %lu used to print long long unsigned.
|
||||
|
||||
Hence, if there is a built-in attribute specifier and at least
|
||||
one another, we skip the built-in one. See PR 95130 (but note that
|
||||
GCC ms_printf already supports %llu) and PR 92292. */
|
||||
|
||||
if (!skipped_default_format
|
||||
&& fn
|
||||
&& TREE_CODE (fn) == FUNCTION_DECL
|
||||
&& fndecl_built_in_p (fn, BUILT_IN_NORMAL)
|
||||
&& (tree_to_uhwi (TREE_PURPOSE (TREE_VALUE (a)))
|
||||
& (int) ATTR_FLAG_BUILT_IN))
|
||||
{
|
||||
tree aa;
|
||||
for (aa = attrs; aa; aa = TREE_CHAIN (aa))
|
||||
if (a != aa
|
||||
&& is_attribute_p ("format", get_attribute_name (aa)))
|
||||
{
|
||||
skipped_default_format = true;
|
||||
break;
|
||||
}
|
||||
if (skipped_default_format)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (warn_format)
|
||||
{
|
||||
/* FIXME: Rewrite all the internal functions in this file
|
||||
|
|
@ -2292,13 +2325,13 @@ read_any_format_width (tree ¶ms,
|
|||
{
|
||||
/* Possibly read a numeric width. If the width is zero,
|
||||
we complain if appropriate. */
|
||||
int non_zero_width_char = FALSE;
|
||||
int found_width = FALSE;
|
||||
int non_zero_width_char = false;
|
||||
int found_width = false;
|
||||
while (ISDIGIT (*format_chars))
|
||||
{
|
||||
found_width = TRUE;
|
||||
found_width = true;
|
||||
if (*format_chars != '0')
|
||||
non_zero_width_char = TRUE;
|
||||
non_zero_width_char = true;
|
||||
++format_chars;
|
||||
}
|
||||
if (found_width && !non_zero_width_char &&
|
||||
|
|
@ -5190,6 +5223,9 @@ handle_format_attribute (tree node[3], tree atname, tree args,
|
|||
if (TREE_CODE (TREE_VALUE (args)) == IDENTIFIER_NODE)
|
||||
TREE_VALUE (args) = canonicalize_attr_name (TREE_VALUE (args));
|
||||
|
||||
/* record the flags for check_function_format */
|
||||
TREE_PURPOSE (args) = build_int_cst (unsigned_type_node, flags);
|
||||
|
||||
if (!decode_format_attr (fndecl ? fndecl : type, atname, args, &info,
|
||||
/* validated_p = */false))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "gimplify.h"
|
||||
#include "langhooks.h"
|
||||
#include "bitmap.h"
|
||||
#include "tree-iterator.h"
|
||||
|
||||
|
||||
/* Complete a #pragma oacc wait construct. LOC is the location of
|
||||
|
|
@ -1728,6 +1729,156 @@ c_omp_check_loop_iv_exprs (location_t stmt_loc, enum tree_code code,
|
|||
return !data.fail;
|
||||
}
|
||||
|
||||
|
||||
/* Helper function for c_omp_check_loop_binding_exprs: look for a binding
|
||||
of DECL in BODY. Only traverse things that might be containers for
|
||||
intervening code in an OMP loop. Returns the BIND_EXPR or DECL_EXPR
|
||||
if found, otherwise null. */
|
||||
|
||||
static tree
|
||||
find_binding_in_body (tree decl, tree body)
|
||||
{
|
||||
if (!body)
|
||||
return NULL_TREE;
|
||||
|
||||
switch (TREE_CODE (body))
|
||||
{
|
||||
case BIND_EXPR:
|
||||
for (tree b = BIND_EXPR_VARS (body); b; b = DECL_CHAIN (b))
|
||||
if (b == decl)
|
||||
return body;
|
||||
return find_binding_in_body (decl, BIND_EXPR_BODY (body));
|
||||
|
||||
case DECL_EXPR:
|
||||
if (DECL_EXPR_DECL (body) == decl)
|
||||
return body;
|
||||
return NULL_TREE;
|
||||
|
||||
case STATEMENT_LIST:
|
||||
for (tree_stmt_iterator si = tsi_start (body); !tsi_end_p (si);
|
||||
tsi_next (&si))
|
||||
{
|
||||
tree b = find_binding_in_body (decl, tsi_stmt (si));
|
||||
if (b)
|
||||
return b;
|
||||
}
|
||||
return NULL_TREE;
|
||||
|
||||
case OMP_STRUCTURED_BLOCK:
|
||||
return find_binding_in_body (decl, OMP_BODY (body));
|
||||
|
||||
default:
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Traversal function for check_loop_binding_expr, to diagnose
|
||||
errors when a binding made in intervening code is referenced outside
|
||||
of the loop. Returns non-null if such a reference is found. DATA points
|
||||
to the tree containing the loop body. */
|
||||
|
||||
static tree
|
||||
check_loop_binding_expr_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
|
||||
void *data)
|
||||
{
|
||||
tree body = *(tree *)data;
|
||||
|
||||
if (DECL_P (*tp) && find_binding_in_body (*tp, body))
|
||||
return *tp;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Helper macro used below. */
|
||||
|
||||
#define LOCATION_OR(loc1, loc2) \
|
||||
((loc1) != UNKNOWN_LOCATION ? (loc1) : (loc2))
|
||||
|
||||
/* Check a single expression EXPR for references to variables bound in
|
||||
intervening code in BODY. Return true if ok, otherwise give an error
|
||||
referencing CONTEXT and return false. Use LOC for the error message
|
||||
if EXPR doesn't have one. */
|
||||
static bool
|
||||
check_loop_binding_expr (tree expr, tree body, const char *context,
|
||||
location_t loc)
|
||||
{
|
||||
tree bad = walk_tree (&expr, check_loop_binding_expr_r, (void *)&body, NULL);
|
||||
|
||||
if (bad)
|
||||
{
|
||||
location_t eloc = EXPR_LOCATION (expr);
|
||||
error_at (LOCATION_OR (eloc, loc),
|
||||
"variable %qD used %s is bound "
|
||||
"in intervening code", bad, context);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* STMT is an OMP_FOR construct. Check all of the iteration variable,
|
||||
initializer, end condition, and increment for bindings inside the
|
||||
loop body. If ORIG_INITS is provided, check those elements too.
|
||||
Return true if OK, false otherwise. */
|
||||
bool
|
||||
c_omp_check_loop_binding_exprs (tree stmt, vec<tree> *orig_inits)
|
||||
{
|
||||
bool ok = true;
|
||||
location_t loc = EXPR_LOCATION (stmt);
|
||||
tree body = OMP_FOR_BODY (stmt);
|
||||
int orig_init_length = orig_inits ? orig_inits->length () : 0;
|
||||
|
||||
for (int i = 1; i < TREE_VEC_LENGTH (OMP_FOR_INIT (stmt)); i++)
|
||||
{
|
||||
tree init = TREE_VEC_ELT (OMP_FOR_INIT (stmt), i);
|
||||
tree cond = TREE_VEC_ELT (OMP_FOR_COND (stmt), i);
|
||||
tree incr = TREE_VEC_ELT (OMP_FOR_INCR (stmt), i);
|
||||
gcc_assert (TREE_CODE (init) == MODIFY_EXPR);
|
||||
tree decl = TREE_OPERAND (init, 0);
|
||||
tree orig_init = i < orig_init_length ? (*orig_inits)[i] : NULL_TREE;
|
||||
tree e;
|
||||
location_t eloc;
|
||||
|
||||
e = TREE_OPERAND (init, 1);
|
||||
eloc = LOCATION_OR (EXPR_LOCATION (init), loc);
|
||||
if (!check_loop_binding_expr (decl, body, "as loop variable", eloc))
|
||||
ok = false;
|
||||
if (!check_loop_binding_expr (e, body, "in initializer", eloc))
|
||||
ok = false;
|
||||
if (orig_init
|
||||
&& !check_loop_binding_expr (orig_init, body,
|
||||
"in initializer", eloc))
|
||||
ok = false;
|
||||
|
||||
/* INCR and/or COND may be null if this is a template with a
|
||||
class iterator. */
|
||||
if (cond)
|
||||
{
|
||||
eloc = LOCATION_OR (EXPR_LOCATION (cond), loc);
|
||||
if (COMPARISON_CLASS_P (cond) && TREE_OPERAND (cond, 0) == decl)
|
||||
e = TREE_OPERAND (cond, 1);
|
||||
else if (COMPARISON_CLASS_P (cond) && TREE_OPERAND (cond, 1) == decl)
|
||||
e = TREE_OPERAND (cond, 0);
|
||||
else
|
||||
e = cond;
|
||||
if (!check_loop_binding_expr (e, body, "in end test", eloc))
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if (incr)
|
||||
{
|
||||
eloc = LOCATION_OR (EXPR_LOCATION (incr), loc);
|
||||
/* INCR should be either a MODIFY_EXPR or pre/post
|
||||
increment/decrement. We don't have to check the latter
|
||||
since there are no operands besides the iteration variable. */
|
||||
if (TREE_CODE (incr) == MODIFY_EXPR
|
||||
&& !check_loop_binding_expr (TREE_OPERAND (incr, 1), body,
|
||||
"in increment expression", eloc))
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/* This function splits clauses for OpenACC combined loop
|
||||
constructs. OpenACC combined loop constructs are:
|
||||
#pragma acc kernels loop
|
||||
|
|
|
|||
|
|
@ -974,7 +974,7 @@ c_common_post_options (const char **pfilename)
|
|||
|
||||
/* Change flag_abi_version to be the actual current ABI level, for the
|
||||
benefit of c_cpp_builtins, and to make comparison simpler. */
|
||||
const int latest_abi_version = 18;
|
||||
const int latest_abi_version = 19;
|
||||
/* Generate compatibility aliases for ABI v13 (8.2) by default. */
|
||||
const int abi_compat_default = 13;
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "langhooks.h"
|
||||
#include "options.h"
|
||||
#include "internal-fn.h"
|
||||
#include "function.h"
|
||||
#include "basic-block.h"
|
||||
#include "gimple.h"
|
||||
|
||||
/* The pretty-printer code is primarily designed to closely follow
|
||||
(GNU) C and C++ grammars. That is to be contrasted with spaghetti
|
||||
|
|
@ -1380,12 +1383,14 @@ c_pretty_printer::primary_expression (tree e)
|
|||
else
|
||||
primary_expression (var);
|
||||
}
|
||||
else
|
||||
else if (gimple_assign_single_p (SSA_NAME_DEF_STMT (e)))
|
||||
{
|
||||
/* Print only the right side of the GIMPLE assignment. */
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (e);
|
||||
pp_gimple_stmt_1 (this, def_stmt, 0, TDF_RHS_ONLY);
|
||||
}
|
||||
else
|
||||
expression (e);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1490,7 +1490,7 @@ C++ ObjC++ Var(warn_zero_as_null_pointer_constant) Warning
|
|||
Warn when a literal '0' is used as null pointer.
|
||||
|
||||
Wuseless-cast
|
||||
C++ ObjC++ Var(warn_useless_cast) Warning
|
||||
C ObjC C++ ObjC++ Var(warn_useless_cast) Warning
|
||||
Warn about useless casts.
|
||||
|
||||
Wsubobject-linkage
|
||||
|
|
@ -1935,6 +1935,10 @@ Winvalid-imported-macros
|
|||
C++ ObjC++ Var(warn_imported_macros) Warning
|
||||
Warn about macros that have conflicting header units definitions.
|
||||
|
||||
Wcompare-distinct-pointer-types
|
||||
C ObjC Var(warn_compare_distinct_pointer_types) Warning Init(1)
|
||||
Warn if pointers of distinct types are compared without a cast.
|
||||
|
||||
flang-info-include-translate
|
||||
C++ Var(note_include_translate_yes)
|
||||
Note #include directives translated to import declarations.
|
||||
|
|
@ -1984,7 +1988,7 @@ Implement resolution of DR 150 for matching of template template arguments.
|
|||
|
||||
fnext-runtime
|
||||
ObjC ObjC++ LTO RejectNegative Var(flag_next_runtime)
|
||||
Generate code for NeXT (Apple Mac OS X) runtime environment.
|
||||
Generate code for NeXT (Apple macOS) runtime environment.
|
||||
|
||||
fnil-receivers
|
||||
ObjC ObjC++ Var(flag_nil_receivers) Init(1)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,57 @@
|
|||
2023-08-25 Sandra Loosemore <sandra@codesourcery.com>
|
||||
|
||||
* c-parser.cc (struct c_parser): Add omp_for_parse_state field.
|
||||
(struct omp_for_parse_data): New.
|
||||
(check_omp_intervening_code): New.
|
||||
(add_structured_block_stmt): New.
|
||||
(c_parser_compound_statement_nostart): Recognize intervening code,
|
||||
nested loops, and other things that need special handling in
|
||||
OpenMP loop constructs.
|
||||
(c_parser_while_statement): Error on loop in intervening code.
|
||||
(c_parser_do_statement): Likewise.
|
||||
(c_parser_for_statement): Likewise.
|
||||
(c_parser_postfix_expression_after_primary): Error on calls to
|
||||
the OpenMP runtime in intervening code.
|
||||
(c_parser_pragma): Error on OpenMP pragmas in intervening code.
|
||||
(c_parser_omp_loop_nest): New.
|
||||
(c_parser_omp_for_loop): Rewrite to use recursive descent, calling
|
||||
c_parser_omp_loop_nest to do the heavy lifting.
|
||||
|
||||
2023-08-24 Richard Sandiford <richard.sandiford@arm.com>
|
||||
|
||||
* c-parser.cc (c_parser_std_attribute): Conditionally allow
|
||||
two colons to be used in place of ::.
|
||||
(c_parser_std_attribute_list): New function, split out from...
|
||||
(c_parser_std_attribute_specifier): ...here. Allow the attribute-list
|
||||
to start with __extension__. When it does, also allow two colons
|
||||
to be used in place of ::.
|
||||
|
||||
2023-08-22 Tobias Burnus <tobias@codesourcery.com>
|
||||
|
||||
* c-parser.cc (c_parser_omp_clause_defaultmap): Parse
|
||||
'all' as category.
|
||||
|
||||
2023-08-17 Jose E. Marchesi <jose.marchesi@oracle.com>
|
||||
|
||||
PR c/106537
|
||||
* c-typeck.cc (build_binary_op): Warning on comparing distinct
|
||||
pointer types only when -Wcompare-distinct-pointer-types.
|
||||
|
||||
2023-08-15 Chung-Lin Tang <cltang@codesourcery.com>
|
||||
Thomas Schwinge <thomas@codesourcery.com>
|
||||
|
||||
* c-parser.cc (OACC_DATA_CLAUSE_MASK): Add PRAGMA_OACC_CLAUSE_DEFAULT.
|
||||
|
||||
2023-08-11 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
* c-parser.cc (c_parser_typeof_specifier): Handle
|
||||
__typeof_unqual and __typeof_unqual__ as !is_std.
|
||||
|
||||
2023-08-11 Martin Uecker <uecker@tugraz.at>
|
||||
|
||||
PR c/84510
|
||||
* c-typeck.cc (build_c_cast): Add warning.
|
||||
|
||||
2023-08-05 Martin Uecker <uecker@tugraz.at>
|
||||
|
||||
* c-parser.cc (c_parser_generic_selection): Inhibit evaluation
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -6062,9 +6062,13 @@ build_c_cast (location_t loc, tree type, tree expr)
|
|||
|
||||
if (type == TYPE_MAIN_VARIANT (TREE_TYPE (value)))
|
||||
{
|
||||
if (RECORD_OR_UNION_TYPE_P (type))
|
||||
pedwarn (loc, OPT_Wpedantic,
|
||||
"ISO C forbids casting nonscalar to the same type");
|
||||
if (RECORD_OR_UNION_TYPE_P (type)
|
||||
&& pedwarn (loc, OPT_Wpedantic,
|
||||
"ISO C forbids casting nonscalar to the same type"))
|
||||
;
|
||||
else if (warn_useless_cast)
|
||||
warning_at (loc, OPT_Wuseless_cast,
|
||||
"useless cast to type %qT", type);
|
||||
|
||||
/* Convert to remove any qualifiers from VALUE's type. */
|
||||
value = convert (type, value);
|
||||
|
|
@ -12768,7 +12772,7 @@ build_binary_op (location_t location, enum tree_code code,
|
|||
else
|
||||
/* Avoid warning about the volatile ObjC EH puts on decls. */
|
||||
if (!objc_ok)
|
||||
pedwarn (location, 0,
|
||||
pedwarn (location, OPT_Wcompare_distinct_pointer_types,
|
||||
"comparison of distinct pointer types lacks a cast");
|
||||
|
||||
if (result_type == NULL_TREE)
|
||||
|
|
@ -12908,8 +12912,8 @@ build_binary_op (location_t location, enum tree_code code,
|
|||
int qual = ENCODE_QUAL_ADDR_SPACE (as_common);
|
||||
result_type = build_pointer_type
|
||||
(build_qualified_type (void_type_node, qual));
|
||||
pedwarn (location, 0,
|
||||
"comparison of distinct pointer types lacks a cast");
|
||||
pedwarn (location, OPT_Wcompare_distinct_pointer_types,
|
||||
"comparison of distinct pointer types lacks a cast");
|
||||
}
|
||||
}
|
||||
else if (code0 == POINTER_TYPE && null_pointer_constant_p (orig_op1))
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ new_saved_hard_reg (int regno, int call_freq)
|
|||
saved_reg->num = saved_regs_num++;
|
||||
saved_reg->hard_regno = regno;
|
||||
saved_reg->call_freq = call_freq;
|
||||
saved_reg->first_p = FALSE;
|
||||
saved_reg->first_p = false;
|
||||
saved_reg->next = -1;
|
||||
}
|
||||
|
||||
|
|
@ -558,7 +558,7 @@ setup_save_areas (void)
|
|||
+ saved_reg2->num]
|
||||
= saved_reg_conflicts[saved_reg2->num * saved_regs_num
|
||||
+ saved_reg->num]
|
||||
= TRUE;
|
||||
= true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -608,7 +608,7 @@ setup_save_areas (void)
|
|||
}
|
||||
if (j == i)
|
||||
{
|
||||
saved_reg->first_p = TRUE;
|
||||
saved_reg->first_p = true;
|
||||
for (best_slot_num = -1, j = 0; j < prev_save_slots_num; j++)
|
||||
{
|
||||
slot = prev_save_slots[j];
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ init_flow (struct function *the_fun)
|
|||
= ENTRY_BLOCK_PTR_FOR_FN (the_fun);
|
||||
the_fun->cfg->edge_flags_allocated = EDGE_ALL_FLAGS;
|
||||
the_fun->cfg->bb_flags_allocated = BB_ALL_FLAGS;
|
||||
the_fun->cfg->full_profile = false;
|
||||
}
|
||||
|
||||
/* Helper function for remove_edge and free_cffg. Frees edge structure
|
||||
|
|
|
|||
|
|
@ -78,6 +78,9 @@ struct GTY(()) control_flow_graph {
|
|||
/* Dynamically allocated edge/bb flags. */
|
||||
int edge_flags_allocated;
|
||||
int bb_flags_allocated;
|
||||
|
||||
/* Set if the profile is computed on every edge and basic block. */
|
||||
bool full_profile;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1296,6 +1296,16 @@ duplicate_loop_body_to_header_edge (class loop *loop, edge e,
|
|||
}
|
||||
profile_probability prob_pass_wont_exit =
|
||||
new_count_le.probability_in (count_in);
|
||||
/* If profile count is 0, the probability will be uninitialized.
|
||||
We can set probability to any initialized value to avoid
|
||||
precision loss. If profile is sane, all counts will be 0 anyway. */
|
||||
if (!count_in.nonzero_p ())
|
||||
{
|
||||
prob_pass_thru
|
||||
= profile_probability::always ().apply_scale (1, 2);
|
||||
prob_pass_wont_exit
|
||||
= profile_probability::always ().apply_scale (1, 2);
|
||||
}
|
||||
|
||||
scale_step = XNEWVEC (profile_probability, ndupl);
|
||||
|
||||
|
|
@ -1306,7 +1316,9 @@ duplicate_loop_body_to_header_edge (class loop *loop, edge e,
|
|||
|
||||
/* Complete peeling is special as the probability of exit in last
|
||||
copy becomes 1. */
|
||||
if (flags & DLTHE_FLAG_COMPLETTE_PEEL)
|
||||
if (!count_in.nonzero_p ())
|
||||
;
|
||||
else if (flags & DLTHE_FLAG_COMPLETTE_PEEL)
|
||||
{
|
||||
profile_count wanted_count = e->count ();
|
||||
|
||||
|
|
|
|||
|
|
@ -1010,6 +1010,9 @@ Driver Undocumented
|
|||
; 18: Corrects errors in mangling of lambdas with additional context.
|
||||
; Default in G++ 13.
|
||||
;
|
||||
; 19: Emits ABI tags if needed in structured binding mangled names.
|
||||
; Default in G++ 14.
|
||||
;
|
||||
; Additional positive integers will be assigned as new versions of
|
||||
; the ABI become the default version of the ABI.
|
||||
fabi-version=
|
||||
|
|
|
|||
|
|
@ -533,10 +533,14 @@ get_intel_cpu (struct __processor_model *cpu_model,
|
|||
cpu_model->__cpu_type = INTEL_COREI7;
|
||||
cpu_model->__cpu_subtype = INTEL_COREI7_TIGERLAKE;
|
||||
break;
|
||||
|
||||
case 0xbe:
|
||||
/* Alder Lake N, E-core only. */
|
||||
case 0x97:
|
||||
case 0x9a:
|
||||
/* Alder Lake. */
|
||||
case 0xb7:
|
||||
case 0xba:
|
||||
case 0xbf:
|
||||
/* Raptor Lake. */
|
||||
case 0xaa:
|
||||
|
|
@ -762,7 +766,9 @@ get_available_features (struct __processor_model *cpu_model,
|
|||
/* Get Advanced Features at level 7 (eax = 7, ecx = 0/1). */
|
||||
if (max_cpuid_level >= 7)
|
||||
{
|
||||
__cpuid_count (7, 0, eax, ebx, ecx, edx);
|
||||
unsigned int max_subleaf_level;
|
||||
|
||||
__cpuid_count (7, 0, max_subleaf_level, ebx, ecx, edx);
|
||||
if (ebx & bit_BMI)
|
||||
set_feature (FEATURE_BMI);
|
||||
if (ebx & bit_SGX)
|
||||
|
|
@ -874,45 +880,48 @@ get_available_features (struct __processor_model *cpu_model,
|
|||
set_feature (FEATURE_AVX512FP16);
|
||||
}
|
||||
|
||||
__cpuid_count (7, 1, eax, ebx, ecx, edx);
|
||||
if (eax & bit_HRESET)
|
||||
set_feature (FEATURE_HRESET);
|
||||
if (eax & bit_CMPCCXADD)
|
||||
set_feature(FEATURE_CMPCCXADD);
|
||||
if (edx & bit_PREFETCHI)
|
||||
set_feature (FEATURE_PREFETCHI);
|
||||
if (eax & bit_RAOINT)
|
||||
set_feature (FEATURE_RAOINT);
|
||||
if (avx_usable)
|
||||
if (max_subleaf_level >= 1)
|
||||
{
|
||||
if (eax & bit_AVXVNNI)
|
||||
set_feature (FEATURE_AVXVNNI);
|
||||
if (eax & bit_AVXIFMA)
|
||||
set_feature (FEATURE_AVXIFMA);
|
||||
if (edx & bit_AVXVNNIINT8)
|
||||
set_feature (FEATURE_AVXVNNIINT8);
|
||||
if (edx & bit_AVXNECONVERT)
|
||||
set_feature (FEATURE_AVXNECONVERT);
|
||||
if (edx & bit_AVXVNNIINT16)
|
||||
set_feature (FEATURE_AVXVNNIINT16);
|
||||
if (eax & bit_SM3)
|
||||
set_feature (FEATURE_SM3);
|
||||
if (eax & bit_SHA512)
|
||||
set_feature (FEATURE_SHA512);
|
||||
if (eax & bit_SM4)
|
||||
set_feature (FEATURE_SM4);
|
||||
}
|
||||
if (avx512_usable)
|
||||
{
|
||||
if (eax & bit_AVX512BF16)
|
||||
set_feature (FEATURE_AVX512BF16);
|
||||
}
|
||||
if (amx_usable)
|
||||
{
|
||||
if (eax & bit_AMX_FP16)
|
||||
set_feature (FEATURE_AMX_FP16);
|
||||
if (edx & bit_AMX_COMPLEX)
|
||||
set_feature (FEATURE_AMX_COMPLEX);
|
||||
__cpuid_count (7, 1, eax, ebx, ecx, edx);
|
||||
if (eax & bit_HRESET)
|
||||
set_feature (FEATURE_HRESET);
|
||||
if (eax & bit_CMPCCXADD)
|
||||
set_feature(FEATURE_CMPCCXADD);
|
||||
if (edx & bit_PREFETCHI)
|
||||
set_feature (FEATURE_PREFETCHI);
|
||||
if (eax & bit_RAOINT)
|
||||
set_feature (FEATURE_RAOINT);
|
||||
if (avx_usable)
|
||||
{
|
||||
if (eax & bit_AVXVNNI)
|
||||
set_feature (FEATURE_AVXVNNI);
|
||||
if (eax & bit_AVXIFMA)
|
||||
set_feature (FEATURE_AVXIFMA);
|
||||
if (edx & bit_AVXVNNIINT8)
|
||||
set_feature (FEATURE_AVXVNNIINT8);
|
||||
if (edx & bit_AVXNECONVERT)
|
||||
set_feature (FEATURE_AVXNECONVERT);
|
||||
if (edx & bit_AVXVNNIINT16)
|
||||
set_feature (FEATURE_AVXVNNIINT16);
|
||||
if (eax & bit_SM3)
|
||||
set_feature (FEATURE_SM3);
|
||||
if (eax & bit_SHA512)
|
||||
set_feature (FEATURE_SHA512);
|
||||
if (eax & bit_SM4)
|
||||
set_feature (FEATURE_SM4);
|
||||
}
|
||||
if (avx512_usable)
|
||||
{
|
||||
if (eax & bit_AVX512BF16)
|
||||
set_feature (FEATURE_AVX512BF16);
|
||||
}
|
||||
if (amx_usable)
|
||||
{
|
||||
if (eax & bit_AMX_FP16)
|
||||
set_feature (FEATURE_AMX_FP16);
|
||||
if (edx & bit_AMX_COMPLEX)
|
||||
set_feature (FEATURE_AMX_COMPLEX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2044,7 +2044,9 @@ const char *const processor_names[] =
|
|||
"alderlake",
|
||||
"rocketlake",
|
||||
"graniterapids",
|
||||
"graniterapids-d",
|
||||
"arrowlake",
|
||||
"arrowlake-s",
|
||||
"intel",
|
||||
"lujiazui",
|
||||
"geode",
|
||||
|
|
@ -2168,13 +2170,14 @@ const pta processor_alias_table[] =
|
|||
M_CPU_SUBTYPE (INTEL_COREI7_ALDERLAKE), P_PROC_AVX2},
|
||||
{"graniterapids", PROCESSOR_GRANITERAPIDS, CPU_HASWELL, PTA_GRANITERAPIDS,
|
||||
M_CPU_SUBTYPE (INTEL_COREI7_GRANITERAPIDS), P_PROC_AVX512F},
|
||||
{"graniterapids-d", PROCESSOR_GRANITERAPIDS, CPU_HASWELL, PTA_GRANITERAPIDS_D,
|
||||
M_CPU_SUBTYPE (INTEL_COREI7_GRANITERAPIDS_D), P_PROC_AVX512F},
|
||||
{"graniterapids-d", PROCESSOR_GRANITERAPIDS_D, CPU_HASWELL,
|
||||
PTA_GRANITERAPIDS_D, M_CPU_SUBTYPE (INTEL_COREI7_GRANITERAPIDS_D),
|
||||
P_PROC_AVX512F},
|
||||
{"arrowlake", PROCESSOR_ARROWLAKE, CPU_HASWELL, PTA_ARROWLAKE,
|
||||
M_CPU_SUBTYPE (INTEL_COREI7_ARROWLAKE), P_PROC_AVX2},
|
||||
{"arrowlake-s", PROCESSOR_ARROWLAKE, CPU_HASWELL, PTA_ARROWLAKE_S,
|
||||
{"arrowlake-s", PROCESSOR_ARROWLAKE_S, CPU_HASWELL, PTA_ARROWLAKE_S,
|
||||
M_CPU_SUBTYPE (INTEL_COREI7_ARROWLAKE_S), P_PROC_AVX2},
|
||||
{"lunarlake", PROCESSOR_ARROWLAKE, CPU_HASWELL, PTA_ARROWLAKE_S,
|
||||
{"lunarlake", PROCESSOR_ARROWLAKE_S, CPU_HASWELL, PTA_ARROWLAKE_S,
|
||||
M_CPU_SUBTYPE (INTEL_COREI7_ARROWLAKE_S), P_PROC_AVX2},
|
||||
{"bonnell", PROCESSOR_BONNELL, CPU_ATOM, PTA_BONNELL,
|
||||
M_CPU_TYPE (INTEL_BONNELL), P_PROC_SSSE3},
|
||||
|
|
@ -2190,6 +2193,8 @@ const pta processor_alias_table[] =
|
|||
M_CPU_TYPE (INTEL_GOLDMONT_PLUS), P_PROC_SSE4_2},
|
||||
{"tremont", PROCESSOR_TREMONT, CPU_HASWELL, PTA_TREMONT,
|
||||
M_CPU_TYPE (INTEL_TREMONT), P_PROC_SSE4_2},
|
||||
{"gracemont", PROCESSOR_ALDERLAKE, CPU_HASWELL, PTA_ALDERLAKE,
|
||||
M_CPU_SUBTYPE (INTEL_COREI7_ALDERLAKE), P_PROC_AVX2},
|
||||
{"sierraforest", PROCESSOR_SIERRAFOREST, CPU_HASWELL, PTA_SIERRAFOREST,
|
||||
M_CPU_SUBTYPE (INTEL_SIERRAFOREST), P_PROC_AVX2},
|
||||
{"grandridge", PROCESSOR_GRANDRIDGE, CPU_HASWELL, PTA_GRANDRIDGE,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ static const struct default_options loongarch_option_optimization_table[] =
|
|||
{
|
||||
{ OPT_LEVELS_ALL, OPT_fasynchronous_unwind_tables, NULL, 1 },
|
||||
{ OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 },
|
||||
{ OPT_LEVELS_2_PLUS, OPT_free, NULL, 1 },
|
||||
{ OPT_LEVELS_NONE, 0, NULL, 0 }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -71,6 +71,8 @@ static const riscv_implied_info_t riscv_implied_info[] =
|
|||
{"zks", "zksed"},
|
||||
{"zks", "zksh"},
|
||||
|
||||
{"ztso", "a"},
|
||||
|
||||
{"v", "zvl128b"},
|
||||
{"v", "zve64d"},
|
||||
|
||||
|
|
@ -121,6 +123,9 @@ static const riscv_implied_info_t riscv_implied_info[] =
|
|||
|
||||
{"zfh", "zfhmin"},
|
||||
{"zfhmin", "f"},
|
||||
|
||||
{"zfa", "f"},
|
||||
|
||||
{"zvfhmin", "zve32f"},
|
||||
{"zvfh", "zve32f"},
|
||||
{"zvfh", "zfhmin"},
|
||||
|
|
@ -128,6 +133,25 @@ static const riscv_implied_info_t riscv_implied_info[] =
|
|||
{"zhinx", "zhinxmin"},
|
||||
{"zhinxmin", "zfinx"},
|
||||
|
||||
{"zce", "zca"},
|
||||
{"zce", "zcb"},
|
||||
{"zce", "zcmp"},
|
||||
{"zce", "zcmt"},
|
||||
{"zcf", "zca"},
|
||||
{"zcd", "zca"},
|
||||
{"zcb", "zca"},
|
||||
{"zcmp", "zca"},
|
||||
{"zcmt", "zca"},
|
||||
{"zcmt", "zicsr"},
|
||||
|
||||
{"smaia", "ssaia"},
|
||||
{"smstateen", "ssstateen"},
|
||||
{"smepmp", "zicsr"},
|
||||
{"ssaia", "zicsr"},
|
||||
{"sscofpmf", "zicsr"},
|
||||
{"ssstateen", "zicsr"},
|
||||
{"sstc", "zicsr"},
|
||||
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
@ -209,6 +233,7 @@ static const struct riscv_ext_version riscv_ext_version_table[] =
|
|||
{"zkt", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"zihintntl", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zihintpause", ISA_SPEC_CLASS_NONE, 2, 0},
|
||||
|
||||
{"zicboz",ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zicbom",ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
|
@ -218,9 +243,10 @@ static const struct riscv_ext_version riscv_ext_version_table[] =
|
|||
{"zkn", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zks", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"ztso", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"zve32x", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zve32f", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zve32d", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zve64x", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zve64f", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zve64d", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
|
@ -259,10 +285,30 @@ static const struct riscv_ext_version riscv_ext_version_table[] =
|
|||
{"zvfhmin", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zvfh", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"zfa", ISA_SPEC_CLASS_NONE, 0, 1},
|
||||
|
||||
{"zmmul", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"zca", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zcb", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zce", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zcf", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zcd", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zcmp", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"zcmt", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"smaia", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"smepmp", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"smstateen", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"ssaia", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"sscofpmf", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"ssstateen", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"sstc", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"svinval", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"svnapot", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"svpbmt", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"xtheadba", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"xtheadbb", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
|
@ -277,6 +323,8 @@ static const struct riscv_ext_version riscv_ext_version_table[] =
|
|||
{"xtheadmempair", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
{"xtheadsync", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
{"xventanacondops", ISA_SPEC_CLASS_NONE, 1, 0},
|
||||
|
||||
/* Terminate the list. */
|
||||
{NULL, ISA_SPEC_CLASS_NONE, 0, 0}
|
||||
};
|
||||
|
|
@ -1266,11 +1314,22 @@ riscv_subset_list::parse (const char *arch, location_t loc)
|
|||
subset_list->handle_implied_ext (itr->name.c_str ());
|
||||
}
|
||||
|
||||
/* Zce only implies zcf when RV32 and 'f' extension exist. */
|
||||
if (subset_list->lookup ("zce") != NULL
|
||||
&& subset_list->m_xlen == 32
|
||||
&& subset_list->lookup ("f") != NULL
|
||||
&& subset_list->lookup ("zcf") == NULL)
|
||||
subset_list->add ("zcf", false);
|
||||
|
||||
/* Make sure all implied extensions are included. */
|
||||
gcc_assert (subset_list->check_implied_ext ());
|
||||
|
||||
subset_list->handle_combine_ext ();
|
||||
|
||||
if (subset_list->lookup ("zcf") && subset_list->m_xlen == 64)
|
||||
error_at (loc, "%<-march=%s%>: zcf extension supports in rv32 only"
|
||||
, arch);
|
||||
|
||||
if (subset_list->lookup ("zfinx") && subset_list->lookup ("f"))
|
||||
error_at (loc, "%<-march=%s%>: z*inx conflicts with floating-point "
|
||||
"extensions", arch);
|
||||
|
|
@ -1344,6 +1403,7 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
|
|||
{"zkt", &gcc_options::x_riscv_zk_subext, MASK_ZKT},
|
||||
|
||||
{"zihintntl", &gcc_options::x_riscv_zi_subext, MASK_ZIHINTNTL},
|
||||
{"zihintpause", &gcc_options::x_riscv_zi_subext, MASK_ZIHINTPAUSE},
|
||||
|
||||
{"zicboz", &gcc_options::x_riscv_zicmo_subext, MASK_ZICBOZ},
|
||||
{"zicbom", &gcc_options::x_riscv_zicmo_subext, MASK_ZICBOM},
|
||||
|
|
@ -1402,11 +1462,24 @@ static const riscv_ext_flag_table_t riscv_ext_flag_table[] =
|
|||
{"zvfhmin", &gcc_options::x_riscv_zf_subext, MASK_ZVFHMIN},
|
||||
{"zvfh", &gcc_options::x_riscv_zf_subext, MASK_ZVFH},
|
||||
|
||||
{"zfa", &gcc_options::x_riscv_zfa_subext, MASK_ZFA},
|
||||
|
||||
{"zmmul", &gcc_options::x_riscv_zm_subext, MASK_ZMMUL},
|
||||
|
||||
/* Code-size reduction extensions. */
|
||||
{"zca", &gcc_options::x_riscv_zc_subext, MASK_ZCA},
|
||||
{"zcb", &gcc_options::x_riscv_zc_subext, MASK_ZCB},
|
||||
{"zce", &gcc_options::x_riscv_zc_subext, MASK_ZCE},
|
||||
{"zcf", &gcc_options::x_riscv_zc_subext, MASK_ZCF},
|
||||
{"zcd", &gcc_options::x_riscv_zc_subext, MASK_ZCD},
|
||||
{"zcmp", &gcc_options::x_riscv_zc_subext, MASK_ZCMP},
|
||||
{"zcmt", &gcc_options::x_riscv_zc_subext, MASK_ZCMT},
|
||||
|
||||
{"svinval", &gcc_options::x_riscv_sv_subext, MASK_SVINVAL},
|
||||
{"svnapot", &gcc_options::x_riscv_sv_subext, MASK_SVNAPOT},
|
||||
|
||||
{"ztso", &gcc_options::x_riscv_ztso_subext, MASK_ZTSO},
|
||||
|
||||
{"xtheadba", &gcc_options::x_riscv_xthead_subext, MASK_XTHEADBA},
|
||||
{"xtheadbb", &gcc_options::x_riscv_xthead_subext, MASK_XTHEADBB},
|
||||
{"xtheadbs", &gcc_options::x_riscv_xthead_subext, MASK_XTHEADBS},
|
||||
|
|
@ -1973,6 +2046,8 @@ riscv_get_valid_option_values (int option_code,
|
|||
static const struct default_options riscv_option_optimization_table[] =
|
||||
{
|
||||
{ OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 },
|
||||
/* Enable -fsched-pressure starting at -O1. */
|
||||
{ OPT_LEVELS_1_PLUS, OPT_fsched_pressure, NULL, 1 },
|
||||
{ OPT_LEVELS_2_PLUS, OPT_free, NULL, 1 },
|
||||
#if TARGET_DEFAULT_ASYNC_UNWIND_TABLES == 1
|
||||
{ OPT_LEVELS_ALL, OPT_fasynchronous_unwind_tables, NULL, 1 },
|
||||
|
|
|
|||
|
|
@ -541,7 +541,7 @@ pru-*-*)
|
|||
;;
|
||||
riscv*)
|
||||
cpu_type=riscv
|
||||
extra_objs="riscv-builtins.o riscv-c.o riscv-sr.o riscv-shorten-memrefs.o riscv-selftests.o riscv-v.o riscv-vsetvl.o"
|
||||
extra_objs="riscv-builtins.o riscv-c.o riscv-sr.o riscv-shorten-memrefs.o riscv-selftests.o riscv-v.o riscv-vsetvl.o riscv-vector-costs.o"
|
||||
extra_objs="${extra_objs} riscv-vector-builtins.o riscv-vector-builtins-shapes.o riscv-vector-builtins-bases.o"
|
||||
extra_objs="${extra_objs} thead.o"
|
||||
d_target_objs="riscv-d.o"
|
||||
|
|
|
|||
|
|
@ -622,15 +622,13 @@
|
|||
#endif
|
||||
|
||||
|
||||
/* Define if your Mac OS X assembler supports -mllvm -x86-pad-for-align=false.
|
||||
*/
|
||||
/* Define if your macOS assembler supports -mllvm -x86-pad-for-align=false. */
|
||||
#ifndef USED_FOR_TARGET
|
||||
#undef HAVE_AS_MLLVM_X86_PAD_FOR_ALIGN
|
||||
#endif
|
||||
|
||||
|
||||
/* Define if your Mac OS X assembler supports the -mmacos-version-min option.
|
||||
*/
|
||||
/* Define if your macOS assembler supports the -mmacos-version-min option. */
|
||||
#ifndef USED_FOR_TARGET
|
||||
#undef HAVE_AS_MMACOSX_VERSION_MIN_OPTION
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -170,10 +170,14 @@ AARCH64_CORE("cortex-r82", cortexr82, cortexa53, V8R, (), cortexa53, 0x41, 0xd15
|
|||
/* Arm ('A') cores. */
|
||||
AARCH64_CORE("cortex-a510", cortexa510, cortexa55, V9A, (SVE2_BITPERM, MEMTAG, I8MM, BF16), cortexa53, 0x41, 0xd46, -1)
|
||||
|
||||
AARCH64_CORE("cortex-a520", cortexa520, cortexa55, V9_2A, (SVE2_BITPERM, MEMTAG), cortexa53, 0x41, 0xd80, -1)
|
||||
|
||||
AARCH64_CORE("cortex-a710", cortexa710, cortexa57, V9A, (SVE2_BITPERM, MEMTAG, I8MM, BF16), neoversen2, 0x41, 0xd47, -1)
|
||||
|
||||
AARCH64_CORE("cortex-a715", cortexa715, cortexa57, V9A, (SVE2_BITPERM, MEMTAG, I8MM, BF16), neoversen2, 0x41, 0xd4d, -1)
|
||||
|
||||
AARCH64_CORE("cortex-a720", cortexa720, cortexa57, V9_2A, (SVE2_BITPERM, MEMTAG, PROFILE), neoversen2, 0x41, 0xd81, -1)
|
||||
|
||||
AARCH64_CORE("cortex-x2", cortexx2, cortexa57, V9A, (SVE2_BITPERM, MEMTAG, I8MM, BF16), neoversen2, 0x41, 0xd48, -1)
|
||||
|
||||
AARCH64_CORE("cortex-x3", cortexx3, cortexa57, V9A, (SVE2_BITPERM, MEMTAG, I8MM, BF16), neoversen2, 0x41, 0xd4e, -1)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
;; -*- buffer-read-only: t -*-
|
||||
;; Generated automatically by gentune.sh from aarch64-cores.def
|
||||
(define_attr "tune"
|
||||
"cortexa34,cortexa35,cortexa53,cortexa57,cortexa72,cortexa73,thunderx,thunderxt88p1,thunderxt88,octeontx,octeontxt81,octeontxt83,thunderxt81,thunderxt83,ampere1,ampere1a,emag,xgene1,falkor,qdf24xx,exynosm1,phecda,thunderx2t99p1,vulcan,thunderx2t99,cortexa55,cortexa75,cortexa76,cortexa76ae,cortexa77,cortexa78,cortexa78ae,cortexa78c,cortexa65,cortexa65ae,cortexx1,cortexx1c,ares,neoversen1,neoversee1,octeontx2,octeontx2t98,octeontx2t96,octeontx2t93,octeontx2f95,octeontx2f95n,octeontx2f95mm,a64fx,tsv110,thunderx3t110,zeus,neoversev1,neoverse512tvb,saphira,cortexa57cortexa53,cortexa72cortexa53,cortexa73cortexa35,cortexa73cortexa53,cortexa75cortexa55,cortexa76cortexa55,cortexr82,cortexa510,cortexa710,cortexa715,cortexx2,cortexx3,neoversen2,demeter,neoversev2"
|
||||
"cortexa34,cortexa35,cortexa53,cortexa57,cortexa72,cortexa73,thunderx,thunderxt88p1,thunderxt88,octeontx,octeontxt81,octeontxt83,thunderxt81,thunderxt83,ampere1,ampere1a,emag,xgene1,falkor,qdf24xx,exynosm1,phecda,thunderx2t99p1,vulcan,thunderx2t99,cortexa55,cortexa75,cortexa76,cortexa76ae,cortexa77,cortexa78,cortexa78ae,cortexa78c,cortexa65,cortexa65ae,cortexx1,cortexx1c,ares,neoversen1,neoversee1,octeontx2,octeontx2t98,octeontx2t96,octeontx2t93,octeontx2f95,octeontx2f95n,octeontx2f95mm,a64fx,tsv110,thunderx3t110,zeus,neoversev1,neoverse512tvb,saphira,cortexa57cortexa53,cortexa72cortexa53,cortexa73cortexa35,cortexa73cortexa53,cortexa75cortexa55,cortexa76cortexa55,cortexr82,cortexa510,cortexa520,cortexa710,cortexa715,cortexa720,cortexx2,cortexx3,neoversen2,demeter,neoversev2"
|
||||
(const (symbol_ref "((enum attr_tune) aarch64_tune)")))
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@
|
|||
#include "aarch64-feature-deps.h"
|
||||
#include "config/arm/aarch-common.h"
|
||||
#include "config/arm/aarch-common-protos.h"
|
||||
#include "ssa.h"
|
||||
|
||||
/* This file should be included last. */
|
||||
#include "target-def.h"
|
||||
|
|
@ -11182,7 +11183,8 @@ aarch64_classify_symbolic_expression (rtx x)
|
|||
/* Return TRUE if X is a legitimate address for accessing memory in
|
||||
mode MODE. */
|
||||
static bool
|
||||
aarch64_legitimate_address_hook_p (machine_mode mode, rtx x, bool strict_p)
|
||||
aarch64_legitimate_address_hook_p (machine_mode mode, rtx x, bool strict_p,
|
||||
code_helper = ERROR_MARK)
|
||||
{
|
||||
struct aarch64_address_info addr;
|
||||
|
||||
|
|
@ -16410,20 +16412,20 @@ aarch64_multiply_add_p (vec_info *vinfo, stmt_vec_info stmt_info,
|
|||
if (code != PLUS_EXPR && code != MINUS_EXPR)
|
||||
return false;
|
||||
|
||||
for (int i = 1; i < 3; ++i)
|
||||
auto is_mul_result = [&](int i)
|
||||
{
|
||||
tree rhs = gimple_op (assign, i);
|
||||
/* ??? Should we try to check for a single use as well? */
|
||||
if (TREE_CODE (rhs) != SSA_NAME)
|
||||
continue;
|
||||
return false;
|
||||
|
||||
stmt_vec_info def_stmt_info = vinfo->lookup_def (rhs);
|
||||
if (!def_stmt_info
|
||||
|| STMT_VINFO_DEF_TYPE (def_stmt_info) != vect_internal_def)
|
||||
continue;
|
||||
return false;
|
||||
gassign *rhs_assign = dyn_cast<gassign *> (def_stmt_info->stmt);
|
||||
if (!rhs_assign || gimple_assign_rhs_code (rhs_assign) != MULT_EXPR)
|
||||
continue;
|
||||
return false;
|
||||
|
||||
if (vec_flags & VEC_ADVSIMD)
|
||||
{
|
||||
|
|
@ -16443,8 +16445,19 @@ aarch64_multiply_add_p (vec_info *vinfo, stmt_vec_info stmt_info,
|
|||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
if (code == MINUS_EXPR && (vec_flags & VEC_ADVSIMD))
|
||||
/* Advanced SIMD doesn't have FNMADD/FNMSUB/FNMLA/FNMLS, so the
|
||||
multiplication must be on the second operand (to form an FMLS).
|
||||
But if both operands are multiplications and the second operand
|
||||
is used more than once, we'll instead negate the second operand
|
||||
and use it as an accumulator for the first operand. */
|
||||
return (is_mul_result (2)
|
||||
&& (has_single_use (gimple_assign_rhs2 (assign))
|
||||
|| !is_mul_result (1)));
|
||||
|
||||
return is_mul_result (1) || is_mul_result (2);
|
||||
}
|
||||
|
||||
/* Return true if STMT_INFO is the second part of a two-statement boolean AND
|
||||
|
|
@ -25665,7 +25678,7 @@ aarch64_asan_shadow_offset (void)
|
|||
|
||||
static rtx
|
||||
aarch64_gen_ccmp_first (rtx_insn **prep_seq, rtx_insn **gen_seq,
|
||||
int code, tree treeop0, tree treeop1)
|
||||
rtx_code code, tree treeop0, tree treeop1)
|
||||
{
|
||||
machine_mode op_mode, cmp_mode, cc_mode = CCmode;
|
||||
rtx op0, op1;
|
||||
|
|
@ -25739,7 +25752,8 @@ aarch64_gen_ccmp_first (rtx_insn **prep_seq, rtx_insn **gen_seq,
|
|||
|
||||
static rtx
|
||||
aarch64_gen_ccmp_next (rtx_insn **prep_seq, rtx_insn **gen_seq, rtx prev,
|
||||
int cmp_code, tree treeop0, tree treeop1, int bit_code)
|
||||
rtx_code cmp_code, tree treeop0, tree treeop1,
|
||||
rtx_code bit_code)
|
||||
{
|
||||
rtx op0, op1, target;
|
||||
machine_mode op_mode, cmp_mode, cc_mode = CCmode;
|
||||
|
|
|
|||
|
|
@ -1170,9 +1170,27 @@
|
|||
{
|
||||
int i;
|
||||
|
||||
/* Generate a PARALLEL that contains all of the register results.
|
||||
The offsets are somewhat arbitrary, since we don't know the
|
||||
actual return type. The main thing we need to avoid is having
|
||||
overlapping byte ranges, since those might give the impression
|
||||
that two registers are known to have data in common. */
|
||||
rtvec rets = rtvec_alloc (XVECLEN (operands[2], 0));
|
||||
poly_int64 offset = 0;
|
||||
for (i = 0; i < XVECLEN (operands[2], 0); i++)
|
||||
{
|
||||
rtx reg = SET_SRC (XVECEXP (operands[2], 0, i));
|
||||
gcc_assert (REG_P (reg));
|
||||
rtx offset_rtx = gen_int_mode (offset, Pmode);
|
||||
rtx piece = gen_rtx_EXPR_LIST (VOIDmode, reg, offset_rtx);
|
||||
RTVEC_ELT (rets, i) = piece;
|
||||
offset += GET_MODE_SIZE (GET_MODE (reg));
|
||||
}
|
||||
rtx ret = gen_rtx_PARALLEL (VOIDmode, rets);
|
||||
|
||||
/* Untyped calls always use the default ABI. It's only possible to use
|
||||
ABI variants if we know the type of the target function. */
|
||||
emit_call_insn (gen_call (operands[0], const0_rtx, const0_rtx));
|
||||
emit_call_insn (gen_call_value (ret, operands[0], const0_rtx, const0_rtx));
|
||||
|
||||
for (i = 0; i < XVECLEN (operands[2], 0); i++)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -740,7 +740,7 @@ dump_insn_list (const rtx &t, const insn_info_list_t &insn_info,
|
|||
void *unused ATTRIBUTE_UNUSED)
|
||||
{
|
||||
gcc_assert (dump_file);
|
||||
fprintf (dump_file, "Tag 0x%lx ::\n", INTVAL (t));
|
||||
fprintf (dump_file, "Tag 0x" HOST_WIDE_INT_PRINT_HEX_PURE " ::\n", INTVAL (t));
|
||||
|
||||
for (unsigned i = 0; i < insn_info.length (); i++)
|
||||
dump_insn_slim (dump_file, insn_info[i]->insn);
|
||||
|
|
|
|||
|
|
@ -844,7 +844,8 @@ alpha_linkage_symbol_p (const char *symname)
|
|||
low-order three bits; this is an "unaligned" access. */
|
||||
|
||||
static bool
|
||||
alpha_legitimate_address_p (machine_mode mode, rtx x, bool strict)
|
||||
alpha_legitimate_address_p (machine_mode mode, rtx x, bool strict,
|
||||
code_helper = ERROR_MARK)
|
||||
{
|
||||
/* If this is an ldq_u type address, discard the outer AND. */
|
||||
if (mode == DImode
|
||||
|
|
|
|||
|
|
@ -6715,7 +6715,8 @@ arc_legitimate_constant_p (machine_mode mode, rtx x)
|
|||
}
|
||||
|
||||
static bool
|
||||
arc_legitimate_address_p (machine_mode mode, rtx x, bool strict)
|
||||
arc_legitimate_address_p (machine_mode mode, rtx x, bool strict,
|
||||
code_helper = ERROR_MARK)
|
||||
{
|
||||
if (RTX_OK_FOR_BASE_P (x, strict))
|
||||
return true;
|
||||
|
|
@ -11646,7 +11647,7 @@ arc_split_mov_const (rtx *operands)
|
|||
}
|
||||
|
||||
/* 3. Check if we can just shift by 16 to fit into the u6 of LSL16. */
|
||||
if (TARGET_BARREL_SHIFTER && TARGET_V2
|
||||
if (TARGET_SWAP && TARGET_V2
|
||||
&& ((ival & ~0x3f0000) == 0))
|
||||
{
|
||||
shimm = (ival >> 16) & 0x3f;
|
||||
|
|
|
|||
|
|
@ -5991,7 +5991,7 @@ archs4x, archs4xd"
|
|||
[(set (match_operand:SI 0 "register_operand" "=r")
|
||||
(ashift:SI (match_operand:SI 1 "nonmemory_operand" "rL")
|
||||
(const_int 16)))]
|
||||
"TARGET_BARREL_SHIFTER && TARGET_V2"
|
||||
"TARGET_SWAP && TARGET_V2"
|
||||
"lsl16\\t%0,%1"
|
||||
[(set_attr "type" "shift")
|
||||
(set_attr "iscompact" "false")
|
||||
|
|
|
|||
|
|
@ -260,8 +260,8 @@ FUNCTION_PRED_P_S_U (vaddvq, VADDVQ)
|
|||
FUNCTION_PRED_P_S_U (vaddvaq, VADDVAQ)
|
||||
FUNCTION_WITH_RTX_M (vandq, AND, VANDQ)
|
||||
FUNCTION_ONLY_N (vbrsrq, VBRSRQ)
|
||||
FUNCTION (vcaddq_rot90, unspec_mve_function_exact_insn_rot, (UNSPEC_VCADD90, UNSPEC_VCADD90, UNSPEC_VCADD90, VCADDQ_ROT90_M_S, VCADDQ_ROT90_M_U, VCADDQ_ROT90_M_F))
|
||||
FUNCTION (vcaddq_rot270, unspec_mve_function_exact_insn_rot, (UNSPEC_VCADD270, UNSPEC_VCADD270, UNSPEC_VCADD270, VCADDQ_ROT270_M_S, VCADDQ_ROT270_M_U, VCADDQ_ROT270_M_F))
|
||||
FUNCTION (vcaddq_rot90, unspec_mve_function_exact_insn_rot, (UNSPEC_VCADD90, UNSPEC_VCADD90, UNSPEC_VCADD90, VCADDQ_ROT90_M, VCADDQ_ROT90_M, VCADDQ_ROT90_M_F))
|
||||
FUNCTION (vcaddq_rot270, unspec_mve_function_exact_insn_rot, (UNSPEC_VCADD270, UNSPEC_VCADD270, UNSPEC_VCADD270, VCADDQ_ROT270_M, VCADDQ_ROT270_M, VCADDQ_ROT270_M_F))
|
||||
FUNCTION (vcmlaq, unspec_mve_function_exact_insn_rot, (-1, -1, UNSPEC_VCMLA, -1, -1, VCMLAQ_M_F))
|
||||
FUNCTION (vcmlaq_rot90, unspec_mve_function_exact_insn_rot, (-1, -1, UNSPEC_VCMLA90, -1, -1, VCMLAQ_ROT90_M_F))
|
||||
FUNCTION (vcmlaq_rot180, unspec_mve_function_exact_insn_rot, (-1, -1, UNSPEC_VCMLA180, -1, -1, VCMLAQ_ROT180_M_F))
|
||||
|
|
@ -329,6 +329,10 @@ FUNCTION_WITHOUT_N_NO_F (vmovltq, VMOVLTQ)
|
|||
FUNCTION_WITHOUT_N_NO_F (vmovnbq, VMOVNBQ)
|
||||
FUNCTION_WITHOUT_N_NO_F (vmovntq, VMOVNTQ)
|
||||
FUNCTION_WITHOUT_N_NO_F (vmulhq, VMULHQ)
|
||||
FUNCTION (vmullbq_int, unspec_mve_function_exact_insn_vmull, (VMULLBQ_INT_S, VMULLBQ_INT_U, VMULLBQ_INT_M_S, VMULLBQ_INT_M_U))
|
||||
FUNCTION (vmulltq_int, unspec_mve_function_exact_insn_vmull, (VMULLTQ_INT_S, VMULLTQ_INT_U, VMULLTQ_INT_M_S, VMULLTQ_INT_M_U))
|
||||
FUNCTION (vmullbq_poly, unspec_mve_function_exact_insn_vmull_poly, (VMULLBQ_POLY_P, VMULLBQ_POLY_M_P))
|
||||
FUNCTION (vmulltq_poly, unspec_mve_function_exact_insn_vmull_poly, (VMULLTQ_POLY_P, VMULLTQ_POLY_M_P))
|
||||
FUNCTION_WITH_RTX_M_N (vmulq, MULT, VMULQ)
|
||||
FUNCTION_WITH_RTX_M_N_NO_F (vmvnq, NOT, VMVNQ)
|
||||
FUNCTION (vnegq, unspec_based_mve_function_exact_insn, (NEG, NEG, NEG, -1, -1, -1, VNEGQ_M_S, -1, VNEGQ_M_F, -1, -1, -1))
|
||||
|
|
|
|||
|
|
@ -78,6 +78,10 @@ DEF_MVE_FUNCTION (vmovltq, unary_widen, integer_8_16, mx_or_none)
|
|||
DEF_MVE_FUNCTION (vmovnbq, binary_move_narrow, integer_16_32, m_or_none)
|
||||
DEF_MVE_FUNCTION (vmovntq, binary_move_narrow, integer_16_32, m_or_none)
|
||||
DEF_MVE_FUNCTION (vmulhq, binary, all_integer, mx_or_none)
|
||||
DEF_MVE_FUNCTION (vmullbq_int, binary_widen, all_integer, mx_or_none)
|
||||
DEF_MVE_FUNCTION (vmulltq_int, binary_widen, all_integer, mx_or_none)
|
||||
DEF_MVE_FUNCTION (vmullbq_poly, binary_widen_poly, poly_8_16, mx_or_none)
|
||||
DEF_MVE_FUNCTION (vmulltq_poly, binary_widen_poly, poly_8_16, mx_or_none)
|
||||
DEF_MVE_FUNCTION (vmulq, binary_opt_n, all_integer, mx_or_none)
|
||||
DEF_MVE_FUNCTION (vmvnq, mvn, all_integer, mx_or_none)
|
||||
DEF_MVE_FUNCTION (vnegq, unary, all_signed, mx_or_none)
|
||||
|
|
|
|||
|
|
@ -102,6 +102,10 @@ extern const function_base *const vmovltq;
|
|||
extern const function_base *const vmovnbq;
|
||||
extern const function_base *const vmovntq;
|
||||
extern const function_base *const vmulhq;
|
||||
extern const function_base *const vmullbq_int;
|
||||
extern const function_base *const vmulltq_int;
|
||||
extern const function_base *const vmullbq_poly;
|
||||
extern const function_base *const vmulltq_poly;
|
||||
extern const function_base *const vmulq;
|
||||
extern const function_base *const vmvnq;
|
||||
extern const function_base *const vnegq;
|
||||
|
|
|
|||
|
|
@ -838,6 +838,134 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/* Map the vmull-related function directly to CODE (UNSPEC, UNSPEC, M)
|
||||
where M is the vector mode associated with type suffix 0. We need
|
||||
this special case because the builtins have _int in their
|
||||
names. */
|
||||
class unspec_mve_function_exact_insn_vmull : public function_base
|
||||
{
|
||||
public:
|
||||
CONSTEXPR unspec_mve_function_exact_insn_vmull (int unspec_for_sint,
|
||||
int unspec_for_uint,
|
||||
int unspec_for_m_sint,
|
||||
int unspec_for_m_uint)
|
||||
: m_unspec_for_sint (unspec_for_sint),
|
||||
m_unspec_for_uint (unspec_for_uint),
|
||||
m_unspec_for_m_sint (unspec_for_m_sint),
|
||||
m_unspec_for_m_uint (unspec_for_m_uint)
|
||||
{}
|
||||
|
||||
/* The unspec code associated with signed-integer and
|
||||
unsigned-integer operations respectively. It covers the cases
|
||||
with and without the _m predicate. */
|
||||
int m_unspec_for_sint;
|
||||
int m_unspec_for_uint;
|
||||
int m_unspec_for_m_sint;
|
||||
int m_unspec_for_m_uint;
|
||||
|
||||
rtx
|
||||
expand (function_expander &e) const override
|
||||
{
|
||||
insn_code code;
|
||||
|
||||
if (! e.type_suffix (0).integer_p)
|
||||
gcc_unreachable ();
|
||||
|
||||
if (e.mode_suffix_id != MODE_none)
|
||||
gcc_unreachable ();
|
||||
|
||||
switch (e.pred)
|
||||
{
|
||||
case PRED_none:
|
||||
/* No predicate, no suffix. */
|
||||
if (e.type_suffix (0).unsigned_p)
|
||||
code = code_for_mve_q_int (m_unspec_for_uint, m_unspec_for_uint, e.vector_mode (0));
|
||||
else
|
||||
code = code_for_mve_q_int (m_unspec_for_sint, m_unspec_for_sint, e.vector_mode (0));
|
||||
|
||||
return e.use_exact_insn (code);
|
||||
|
||||
case PRED_m:
|
||||
/* No suffix, "m" predicate. */
|
||||
if (e.type_suffix (0).unsigned_p)
|
||||
code = code_for_mve_q_int_m (m_unspec_for_m_uint, m_unspec_for_m_uint, e.vector_mode (0));
|
||||
else
|
||||
code = code_for_mve_q_int_m (m_unspec_for_m_sint, m_unspec_for_m_sint, e.vector_mode (0));
|
||||
|
||||
return e.use_cond_insn (code, 0);
|
||||
|
||||
case PRED_x:
|
||||
/* No suffix, "x" predicate. */
|
||||
if (e.type_suffix (0).unsigned_p)
|
||||
code = code_for_mve_q_int_m (m_unspec_for_m_uint, m_unspec_for_m_uint, e.vector_mode (0));
|
||||
else
|
||||
code = code_for_mve_q_int_m (m_unspec_for_m_sint, m_unspec_for_m_sint, e.vector_mode (0));
|
||||
|
||||
return e.use_pred_x_insn (code);
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
gcc_unreachable ();
|
||||
}
|
||||
};
|
||||
|
||||
/* Map the vmull_poly-related function directly to CODE (UNSPEC,
|
||||
UNSPEC, M) where M is the vector mode associated with type suffix
|
||||
0. We need this special case because the builtins have _poly in
|
||||
their names, and use the special poly type.. */
|
||||
class unspec_mve_function_exact_insn_vmull_poly : public function_base
|
||||
{
|
||||
public:
|
||||
CONSTEXPR unspec_mve_function_exact_insn_vmull_poly (int unspec_for_poly,
|
||||
int unspec_for_m_poly)
|
||||
: m_unspec_for_poly (unspec_for_poly),
|
||||
m_unspec_for_m_poly (unspec_for_m_poly)
|
||||
{}
|
||||
|
||||
/* The unspec code associated with signed-integer, unsigned-integer
|
||||
and poly operations respectively. It covers the cases with and
|
||||
without the _m predicate. */
|
||||
int m_unspec_for_poly;
|
||||
int m_unspec_for_m_poly;
|
||||
|
||||
rtx
|
||||
expand (function_expander &e) const override
|
||||
{
|
||||
insn_code code;
|
||||
|
||||
if (e.mode_suffix_id != MODE_none)
|
||||
gcc_unreachable ();
|
||||
|
||||
if (! e.type_suffix (0).poly_p)
|
||||
gcc_unreachable ();
|
||||
|
||||
switch (e.pred)
|
||||
{
|
||||
case PRED_none:
|
||||
/* No predicate, no suffix. */
|
||||
code = code_for_mve_q_poly (m_unspec_for_poly, m_unspec_for_poly, e.vector_mode (0));
|
||||
return e.use_exact_insn (code);
|
||||
|
||||
case PRED_m:
|
||||
/* No suffix, "m" predicate. */
|
||||
code = code_for_mve_q_poly_m (m_unspec_for_m_poly, m_unspec_for_m_poly, e.vector_mode (0));
|
||||
return e.use_cond_insn (code, 0);
|
||||
|
||||
case PRED_x:
|
||||
/* No suffix, "x" predicate. */
|
||||
code = code_for_mve_q_poly_m (m_unspec_for_m_poly, m_unspec_for_m_poly, e.vector_mode (0));
|
||||
return e.use_pred_x_insn (code);
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
|
||||
gcc_unreachable ();
|
||||
}
|
||||
};
|
||||
|
||||
} /* end namespace arm_mve */
|
||||
|
||||
/* Declare the global function base NAME, creating it from an instance
|
||||
|
|
|
|||
|
|
@ -61,10 +61,12 @@ apply_predication (const function_instance &instance, tree return_type,
|
|||
|
||||
[01] - the element type in type suffix 0 or 1 of INSTANCE.
|
||||
h<elt> - a half-sized version of <elt>
|
||||
p<elt> - a poly type with the same width as <elt>
|
||||
s<bits> - a signed type with the given number of bits
|
||||
s[01] - a signed type with the same width as type suffix 0 or 1
|
||||
u<bits> - an unsigned type with the given number of bits
|
||||
u[01] - an unsigned type with the same width as type suffix 0 or 1
|
||||
U<elt> - an unsigned type with the double width as <elt>
|
||||
w<elt> - a double-sized version of <elt>
|
||||
x<bits> - a type with the given number of bits and same signedness
|
||||
as the next argument.
|
||||
|
|
@ -78,8 +80,7 @@ parse_element_type (const function_instance &instance, const char *&format)
|
|||
|
||||
if (ch == 's' || ch == 'u')
|
||||
{
|
||||
type_class_index tclass = (ch == 'f' ? TYPE_float
|
||||
: ch == 's' ? TYPE_signed
|
||||
type_class_index tclass = (ch == 's' ? TYPE_signed
|
||||
: TYPE_unsigned);
|
||||
char *end;
|
||||
unsigned int bits = strtol (format, &end, 10);
|
||||
|
|
@ -103,6 +104,20 @@ parse_element_type (const function_instance &instance, const char *&format)
|
|||
type_suffixes[suffix].element_bits * 2);
|
||||
}
|
||||
|
||||
if (ch == 'U')
|
||||
{
|
||||
type_suffix_index suffix = parse_element_type (instance, format);
|
||||
return find_type_suffix (TYPE_unsigned,
|
||||
type_suffixes[suffix].element_bits * 2);
|
||||
}
|
||||
|
||||
if (ch == 'p')
|
||||
{
|
||||
type_suffix_index suffix = parse_element_type (instance, format);
|
||||
return find_type_suffix (TYPE_poly,
|
||||
type_suffixes[suffix].element_bits);
|
||||
}
|
||||
|
||||
if (ch == 'x')
|
||||
{
|
||||
const char *next = format;
|
||||
|
|
@ -439,7 +454,7 @@ struct binary_acca_int32_def : public overloaded_base<0>
|
|||
|| (type = r.infer_vector_type (1)) == NUM_TYPE_SUFFIXES)
|
||||
return error_mark_node;
|
||||
|
||||
unsigned int last_arg = i;
|
||||
unsigned int last_arg = i + 1;
|
||||
for (i = 1; i < last_arg; i++)
|
||||
if (!r.require_matching_vector_type (i, type))
|
||||
return error_mark_node;
|
||||
|
|
@ -476,7 +491,7 @@ struct binary_acca_int64_def : public overloaded_base<0>
|
|||
|| (type = r.infer_vector_type (1)) == NUM_TYPE_SUFFIXES)
|
||||
return error_mark_node;
|
||||
|
||||
unsigned int last_arg = i;
|
||||
unsigned int last_arg = i + 1;
|
||||
for (i = 1; i < last_arg; i++)
|
||||
if (!r.require_matching_vector_type (i, type))
|
||||
return error_mark_node;
|
||||
|
|
@ -1130,6 +1145,97 @@ struct binary_rshift_narrow_unsigned_def : public overloaded_base<0>
|
|||
};
|
||||
SHAPE (binary_rshift_narrow_unsigned)
|
||||
|
||||
/* <T0:twice>_t vfoo[_t0](<T0>_t, <T0>_t)
|
||||
|
||||
Example: vmullbq.
|
||||
int32x4_t [__arm_]vmullbq_int[_s16](int16x8_t a, int16x8_t b)
|
||||
int32x4_t [__arm_]vmullbq_int_m[_s16](int32x4_t inactive, int16x8_t a, int16x8_t b, mve_pred16_t p)
|
||||
int32x4_t [__arm_]vmullbq_int_x[_s16](int16x8_t a, int16x8_t b, mve_pred16_t p) */
|
||||
struct binary_widen_def : public overloaded_base<0>
|
||||
{
|
||||
void
|
||||
build (function_builder &b, const function_group_info &group,
|
||||
bool preserve_user_namespace) const override
|
||||
{
|
||||
b.add_overloaded_functions (group, MODE_none, preserve_user_namespace);
|
||||
build_all (b, "vw0,v0,v0", group, MODE_none, preserve_user_namespace);
|
||||
}
|
||||
|
||||
tree
|
||||
resolve (function_resolver &r) const override
|
||||
{
|
||||
unsigned int i, nargs;
|
||||
type_suffix_index type;
|
||||
if (!r.check_gp_argument (2, i, nargs)
|
||||
|| (type = r.infer_vector_type (i - 1)) == NUM_TYPE_SUFFIXES)
|
||||
return error_mark_node;
|
||||
|
||||
type_suffix_index wide_suffix
|
||||
= find_type_suffix (type_suffixes[type].tclass,
|
||||
type_suffixes[type].element_bits * 2);
|
||||
|
||||
if (!r.require_matching_vector_type (i, type))
|
||||
return error_mark_node;
|
||||
|
||||
/* Check the inactive argument has the wide type. */
|
||||
if ((r.pred == PRED_m)
|
||||
&& (r.infer_vector_type (0) != wide_suffix))
|
||||
return r.report_no_such_form (type);
|
||||
|
||||
return r.resolve_to (r.mode_suffix_id, type);
|
||||
}
|
||||
};
|
||||
SHAPE (binary_widen)
|
||||
|
||||
/* <T0:twice>_t vfoo[_t0](<T0>_t, <T0>_t)
|
||||
|
||||
Example: vmullbq_poly.
|
||||
uint32x4_t [__arm_]vmullbq_poly[_p16](uint16x8_t a, uint16x8_t b)
|
||||
uint32x4_t [__arm_]vmullbq_poly_m[_p16](uint32x4_t inactive, uint16x8_t a, uint16x8_t b, mve_pred16_t p)
|
||||
uint32x4_t [__arm_]vmullbq_poly_x[_p16](uint16x8_t a, uint16x8_t b, mve_pred16_t p) */
|
||||
struct binary_widen_poly_def : public overloaded_base<0>
|
||||
{
|
||||
void
|
||||
build (function_builder &b, const function_group_info &group,
|
||||
bool preserve_user_namespace) const override
|
||||
{
|
||||
b.add_overloaded_functions (group, MODE_none, preserve_user_namespace);
|
||||
build_all (b, "vU0,vp0,vp0", group, MODE_none, preserve_user_namespace);
|
||||
}
|
||||
|
||||
tree
|
||||
resolve (function_resolver &r) const override
|
||||
{
|
||||
unsigned int i, nargs;
|
||||
type_suffix_index type;
|
||||
if (!r.check_gp_argument (2, i, nargs)
|
||||
|| (type = r.infer_vector_type (i - 1)) == NUM_TYPE_SUFFIXES)
|
||||
return error_mark_node;
|
||||
|
||||
/* infer_vector_type found the 'unsigned' version of the 'poly'
|
||||
type we are looking for, so find the 'poly' type with the same
|
||||
width. */
|
||||
type = find_type_suffix (TYPE_poly, type_suffixes[type].element_bits);
|
||||
|
||||
type_suffix_index wide_suffix
|
||||
= find_type_suffix (TYPE_unsigned,
|
||||
type_suffixes[type].element_bits * 2);
|
||||
|
||||
/* Require the 'poly' type, require_matching_vector_type would try
|
||||
and fail with the 'unsigned' one. */
|
||||
if (!r.require_vector_type (i, type_suffixes[type].vector_type))
|
||||
return error_mark_node;
|
||||
|
||||
/* Check the inactive argument has the wide type. */
|
||||
if ((r.pred == PRED_m)
|
||||
&& (r.infer_vector_type (0) != wide_suffix))
|
||||
return r.report_no_such_form (type);
|
||||
|
||||
return r.resolve_to (r.mode_suffix_id, type);
|
||||
}
|
||||
};
|
||||
SHAPE (binary_widen_poly)
|
||||
|
||||
/* <T0:twice>_t vfoo[_n_t0](<T0>_t, const int)
|
||||
|
||||
Check that 'imm' is in the [1..#bits] range.
|
||||
|
|
|
|||
|
|
@ -35,13 +35,13 @@ namespace arm_mve
|
|||
{
|
||||
|
||||
extern const function_shape *const binary;
|
||||
extern const function_shape *const binary_lshift;
|
||||
extern const function_shape *const binary_lshift_r;
|
||||
extern const function_shape *const binary_acc_int32;
|
||||
extern const function_shape *const binary_acc_int64;
|
||||
extern const function_shape *const binary_acca_int32;
|
||||
extern const function_shape *const binary_acca_int64;
|
||||
extern const function_shape *const binary_imm32;
|
||||
extern const function_shape *const binary_lshift;
|
||||
extern const function_shape *const binary_lshift_r;
|
||||
extern const function_shape *const binary_lshift_unsigned;
|
||||
extern const function_shape *const binary_maxamina;
|
||||
extern const function_shape *const binary_maxavminav;
|
||||
|
|
@ -54,8 +54,10 @@ namespace arm_mve
|
|||
extern const function_shape *const binary_rshift;
|
||||
extern const function_shape *const binary_rshift_narrow;
|
||||
extern const function_shape *const binary_rshift_narrow_unsigned;
|
||||
extern const function_shape *const binary_widen;
|
||||
extern const function_shape *const binary_widen_n;
|
||||
extern const function_shape *const binary_widen_opt_n;
|
||||
extern const function_shape *const binary_widen_poly;
|
||||
extern const function_shape *const cmp;
|
||||
extern const function_shape *const create;
|
||||
extern const function_shape *const inherent;
|
||||
|
|
|
|||
|
|
@ -128,10 +128,11 @@ CONSTEXPR const type_suffix_info type_suffixes[NUM_TYPE_SUFFIXES + 1] = {
|
|||
TYPE_##CLASS == TYPE_signed || TYPE_##CLASS == TYPE_unsigned, \
|
||||
TYPE_##CLASS == TYPE_unsigned, \
|
||||
TYPE_##CLASS == TYPE_float, \
|
||||
TYPE_##CLASS == TYPE_poly, \
|
||||
0, \
|
||||
MODE },
|
||||
#include "arm-mve-builtins.def"
|
||||
{ "", NUM_VECTOR_TYPES, TYPE_bool, 0, 0, false, false, false,
|
||||
{ "", NUM_VECTOR_TYPES, TYPE_bool, 0, 0, false, false, false, false,
|
||||
0, VOIDmode }
|
||||
};
|
||||
|
||||
|
|
@ -177,6 +178,10 @@ CONSTEXPR const type_suffix_info type_suffixes[NUM_TYPE_SUFFIXES + 1] = {
|
|||
#define TYPES_all_signed(S, D) \
|
||||
S (s8), S (s16), S (s32)
|
||||
|
||||
/* _p8 _p16. */
|
||||
#define TYPES_poly_8_16(S, D) \
|
||||
S (p8), S (p16)
|
||||
|
||||
/* _u8 _u16 _u32. */
|
||||
#define TYPES_all_unsigned(S, D) \
|
||||
S (u8), S (u16), S (u32)
|
||||
|
|
@ -275,6 +280,7 @@ DEF_MVE_TYPES_ARRAY (integer_8);
|
|||
DEF_MVE_TYPES_ARRAY (integer_8_16);
|
||||
DEF_MVE_TYPES_ARRAY (integer_16_32);
|
||||
DEF_MVE_TYPES_ARRAY (integer_32);
|
||||
DEF_MVE_TYPES_ARRAY (poly_8_16);
|
||||
DEF_MVE_TYPES_ARRAY (signed_16_32);
|
||||
DEF_MVE_TYPES_ARRAY (signed_32);
|
||||
DEF_MVE_TYPES_ARRAY (reinterpret_integer);
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ DEF_MVE_TYPE_SUFFIX (u8, uint8x16_t, unsigned, 8, V16QImode)
|
|||
DEF_MVE_TYPE_SUFFIX (u16, uint16x8_t, unsigned, 16, V8HImode)
|
||||
DEF_MVE_TYPE_SUFFIX (u32, uint32x4_t, unsigned, 32, V4SImode)
|
||||
DEF_MVE_TYPE_SUFFIX (u64, uint64x2_t, unsigned, 64, V2DImode)
|
||||
DEF_MVE_TYPE_SUFFIX (p8, uint8x16_t, poly, 8, V16QImode)
|
||||
DEF_MVE_TYPE_SUFFIX (p16, uint16x8_t, poly, 16, V8HImode)
|
||||
#undef REQUIRES_FLOAT
|
||||
|
||||
#define REQUIRES_FLOAT true
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ enum type_class_index
|
|||
TYPE_float,
|
||||
TYPE_signed,
|
||||
TYPE_unsigned,
|
||||
TYPE_poly,
|
||||
NUM_TYPE_CLASSES
|
||||
};
|
||||
|
||||
|
|
@ -221,7 +222,9 @@ struct type_suffix_info
|
|||
unsigned int unsigned_p : 1;
|
||||
/* True if the suffix is for a floating-point type. */
|
||||
unsigned int float_p : 1;
|
||||
unsigned int spare : 13;
|
||||
/* True if the suffix is for a polynomial type. */
|
||||
unsigned int poly_p : 1;
|
||||
unsigned int spare : 12;
|
||||
|
||||
/* The associated vector or predicate mode. */
|
||||
machine_mode vector_mode : 16;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#define GCC_ARM_PROTOS_H
|
||||
|
||||
#include "sbitmap.h"
|
||||
#include "tree.h" /* For ERROR_MARK. */
|
||||
|
||||
rtl_opt_pass *make_pass_insert_bti (gcc::context *ctxt);
|
||||
|
||||
|
|
@ -83,7 +84,8 @@ extern int arm_split_constant (RTX_CODE, machine_mode, rtx,
|
|||
extern int legitimate_pic_operand_p (rtx);
|
||||
extern rtx legitimize_pic_address (rtx, machine_mode, rtx, rtx, bool);
|
||||
extern rtx legitimize_tls_address (rtx, rtx);
|
||||
extern bool arm_legitimate_address_p (machine_mode, rtx, bool);
|
||||
extern bool arm_legitimate_address_p (machine_mode, rtx, bool,
|
||||
code_helper = ERROR_MARK);
|
||||
extern int arm_legitimate_address_outer_p (machine_mode, rtx, RTX_CODE, int);
|
||||
extern int thumb_legitimate_offset_p (machine_mode, HOST_WIDE_INT);
|
||||
extern int thumb1_legitimate_address_p (machine_mode, rtx, int);
|
||||
|
|
|
|||
|
|
@ -9171,7 +9171,7 @@ thumb_legitimate_offset_p (machine_mode mode, HOST_WIDE_INT val)
|
|||
}
|
||||
|
||||
bool
|
||||
arm_legitimate_address_p (machine_mode mode, rtx x, bool strict_p)
|
||||
arm_legitimate_address_p (machine_mode mode, rtx x, bool strict_p, code_helper)
|
||||
{
|
||||
if (TARGET_ARM)
|
||||
return arm_legitimate_address_outer_p (mode, x, SET, strict_p);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -523,8 +523,8 @@ VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vhsubq_m_n_u, v16qi, v8hi, v4si)
|
|||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vhaddq_m_u, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vhaddq_m_n_u, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, veorq_m_u, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vcaddq_rot90_m_u, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vcaddq_rot270_m_u, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vcaddq_rot90_m_, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vcaddq_rot270_m_, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vbicq_m_u, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vandq_m_u, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_UNONE_UNONE_UNONE_UNONE_PRED, vaddq_m_u, v16qi, v8hi, v4si)
|
||||
|
|
@ -587,8 +587,6 @@ VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, vhcaddq_rot270_m_s, v16qi, v8hi, v4si)
|
|||
VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, vhaddq_m_s, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, vhaddq_m_n_s, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, veorq_m_s, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, vcaddq_rot90_m_s, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, vcaddq_rot270_m_s, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, vbrsrq_m_n_s, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, vbicq_m_s, v16qi, v8hi, v4si)
|
||||
VAR3 (QUADOP_NONE_NONE_NONE_NONE_PRED, vandq_m_s, v16qi, v8hi, v4si)
|
||||
|
|
|
|||
|
|
@ -917,6 +917,7 @@
|
|||
|
||||
(define_int_attr mve_insn [
|
||||
(UNSPEC_VCADD90 "vcadd") (UNSPEC_VCADD270 "vcadd")
|
||||
(UNSPEC_VCMLA "vcmla") (UNSPEC_VCMLA90 "vcmla") (UNSPEC_VCMLA180 "vcmla") (UNSPEC_VCMLA270 "vcmla")
|
||||
(UNSPEC_VCMUL "vcmul") (UNSPEC_VCMUL90 "vcmul") (UNSPEC_VCMUL180 "vcmul") (UNSPEC_VCMUL270 "vcmul")
|
||||
(VABAVQ_P_S "vabav") (VABAVQ_P_U "vabav")
|
||||
(VABAVQ_S "vabav") (VABAVQ_U "vabav")
|
||||
|
|
@ -941,8 +942,8 @@
|
|||
(VBICQ_N_S "vbic") (VBICQ_N_U "vbic")
|
||||
(VBRSRQ_M_N_S "vbrsr") (VBRSRQ_M_N_U "vbrsr") (VBRSRQ_M_N_F "vbrsr")
|
||||
(VBRSRQ_N_S "vbrsr") (VBRSRQ_N_U "vbrsr") (VBRSRQ_N_F "vbrsr")
|
||||
(VCADDQ_ROT270_M_U "vcadd") (VCADDQ_ROT270_M_S "vcadd") (VCADDQ_ROT270_M_F "vcadd")
|
||||
(VCADDQ_ROT90_M_U "vcadd") (VCADDQ_ROT90_M_S "vcadd") (VCADDQ_ROT90_M_F "vcadd")
|
||||
(VCADDQ_ROT270_M "vcadd") (VCADDQ_ROT270_M_F "vcadd")
|
||||
(VCADDQ_ROT90_M "vcadd") (VCADDQ_ROT90_M_F "vcadd")
|
||||
(VCLSQ_M_S "vcls")
|
||||
(VCLSQ_S "vcls")
|
||||
(VCLZQ_M_S "vclz") (VCLZQ_M_U "vclz")
|
||||
|
|
@ -1044,6 +1045,13 @@
|
|||
(VMOVNTQ_S "vmovnt") (VMOVNTQ_U "vmovnt")
|
||||
(VMULHQ_M_S "vmulh") (VMULHQ_M_U "vmulh")
|
||||
(VMULHQ_S "vmulh") (VMULHQ_U "vmulh")
|
||||
(VMULLBQ_INT_M_S "vmullb") (VMULLBQ_INT_M_U "vmullb")
|
||||
(VMULLBQ_INT_S "vmullb") (VMULLBQ_INT_U "vmullb")
|
||||
(VMULLBQ_POLY_M_P "vmullb") (VMULLTQ_POLY_M_P "vmullt")
|
||||
(VMULLBQ_POLY_P "vmullb")
|
||||
(VMULLTQ_INT_M_S "vmullt") (VMULLTQ_INT_M_U "vmullt")
|
||||
(VMULLTQ_INT_S "vmullt") (VMULLTQ_INT_U "vmullt")
|
||||
(VMULLTQ_POLY_P "vmullt")
|
||||
(VMULQ_M_N_S "vmul") (VMULQ_M_N_U "vmul") (VMULQ_M_N_F "vmul")
|
||||
(VMULQ_M_S "vmul") (VMULQ_M_U "vmul") (VMULQ_M_F "vmul")
|
||||
(VMULQ_N_S "vmul") (VMULQ_N_U "vmul") (VMULQ_N_F "vmul")
|
||||
|
|
@ -1209,14 +1217,13 @@
|
|||
(VSUBQ_M_N_S "vsub") (VSUBQ_M_N_U "vsub") (VSUBQ_M_N_F "vsub")
|
||||
(VSUBQ_M_S "vsub") (VSUBQ_M_U "vsub") (VSUBQ_M_F "vsub")
|
||||
(VSUBQ_N_S "vsub") (VSUBQ_N_U "vsub") (VSUBQ_N_F "vsub")
|
||||
(UNSPEC_VCMLA "vcmla") (UNSPEC_VCMLA90 "vcmla") (UNSPEC_VCMLA180 "vcmla") (UNSPEC_VCMLA270 "vcmla")
|
||||
])
|
||||
|
||||
(define_int_attr isu [
|
||||
(UNSPEC_VCADD90 "i") (UNSPEC_VCADD270 "i")
|
||||
(VABSQ_M_S "s")
|
||||
(VCADDQ_ROT270_M_U "i") (VCADDQ_ROT270_M_S "i")
|
||||
(VCADDQ_ROT90_M_U "i") (VCADDQ_ROT90_M_S "i")
|
||||
(VCADDQ_ROT270_M "i")
|
||||
(VCADDQ_ROT90_M "i")
|
||||
(VCLSQ_M_S "s")
|
||||
(VCLZQ_M_S "i")
|
||||
(VCLZQ_M_U "i")
|
||||
|
|
@ -1246,6 +1253,8 @@
|
|||
(VMOVNBQ_S "i") (VMOVNBQ_U "i")
|
||||
(VMOVNTQ_M_S "i") (VMOVNTQ_M_U "i")
|
||||
(VMOVNTQ_S "i") (VMOVNTQ_U "i")
|
||||
(VMULLBQ_INT_S "s") (VMULLBQ_INT_U "u")
|
||||
(VMULLTQ_INT_S "s") (VMULLTQ_INT_U "u")
|
||||
(VNEGQ_M_S "s")
|
||||
(VQABSQ_M_S "s")
|
||||
(VQMOVNBQ_M_S "s") (VQMOVNBQ_M_U "u")
|
||||
|
|
@ -2184,11 +2193,9 @@
|
|||
(define_int_attr rot [(UNSPEC_VCADD90 "90")
|
||||
(UNSPEC_VCADD270 "270")
|
||||
(VCADDQ_ROT90_M_F "90")
|
||||
(VCADDQ_ROT90_M_S "90")
|
||||
(VCADDQ_ROT90_M_U "90")
|
||||
(VCADDQ_ROT90_M "90")
|
||||
(VCADDQ_ROT270_M_F "270")
|
||||
(VCADDQ_ROT270_M_S "270")
|
||||
(VCADDQ_ROT270_M_U "270")
|
||||
(VCADDQ_ROT270_M "270")
|
||||
(VHCADDQ_ROT90_S "90")
|
||||
(VHCADDQ_ROT270_S "270")
|
||||
(VHCADDQ_ROT90_M_S "90")
|
||||
|
|
@ -2241,11 +2248,9 @@
|
|||
(define_int_attr mve_rot [(UNSPEC_VCADD90 "_rot90")
|
||||
(UNSPEC_VCADD270 "_rot270")
|
||||
(VCADDQ_ROT90_M_F "_rot90")
|
||||
(VCADDQ_ROT90_M_S "_rot90")
|
||||
(VCADDQ_ROT90_M_U "_rot90")
|
||||
(VCADDQ_ROT90_M "_rot90")
|
||||
(VCADDQ_ROT270_M_F "_rot270")
|
||||
(VCADDQ_ROT270_M_S "_rot270")
|
||||
(VCADDQ_ROT270_M_U "_rot270")
|
||||
(VCADDQ_ROT270_M "_rot270")
|
||||
(VHCADDQ_ROT90_S "_rot90")
|
||||
(VHCADDQ_ROT270_S "_rot270")
|
||||
(VHCADDQ_ROT90_M_S "_rot90")
|
||||
|
|
@ -2330,6 +2335,10 @@
|
|||
(VMLADAVQ_U "u") (VMULHQ_S "s") (VMULHQ_U "u")
|
||||
(VMULLBQ_INT_S "s") (VMULLBQ_INT_U "u") (VQADDQ_S "s")
|
||||
(VMULLTQ_INT_S "s") (VMULLTQ_INT_U "u") (VQADDQ_U "u")
|
||||
(VMULLBQ_POLY_P "p")
|
||||
(VMULLTQ_POLY_P "p")
|
||||
(VMULLBQ_POLY_M_P "p")
|
||||
(VMULLTQ_POLY_M_P "p")
|
||||
(VMULQ_N_S "s") (VMULQ_N_U "u") (VMULQ_S "s")
|
||||
(VMULQ_U "u")
|
||||
(VQADDQ_N_S "s") (VQADDQ_N_U "u")
|
||||
|
|
@ -2403,7 +2412,7 @@
|
|||
(VCVTQ_M_N_TO_F_U "u") (VADDQ_M_N_U "u")
|
||||
(VSHLQ_M_N_S "s") (VMAXQ_M_U "u") (VHSUBQ_M_N_U "u")
|
||||
(VMULQ_M_N_S "s") (VQSHLQ_M_U "u") (VRHADDQ_M_S "s")
|
||||
(VEORQ_M_U "u") (VSHRQ_M_N_U "u") (VCADDQ_ROT90_M_U "u")
|
||||
(VEORQ_M_U "u") (VSHRQ_M_N_U "u")
|
||||
(VMLADAVAQ_P_U "u") (VEORQ_M_S "s") (VBRSRQ_M_N_S "s")
|
||||
(VMULQ_M_U "u") (VQRDMLAHQ_M_N_S "s") (VHSUBQ_M_N_S "s")
|
||||
(VQRSHLQ_M_S "s") (VMULQ_M_N_U "u")
|
||||
|
|
@ -2412,17 +2421,17 @@
|
|||
(VMULLBQ_INT_M_U "u") (VSHLQ_M_N_U "u") (VQSUBQ_M_U "u")
|
||||
(VQDMLASHQ_M_N_S "s")
|
||||
(VQRDMLASHQ_M_N_U "u") (VRSHRQ_M_N_S "s")
|
||||
(VORNQ_M_S "s") (VCADDQ_ROT270_M_S "s") (VRHADDQ_M_U "u")
|
||||
(VORNQ_M_S "s") (VCADDQ_ROT270_M "") (VRHADDQ_M_U "u")
|
||||
(VRSHRQ_M_N_U "u") (VMLASQ_M_N_U "u") (VHSUBQ_M_U "u")
|
||||
(VQSUBQ_M_N_S "s") (VMULLTQ_INT_M_S "s")
|
||||
(VORRQ_M_S "s") (VQDMLAHQ_M_N_U "u") (VRSHLQ_M_S "s")
|
||||
(VHADDQ_M_U "u") (VHADDQ_M_N_S "s") (VMULLTQ_INT_M_U "u")
|
||||
(VORRQ_M_U "u") (VHADDQ_M_S "s") (VHADDQ_M_N_U "u")
|
||||
(VQDMLAHQ_M_N_S "s") (VMAXQ_M_S "s") (VORNQ_M_U "u")
|
||||
(VCADDQ_ROT270_M_U "u") (VQADDQ_M_U "u")
|
||||
(VQADDQ_M_U "u")
|
||||
(VQRDMLASHQ_M_N_S "s") (VBICQ_M_U "u") (VMINQ_M_U "u")
|
||||
(VSUBQ_M_N_S "s") (VMULLBQ_INT_M_S "s") (VQSUBQ_M_S "s")
|
||||
(VCADDQ_ROT90_M_S "s") (VRMULHQ_M_S "s") (VANDQ_M_U "u")
|
||||
(VCADDQ_ROT90_M "") (VRMULHQ_M_S "s") (VANDQ_M_U "u")
|
||||
(VMULHQ_M_S "s") (VADDQ_M_S "s") (VQRDMLAHQ_M_N_U "u")
|
||||
(VMLASQ_M_N_S "s") (VHSUBQ_M_S "s") (VRMULHQ_M_U "u")
|
||||
(VQADDQ_M_N_S "s") (VSHRQ_M_N_S "s") (VANDQ_M_S "s")
|
||||
|
|
@ -2713,8 +2722,8 @@
|
|||
(define_int_iterator VMINVQ [VMINVQ_U VMINVQ_S])
|
||||
(define_int_iterator VMLADAVQ [VMLADAVQ_U VMLADAVQ_S])
|
||||
(define_int_iterator VMULHQ [VMULHQ_S VMULHQ_U])
|
||||
(define_int_iterator VMULLBQ_INT [VMULLBQ_INT_U VMULLBQ_INT_S])
|
||||
(define_int_iterator VMULLTQ_INT [VMULLTQ_INT_U VMULLTQ_INT_S])
|
||||
(define_int_iterator VMULLxQ_INT [VMULLBQ_INT_U VMULLBQ_INT_S VMULLTQ_INT_U VMULLTQ_INT_S])
|
||||
(define_int_iterator VMULLxQ_POLY [VMULLBQ_POLY_P VMULLTQ_POLY_P])
|
||||
(define_int_iterator VMULQ [VMULQ_U VMULQ_S])
|
||||
(define_int_iterator VMULQ_N [VMULQ_N_U VMULQ_N_S])
|
||||
(define_int_iterator VQADDQ [VQADDQ_U VQADDQ_S])
|
||||
|
|
@ -2815,7 +2824,8 @@
|
|||
(define_int_iterator VSLIQ_M_N [VSLIQ_M_N_U VSLIQ_M_N_S])
|
||||
(define_int_iterator VRSHLQ_M [VRSHLQ_M_S VRSHLQ_M_U])
|
||||
(define_int_iterator VMINQ_M [VMINQ_M_S VMINQ_M_U])
|
||||
(define_int_iterator VMULLBQ_INT_M [VMULLBQ_INT_M_U VMULLBQ_INT_M_S])
|
||||
(define_int_iterator VMULLxQ_INT_M [VMULLBQ_INT_M_U VMULLBQ_INT_M_S VMULLTQ_INT_M_U VMULLTQ_INT_M_S])
|
||||
(define_int_iterator VMULLxQ_POLY_M [VMULLBQ_POLY_M_P VMULLTQ_POLY_M_P])
|
||||
(define_int_iterator VMULHQ_M [VMULHQ_M_S VMULHQ_M_U])
|
||||
(define_int_iterator VMULQ_M [VMULQ_M_S VMULQ_M_U])
|
||||
(define_int_iterator VHSUBQ_M_N [VHSUBQ_M_N_S VHSUBQ_M_N_U])
|
||||
|
|
@ -2834,7 +2844,7 @@
|
|||
(define_int_iterator VSHLQ_M_N [VSHLQ_M_N_S VSHLQ_M_N_U])
|
||||
(define_int_iterator VCADDQ_M_F [VCADDQ_ROT90_M_F VCADDQ_ROT270_M_F])
|
||||
(define_int_iterator VxCADDQ [UNSPEC_VCADD90 UNSPEC_VCADD270 VHCADDQ_ROT90_S VHCADDQ_ROT270_S])
|
||||
(define_int_iterator VxCADDQ_M [VHCADDQ_ROT90_M_S VHCADDQ_ROT270_M_S VCADDQ_ROT90_M_U VCADDQ_ROT90_M_S VCADDQ_ROT270_M_U VCADDQ_ROT270_M_S])
|
||||
(define_int_iterator VxCADDQ_M [VHCADDQ_ROT90_M_S VHCADDQ_ROT270_M_S VCADDQ_ROT90_M VCADDQ_ROT270_M])
|
||||
(define_int_iterator VQRSHLQ_M [VQRSHLQ_M_U VQRSHLQ_M_S])
|
||||
(define_int_iterator VQADDQ_M_N [VQADDQ_M_N_U VQADDQ_M_N_S])
|
||||
(define_int_iterator VADDQ_M_N [VADDQ_M_N_S VADDQ_M_N_U])
|
||||
|
|
@ -2844,7 +2854,6 @@
|
|||
(define_int_iterator VMLADAVAQ_P [VMLADAVAQ_P_U VMLADAVAQ_P_S])
|
||||
(define_int_iterator VBRSRQ_M_N [VBRSRQ_M_N_U VBRSRQ_M_N_S])
|
||||
(define_int_iterator VMULQ_M_N [VMULQ_M_N_U VMULQ_M_N_S])
|
||||
(define_int_iterator VMULLTQ_INT_M [VMULLTQ_INT_M_S VMULLTQ_INT_M_U])
|
||||
(define_int_iterator VEORQ_M [VEORQ_M_S VEORQ_M_U])
|
||||
(define_int_iterator VSHRQ_M_N [VSHRQ_M_N_S VSHRQ_M_N_U])
|
||||
(define_int_iterator VSUBQ_M_N [VSUBQ_M_N_S VSUBQ_M_N_U])
|
||||
|
|
|
|||
|
|
@ -839,8 +839,8 @@
|
|||
])
|
||||
|
||||
;;
|
||||
;; [vcaddq_rot90_s, vcadd_rot90_u]
|
||||
;; [vcaddq_rot270_s, vcadd_rot270_u]
|
||||
;; [vcaddq_rot90_s, vcaddq_rot90_u]
|
||||
;; [vcaddq_rot270_s, vcaddq_rot270_u]
|
||||
;; [vhcaddq_rot90_s]
|
||||
;; [vhcaddq_rot270_s]
|
||||
;;
|
||||
|
|
@ -976,32 +976,18 @@
|
|||
])
|
||||
|
||||
;;
|
||||
;; [vmullbq_int_u, vmullbq_int_s])
|
||||
;; [vmullbq_int_u, vmullbq_int_s]
|
||||
;; [vmulltq_int_u, vmulltq_int_s]
|
||||
;;
|
||||
(define_insn "mve_vmullbq_int_<supf><mode>"
|
||||
(define_insn "@mve_<mve_insn>q_int_<supf><mode>"
|
||||
[
|
||||
(set (match_operand:<V_double_width> 0 "s_register_operand" "<earlyclobber_32>")
|
||||
(unspec:<V_double_width> [(match_operand:MVE_2 1 "s_register_operand" "w")
|
||||
(match_operand:MVE_2 2 "s_register_operand" "w")]
|
||||
VMULLBQ_INT))
|
||||
VMULLxQ_INT))
|
||||
]
|
||||
"TARGET_HAVE_MVE"
|
||||
"vmullb.<supf>%#<V_sz_elem>\t%q0, %q1, %q2"
|
||||
[(set_attr "type" "mve_move")
|
||||
])
|
||||
|
||||
;;
|
||||
;; [vmulltq_int_u, vmulltq_int_s])
|
||||
;;
|
||||
(define_insn "mve_vmulltq_int_<supf><mode>"
|
||||
[
|
||||
(set (match_operand:<V_double_width> 0 "s_register_operand" "<earlyclobber_32>")
|
||||
(unspec:<V_double_width> [(match_operand:MVE_2 1 "s_register_operand" "w")
|
||||
(match_operand:MVE_2 2 "s_register_operand" "w")]
|
||||
VMULLTQ_INT))
|
||||
]
|
||||
"TARGET_HAVE_MVE"
|
||||
"vmullt.<supf>%#<V_sz_elem>\t%q0, %q1, %q2"
|
||||
"<mve_insn>.<isu>%#<V_sz_elem>\t%q0, %q1, %q2"
|
||||
[(set_attr "type" "mve_move")
|
||||
])
|
||||
|
||||
|
|
@ -1528,32 +1514,18 @@
|
|||
])
|
||||
|
||||
;;
|
||||
;; [vmulltq_poly_p])
|
||||
;; [vmulltq_poly_p]
|
||||
;; [vmullbq_poly_p]
|
||||
;;
|
||||
(define_insn "mve_vmulltq_poly_p<mode>"
|
||||
(define_insn "@mve_<mve_insn>q_poly_<supf><mode>"
|
||||
[
|
||||
(set (match_operand:<V_double_width> 0 "s_register_operand" "=w")
|
||||
(unspec:<V_double_width> [(match_operand:MVE_3 1 "s_register_operand" "w")
|
||||
(match_operand:MVE_3 2 "s_register_operand" "w")]
|
||||
VMULLTQ_POLY_P))
|
||||
VMULLxQ_POLY))
|
||||
]
|
||||
"TARGET_HAVE_MVE"
|
||||
"vmullt.p%#<V_sz_elem>\t%q0, %q1, %q2"
|
||||
[(set_attr "type" "mve_move")
|
||||
])
|
||||
|
||||
;;
|
||||
;; [vmullbq_poly_p])
|
||||
;;
|
||||
(define_insn "mve_vmullbq_poly_p<mode>"
|
||||
[
|
||||
(set (match_operand:<V_double_width> 0 "s_register_operand" "=w")
|
||||
(unspec:<V_double_width> [(match_operand:MVE_3 1 "s_register_operand" "w")
|
||||
(match_operand:MVE_3 2 "s_register_operand" "w")]
|
||||
VMULLBQ_POLY_P))
|
||||
]
|
||||
"TARGET_HAVE_MVE"
|
||||
"vmullb.p%#<V_sz_elem>\t%q0, %q1, %q2"
|
||||
"<mve_insn>.<supf>%#<V_sz_elem>\t%q0, %q1, %q2"
|
||||
[(set_attr "type" "mve_move")
|
||||
])
|
||||
|
||||
|
|
@ -2816,36 +2788,20 @@
|
|||
(set_attr "length""8")])
|
||||
|
||||
;;
|
||||
;; [vmullbq_int_m_u, vmullbq_int_m_s])
|
||||
;; [vmullbq_int_m_u, vmullbq_int_m_s]
|
||||
;; [vmulltq_int_m_s, vmulltq_int_m_u]
|
||||
;;
|
||||
(define_insn "mve_vmullbq_int_m_<supf><mode>"
|
||||
(define_insn "@mve_<mve_insn>q_int_m_<supf><mode>"
|
||||
[
|
||||
(set (match_operand:<V_double_width> 0 "s_register_operand" "<earlyclobber_32>")
|
||||
(unspec:<V_double_width> [(match_operand:<V_double_width> 1 "s_register_operand" "0")
|
||||
(match_operand:MVE_2 2 "s_register_operand" "w")
|
||||
(match_operand:MVE_2 3 "s_register_operand" "w")
|
||||
(match_operand:<MVE_VPRED> 4 "vpr_register_operand" "Up")]
|
||||
VMULLBQ_INT_M))
|
||||
VMULLxQ_INT_M))
|
||||
]
|
||||
"TARGET_HAVE_MVE"
|
||||
"vpst\;vmullbt.<supf>%#<V_sz_elem> %q0, %q2, %q3"
|
||||
[(set_attr "type" "mve_move")
|
||||
(set_attr "length""8")])
|
||||
|
||||
;;
|
||||
;; [vmulltq_int_m_s, vmulltq_int_m_u])
|
||||
;;
|
||||
(define_insn "mve_vmulltq_int_m_<supf><mode>"
|
||||
[
|
||||
(set (match_operand:<V_double_width> 0 "s_register_operand" "<earlyclobber_32>")
|
||||
(unspec:<V_double_width> [(match_operand:<V_double_width> 1 "s_register_operand" "0")
|
||||
(match_operand:MVE_2 2 "s_register_operand" "w")
|
||||
(match_operand:MVE_2 3 "s_register_operand" "w")
|
||||
(match_operand:<MVE_VPRED> 4 "vpr_register_operand" "Up")]
|
||||
VMULLTQ_INT_M))
|
||||
]
|
||||
"TARGET_HAVE_MVE"
|
||||
"vpst\;vmulltt.<supf>%#<V_sz_elem> %q0, %q2, %q3"
|
||||
"vpst\;<mve_insn>t.<supf>%#<V_sz_elem>\t%q0, %q2, %q3"
|
||||
[(set_attr "type" "mve_move")
|
||||
(set_attr "length""8")])
|
||||
|
||||
|
|
@ -3006,36 +2962,20 @@
|
|||
(set_attr "length""8")])
|
||||
|
||||
;;
|
||||
;; [vmullbq_poly_m_p])
|
||||
;; [vmullbq_poly_m_p]
|
||||
;; [vmulltq_poly_m_p]
|
||||
;;
|
||||
(define_insn "mve_vmullbq_poly_m_p<mode>"
|
||||
(define_insn "@mve_<mve_insn>q_poly_m_<supf><mode>"
|
||||
[
|
||||
(set (match_operand:<V_double_width> 0 "s_register_operand" "=w")
|
||||
(unspec:<V_double_width> [(match_operand:<V_double_width> 1 "s_register_operand" "0")
|
||||
(match_operand:MVE_3 2 "s_register_operand" "w")
|
||||
(match_operand:MVE_3 3 "s_register_operand" "w")
|
||||
(match_operand:<MVE_VPRED> 4 "vpr_register_operand" "Up")]
|
||||
VMULLBQ_POLY_M_P))
|
||||
VMULLxQ_POLY_M))
|
||||
]
|
||||
"TARGET_HAVE_MVE"
|
||||
"vpst\;vmullbt.p%#<V_sz_elem>\t%q0, %q2, %q3"
|
||||
[(set_attr "type" "mve_move")
|
||||
(set_attr "length""8")])
|
||||
|
||||
;;
|
||||
;; [vmulltq_poly_m_p])
|
||||
;;
|
||||
(define_insn "mve_vmulltq_poly_m_p<mode>"
|
||||
[
|
||||
(set (match_operand:<V_double_width> 0 "s_register_operand" "=w")
|
||||
(unspec:<V_double_width> [(match_operand:<V_double_width> 1 "s_register_operand" "0")
|
||||
(match_operand:MVE_3 2 "s_register_operand" "w")
|
||||
(match_operand:MVE_3 3 "s_register_operand" "w")
|
||||
(match_operand:<MVE_VPRED> 4 "vpr_register_operand" "Up")]
|
||||
VMULLTQ_POLY_M_P))
|
||||
]
|
||||
"TARGET_HAVE_MVE"
|
||||
"vpst\;vmulltt.p%#<V_sz_elem>\t%q0, %q2, %q3"
|
||||
"vpst\;<mve_insn>t.<supf>%#<V_sz_elem>\t%q0, %q2, %q3"
|
||||
[(set_attr "type" "mve_move")
|
||||
(set_attr "length""8")])
|
||||
|
||||
|
|
|
|||
|
|
@ -995,8 +995,7 @@
|
|||
VMAXQ_M_U
|
||||
VQRDMLAHQ_M_N_U
|
||||
VCADDQ_ROT270_M_F
|
||||
VCADDQ_ROT270_M_U
|
||||
VCADDQ_ROT270_M_S
|
||||
VCADDQ_ROT270_M
|
||||
VQRSHLQ_M_S
|
||||
VMULQ_M_F
|
||||
VRHADDQ_M_U
|
||||
|
|
@ -1050,8 +1049,7 @@
|
|||
VSLIQ_M_N_S
|
||||
VQSHLQ_M_U
|
||||
VQSHLQ_M_S
|
||||
VCADDQ_ROT90_M_U
|
||||
VCADDQ_ROT90_M_S
|
||||
VCADDQ_ROT90_M
|
||||
VORNQ_M_U
|
||||
VORNQ_M_S
|
||||
VQSHLQ_M_N_S
|
||||
|
|
|
|||
|
|
@ -13433,8 +13433,8 @@ avr_reg_ok_for_pgm_addr (rtx reg, bool strict)
|
|||
/* Implement `TARGET_ADDR_SPACE_LEGITIMATE_ADDRESS_P'. */
|
||||
|
||||
static bool
|
||||
avr_addr_space_legitimate_address_p (machine_mode mode, rtx x,
|
||||
bool strict, addr_space_t as)
|
||||
avr_addr_space_legitimate_address_p (machine_mode mode, rtx x, bool strict,
|
||||
addr_space_t as, code_helper = ERROR_MARK)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -2718,7 +2718,8 @@ bfin_valid_reg_p (unsigned int regno, int strict, machine_mode mode,
|
|||
*/
|
||||
|
||||
static bool
|
||||
bfin_legitimate_address_p (machine_mode mode, rtx x, bool strict)
|
||||
bfin_legitimate_address_p (machine_mode mode, rtx x, bool strict,
|
||||
code_helper = ERROR_MARK)
|
||||
{
|
||||
switch (GET_CODE (x)) {
|
||||
case REG:
|
||||
|
|
|
|||
|
|
@ -76,10 +76,6 @@ struct GTY(()) machine_function
|
|||
{
|
||||
/* Number of bytes saved on the stack for local variables. */
|
||||
int local_vars_size;
|
||||
|
||||
/* Number of bytes saved on the stack for callee-saved
|
||||
registers. */
|
||||
int callee_saved_reg_size;
|
||||
};
|
||||
|
||||
/* Handle an attribute requiring a FUNCTION_DECL;
|
||||
|
|
@ -158,6 +154,10 @@ static const struct attribute_spec bpf_attribute_table[] =
|
|||
{ "preserve_access_index", 0, -1, false, true, false, true,
|
||||
bpf_handle_preserve_access_index_attribute, NULL },
|
||||
|
||||
/* Support for `naked' function attribute. */
|
||||
{ "naked", 0, 1, false, false, false, false,
|
||||
bpf_handle_fndecl_attribute, NULL },
|
||||
|
||||
/* The last attribute spec is set to be NULL. */
|
||||
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
|
||||
};
|
||||
|
|
@ -339,6 +339,21 @@ bpf_function_value_regno_p (const unsigned int regno)
|
|||
#undef TARGET_FUNCTION_VALUE_REGNO_P
|
||||
#define TARGET_FUNCTION_VALUE_REGNO_P bpf_function_value_regno_p
|
||||
|
||||
|
||||
/* Determine whether to warn about lack of return statement in a
|
||||
function. */
|
||||
|
||||
static bool
|
||||
bpf_warn_func_return (tree decl)
|
||||
{
|
||||
/* Naked functions are implemented entirely in assembly, including
|
||||
the return instructions. */
|
||||
return lookup_attribute ("naked", DECL_ATTRIBUTES (decl)) == NULL_TREE;
|
||||
}
|
||||
|
||||
#undef TARGET_WARN_FUNC_RETURN
|
||||
#define TARGET_WARN_FUNC_RETURN bpf_warn_func_return
|
||||
|
||||
/* Compute the size of the function's stack frame, including the local
|
||||
area and the register-save area. */
|
||||
|
||||
|
|
@ -346,7 +361,7 @@ static void
|
|||
bpf_compute_frame_layout (void)
|
||||
{
|
||||
int stack_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
|
||||
int padding_locals, regno;
|
||||
int padding_locals;
|
||||
|
||||
/* Set the space used in the stack by local variables. This is
|
||||
rounded up to respect the minimum stack alignment. */
|
||||
|
|
@ -358,23 +373,9 @@ bpf_compute_frame_layout (void)
|
|||
|
||||
cfun->machine->local_vars_size += padding_locals;
|
||||
|
||||
if (TARGET_XBPF)
|
||||
{
|
||||
/* Set the space used in the stack by callee-saved used
|
||||
registers in the current function. There is no need to round
|
||||
up, since the registers are all 8 bytes wide. */
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
if ((df_regs_ever_live_p (regno)
|
||||
&& !call_used_or_fixed_reg_p (regno))
|
||||
|| (cfun->calls_alloca
|
||||
&& regno == STACK_POINTER_REGNUM))
|
||||
cfun->machine->callee_saved_reg_size += 8;
|
||||
}
|
||||
|
||||
/* Check that the total size of the frame doesn't exceed the limit
|
||||
imposed by eBPF. */
|
||||
if ((cfun->machine->local_vars_size
|
||||
+ cfun->machine->callee_saved_reg_size) > bpf_frame_limit)
|
||||
if (cfun->machine->local_vars_size > bpf_frame_limit)
|
||||
{
|
||||
static int stack_limit_exceeded = 0;
|
||||
|
||||
|
|
@ -393,69 +394,22 @@ bpf_compute_frame_layout (void)
|
|||
void
|
||||
bpf_expand_prologue (void)
|
||||
{
|
||||
HOST_WIDE_INT size;
|
||||
|
||||
size = (cfun->machine->local_vars_size
|
||||
+ cfun->machine->callee_saved_reg_size);
|
||||
|
||||
/* The BPF "hardware" provides a fresh new set of registers for each
|
||||
called function, some of which are initialized to the values of
|
||||
the arguments passed in the first five registers. In doing so,
|
||||
it saves the values of the registers of the caller, and restored
|
||||
it saves the values of the registers of the caller, and restores
|
||||
them upon returning. Therefore, there is no need to save the
|
||||
callee-saved registers here. What is worse, the kernel
|
||||
implementation refuses to run programs in which registers are
|
||||
referred before being initialized. */
|
||||
if (TARGET_XBPF)
|
||||
{
|
||||
int regno;
|
||||
int fp_offset = -cfun->machine->local_vars_size;
|
||||
callee-saved registers here. In fact, the kernel implementation
|
||||
refuses to run programs in which registers are referred before
|
||||
being initialized. */
|
||||
|
||||
/* Save callee-saved hard registes. The register-save-area
|
||||
starts right after the local variables. */
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
{
|
||||
if ((df_regs_ever_live_p (regno)
|
||||
&& !call_used_or_fixed_reg_p (regno))
|
||||
|| (cfun->calls_alloca
|
||||
&& regno == STACK_POINTER_REGNUM))
|
||||
{
|
||||
rtx mem;
|
||||
/* BPF does not support functions that allocate stack space
|
||||
dynamically. This should have been checked already and an error
|
||||
emitted. */
|
||||
gcc_assert (!cfun->calls_alloca);
|
||||
|
||||
if (!IN_RANGE (fp_offset, -1 - 0x7fff, 0x7fff))
|
||||
/* This has been already reported as an error in
|
||||
bpf_compute_frame_layout. */
|
||||
break;
|
||||
else
|
||||
{
|
||||
mem = gen_frame_mem (DImode,
|
||||
plus_constant (DImode,
|
||||
hard_frame_pointer_rtx,
|
||||
fp_offset - 8));
|
||||
emit_move_insn (mem, gen_rtx_REG (DImode, regno));
|
||||
fp_offset -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the stack pointer, if the function allocates space
|
||||
dynamically. Note that the value of %sp should be directly
|
||||
derived from %fp, for the kernel verifier to track it as a stack
|
||||
accessor. */
|
||||
if (cfun->calls_alloca)
|
||||
{
|
||||
emit_move_insn (stack_pointer_rtx,
|
||||
hard_frame_pointer_rtx);
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
emit_insn (gen_rtx_SET (stack_pointer_rtx,
|
||||
gen_rtx_PLUS (Pmode,
|
||||
stack_pointer_rtx,
|
||||
GEN_INT (-size))));
|
||||
}
|
||||
}
|
||||
/* If we ever need to have a proper prologue here, please mind the
|
||||
`naked' function attribute. */
|
||||
}
|
||||
|
||||
/* Expand to the instructions in a function epilogue. This function
|
||||
|
|
@ -466,37 +420,9 @@ bpf_expand_epilogue (void)
|
|||
{
|
||||
/* See note in bpf_expand_prologue for an explanation on why we are
|
||||
not restoring callee-saved registers in BPF. */
|
||||
if (TARGET_XBPF)
|
||||
{
|
||||
int regno;
|
||||
int fp_offset = -cfun->machine->local_vars_size;
|
||||
|
||||
/* Restore callee-saved hard registes from the stack. */
|
||||
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
|
||||
{
|
||||
if ((df_regs_ever_live_p (regno)
|
||||
&& !call_used_or_fixed_reg_p (regno))
|
||||
|| (cfun->calls_alloca
|
||||
&& regno == STACK_POINTER_REGNUM))
|
||||
{
|
||||
rtx mem;
|
||||
|
||||
if (!IN_RANGE (fp_offset, -1 - 0x7fff, 0x7fff))
|
||||
/* This has been already reported as an error in
|
||||
bpf_compute_frame_layout. */
|
||||
break;
|
||||
else
|
||||
{
|
||||
mem = gen_frame_mem (DImode,
|
||||
plus_constant (DImode,
|
||||
hard_frame_pointer_rtx,
|
||||
fp_offset - 8));
|
||||
emit_move_insn (gen_rtx_REG (DImode, regno), mem);
|
||||
fp_offset -= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If we ever need to do anything else than just generating a return
|
||||
instruction here, please mind the `naked' function attribute. */
|
||||
|
||||
emit_jump_insn (gen_exit ());
|
||||
}
|
||||
|
|
@ -543,11 +469,10 @@ bpf_initial_elimination_offset (int from, int to)
|
|||
{
|
||||
HOST_WIDE_INT ret;
|
||||
|
||||
if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
|
||||
ret = (cfun->machine->local_vars_size
|
||||
+ cfun->machine->callee_saved_reg_size);
|
||||
else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
|
||||
if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
|
||||
ret = 0;
|
||||
else if (from == STACK_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
|
||||
ret = -(cfun->machine->local_vars_size);
|
||||
else
|
||||
gcc_unreachable ();
|
||||
|
||||
|
|
@ -625,7 +550,8 @@ bpf_address_base_p (rtx x, bool strict)
|
|||
static bool
|
||||
bpf_legitimate_address_p (machine_mode mode,
|
||||
rtx x,
|
||||
bool strict)
|
||||
bool strict,
|
||||
code_helper = ERROR_MARK)
|
||||
{
|
||||
switch (GET_CODE (x))
|
||||
{
|
||||
|
|
@ -731,7 +657,14 @@ bpf_function_arg_advance (cumulative_args_t ca,
|
|||
unsigned num_words = CEIL (num_bytes, UNITS_PER_WORD);
|
||||
|
||||
if (*cum <= 5 && *cum + num_words > 5)
|
||||
error ("too many function arguments for eBPF");
|
||||
{
|
||||
/* Too many arguments for BPF. However, if the function is
|
||||
gonna be inline for sure, we let it pass. Otherwise, issue
|
||||
an error. */
|
||||
if (!lookup_attribute ("always_inline",
|
||||
DECL_ATTRIBUTES (cfun->decl)))
|
||||
error ("too many function arguments for eBPF");
|
||||
}
|
||||
|
||||
*cum += num_words;
|
||||
}
|
||||
|
|
@ -845,7 +778,7 @@ bpf_print_register (FILE *file, rtx op, int code)
|
|||
fprintf (file, "%s", reg_names[REGNO (op)]);
|
||||
else
|
||||
{
|
||||
if (code == 'w' && GET_MODE (op) == SImode)
|
||||
if (code == 'w' && GET_MODE_SIZE (GET_MODE (op)) <= 4)
|
||||
{
|
||||
if (REGNO (op) == BPF_FP)
|
||||
fprintf (file, "w10");
|
||||
|
|
|
|||
|
|
@ -153,24 +153,27 @@
|
|||
#define BPF_R7 7
|
||||
#define BPF_R8 8
|
||||
#define BPF_R9 9
|
||||
#define BPF_SP BPF_R9
|
||||
#define BPF_R10 10
|
||||
#define BPF_FP BPF_R10
|
||||
#define BPF_R11 11
|
||||
#define BPF_R12 12
|
||||
#define BPF_SP BPF_R12
|
||||
|
||||
/* 11 is not a real eBPF hard register and is eliminated or not used
|
||||
in the final assembler. See below. */
|
||||
|
||||
#define FIRST_PSEUDO_REGISTER 12
|
||||
#define FIRST_PSEUDO_REGISTER 13
|
||||
|
||||
/* The registers %r0..%r8 are available for general allocation.
|
||||
%r9 is the pseudo-stack pointer.
|
||||
%r10 is the stack frame, which is read-only.
|
||||
%r11 (__arg__) is a fake register that always gets eliminated. */
|
||||
%r11 (__arg__) is a fake register that always gets eliminated.
|
||||
%r12 is the pseudo-stack pointer that always gets eliminated. */
|
||||
#define FIXED_REGISTERS \
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}
|
||||
|
||||
/* %r0..%r5 are clobbered by function calls. */
|
||||
#define CALL_USED_REGISTERS \
|
||||
{1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1}
|
||||
{1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1}
|
||||
|
||||
/**** Register Classes. */
|
||||
|
||||
|
|
@ -206,7 +209,7 @@ enum reg_class
|
|||
{ \
|
||||
0x00000000, /* NO_REGS */ \
|
||||
0x00000001, /* R0 */ \
|
||||
0x00000fff, /* ALL_REGS */ \
|
||||
0x00001fff, /* ALL_REGS */ \
|
||||
}
|
||||
|
||||
/* A C expression whose value is a register class containing hard
|
||||
|
|
@ -260,15 +263,15 @@ enum reg_class
|
|||
/*** Registers That Address the Stack Frame. */
|
||||
|
||||
#define FRAME_POINTER_REGNUM 10
|
||||
#define STACK_POINTER_REGNUM 9
|
||||
#define ARG_POINTER_REGNUM 11
|
||||
#define STACK_POINTER_REGNUM 12
|
||||
#define STATIC_CHAIN_REGNUM 8
|
||||
|
||||
/*** Registers elimination. */
|
||||
|
||||
#define ELIMINABLE_REGS \
|
||||
{{ ARG_POINTER_REGNUM, FRAME_POINTER_REGNUM }, \
|
||||
{ ARG_POINTER_REGNUM, STACK_POINTER_REGNUM }}
|
||||
{ STACK_POINTER_REGNUM, FRAME_POINTER_REGNUM }}
|
||||
|
||||
/* Define the offset between two registers, one to be eliminated, and
|
||||
the other its replacement, at the start of a routine. */
|
||||
|
|
@ -444,7 +447,7 @@ enum reg_class
|
|||
|
||||
#define REGISTER_NAMES \
|
||||
{ "%r0", "%r1", "%r2", "%r3", "%r4", "%r5", "%r6", "%r7", \
|
||||
"%r8", "%r9", "%fp", "__arg__" }
|
||||
"%r8", "%r9", "%fp", "__arg__", "__sp__" }
|
||||
|
||||
#define ADDITIONAL_REGISTER_NAMES \
|
||||
{ { "%a", 0 }, { "%ctx", 6 }, { "%r10" , 10 } }
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue