diff options
author | Martin Liska <mliska@suse.cz> | 2021-05-17 15:53:06 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2021-05-17 15:53:06 +0200 |
commit | 6ec928c2ae25f84be1ca022be60dadea5140c155 (patch) | |
tree | e59b3c930c84acb9b96fc18cd13883411af91e27 | |
parent | 9c217a1395dad1fa1cf9c3e3ac6e3a330cb003ae (diff) | |
parent | e0a5daf81f2c79a0275eccd7c1a25349990a7a4d (diff) | |
download | gcc-6ec928c2ae25f84be1ca022be60dadea5140c155.zip gcc-6ec928c2ae25f84be1ca022be60dadea5140c155.tar.gz gcc-6ec928c2ae25f84be1ca022be60dadea5140c155.tar.bz2 |
Merge branch 'master' into devel/sphinx
382 files changed, 11072 insertions, 2557 deletions
@@ -1,3 +1,45 @@ +2021-05-12 Marcel Vollweiler <marcel@codesourcery.com> + + * MAINTAINERS (Write After Approval): Add myself. + +2021-05-11 Martin Liska <mliska@suse.cz> + + * MAINTAINERS: Remove entries for removed + components (HSA, BRIG, libhsail-rt). + Move Pekka to Write After Approval. + +2021-05-11 Martin Liska <mliska@suse.cz> + + * Makefile.def: Remove libhsail-rt. + * Makefile.in: Likewise. + * configure.ac: Likewise. + * configure: Regenerate. + * libhsail-rt/ChangeLog: Removed. + * libhsail-rt/Makefile.am: Removed. + * libhsail-rt/Makefile.in: Removed. + * libhsail-rt/README: Removed. + * libhsail-rt/aclocal.m4: Removed. + * libhsail-rt/configure: Removed. + * libhsail-rt/configure.ac: Removed. + * libhsail-rt/configure.tgt: Removed. + * libhsail-rt/include/internal/fibers.h: Removed. + * libhsail-rt/include/internal/phsa-queue-interface.h: Removed. + * libhsail-rt/include/internal/phsa-rt.h: Removed. + * libhsail-rt/include/internal/workitems.h: Removed. + * libhsail-rt/rt/arithmetic.c: Removed. + * libhsail-rt/rt/atomics.c: Removed. + * libhsail-rt/rt/bitstring.c: Removed. + * libhsail-rt/rt/fbarrier.c: Removed. + * libhsail-rt/rt/fibers.c: Removed. + * libhsail-rt/rt/fp16.c: Removed. + * libhsail-rt/rt/misc.c: Removed. + * libhsail-rt/rt/multimedia.c: Removed. + * libhsail-rt/rt/queue.c: Removed. + * libhsail-rt/rt/sat_arithmetic.c: Removed. + * libhsail-rt/rt/segment.c: Removed. + * libhsail-rt/rt/workitems.c: Removed. + * libhsail-rt/target-config.h.in: Removed. + 2021-05-04 Nick Clifton <nickc@redhat.com> * configure.ac (AC_PROG_CC): Replace with AC_PROG_CC_C99. diff --git a/MAINTAINERS b/MAINTAINERS index 44a51a7..5b10f21 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -643,6 +643,7 @@ Ilya Verbin <iverbin@gmail.com> Andre Vieira <andre.simoesdiasvieira@arm.com> Rasmus Villemoes <rv@rasmusvillemoes.dk> Kugan Vivekanandarajah <kuganv@linaro.org> +Marcel Vollweiler <marcel@codesourcery.com> Ville Voutilainen <ville.voutilainen@gmail.com> Nenad Vukicevic <nenad@intrepid.com> Feng Wang <fengwang@nudt.edu.cn> diff --git a/contrib/ChangeLog b/contrib/ChangeLog index 2d6e29f..8e393b7 100644 --- a/contrib/ChangeLog +++ b/contrib/ChangeLog @@ -1,3 +1,50 @@ +2021-05-14 Martin Liska <mliska@suse.cz> + + * download_prerequisites: Use version 4.1.0. + +2021-05-14 Jakub Jelinek <jakub@redhat.com> + + * gcc-changelog/git_update_version.py: Remove releases/gcc-8 from + active_refs. + +2021-05-14 Martin Liska <mliska@suse.cz> + + * gcc-changelog/git_commit.py: Support Co-Authored-By before + a first ChangeLog entry. + +2021-05-13 Martin Liska <mliska@suse.cz> + + * mklog.py: Put PR entries before all ChangeLog entries + (will be added to all ChangeLog locations by Daily bump script). + * test_mklog.py: Test the new behavior. + +2021-05-12 Martin Liska <mliska@suse.cz> + + * gcc-changelog/git_commit.py: Simplify needle lookup. + * gcc-changelog/git_update_version.py: Pass ref_name to + parse_git_revisions. + +2021-05-11 Martin Liska <mliska@suse.cz> + + * gcc_update: Remove libhsail-rt folder. + * update-copyright.py: Likewise. + +2021-05-11 Martin Liska <mliska@suse.cz> + + * gcc-changelog/git_check_commit.py: Remove --non-strict-mode. + * gcc-changelog/git_commit.py: Remove strict mode. + * gcc-changelog/git_email.py: Likewise. + * gcc-changelog/git_repository.py: Likewise. + * gcc-changelog/test_email.py: Likewise. + * gcc-changelog/test_patches.txt: Update patches so that they + don't contain a ChangeLog file changes. + +2021-05-11 Martin Liska <mliska@suse.cz> + + * gcc-changelog/git_commit.py: Remove ChangeLog locations + based on ref_name. + * gcc-changelog/git_repository.py: Likewise. + 2021-05-10 Martin Liska <mliska@suse.cz> * gcc-changelog/git_email.py: Remove newlines when --help diff --git a/contrib/download_prerequisites b/contrib/download_prerequisites index 7d0c4b5..51e715f 100755 --- a/contrib/download_prerequisites +++ b/contrib/download_prerequisites @@ -28,7 +28,7 @@ version='(unversioned)' # `contrib/prerequisites.md5` with the new checksums. gmp='gmp-6.1.0.tar.bz2' -mpfr='mpfr-3.1.4.tar.bz2' +mpfr='mpfr-3.1.6.tar.bz2' mpc='mpc-1.0.3.tar.gz' isl='isl-0.18.tar.bz2' diff --git a/contrib/gcc-changelog/git_commit.py b/contrib/gcc-changelog/git_commit.py index c70279e..4958ab9 100755 --- a/contrib/gcc-changelog/git_commit.py +++ b/contrib/gcc-changelog/git_commit.py @@ -394,7 +394,7 @@ class GitCommit: self.changelog_locations = list(default_changelog_locations) if ref_name: version = sys.maxsize - if ref_name.startswith('refs/heads/releases/gcc-'): + if 'releases/gcc-' in ref_name: version = int(ref_name.split('-')[-1]) if version >= 12: # HSA and BRIG were removed in GCC 12 @@ -409,7 +409,8 @@ class GitCommit: continue if (changelog_regex.match(b) or self.find_changelog_location(b) or star_prefix_regex.match(b) or pr_regex.match(b) - or dr_regex.match(b) or author_line_regex.match(b)): + or dr_regex.match(b) or author_line_regex.match(b) + or b.lower().startswith(CO_AUTHORED_BY_PREFIX)): self.changes = body[i:] return if not all_are_ignored: diff --git a/contrib/gcc-changelog/git_update_version.py b/contrib/gcc-changelog/git_update_version.py index 9bc41bb..0508f19 100755 --- a/contrib/gcc-changelog/git_update_version.py +++ b/contrib/gcc-changelog/git_update_version.py @@ -57,7 +57,7 @@ def prepend_to_changelog_files(repo, folder, git_commit, add_to_git): repo.git.add(full_path) -active_refs = ['master', 'releases/gcc-8', 'releases/gcc-9', 'releases/gcc-10', +active_refs = ['master', 'releases/gcc-9', 'releases/gcc-10', 'releases/gcc-11'] parser = argparse.ArgumentParser(description='Update DATESTAMP and generate ' @@ -78,7 +78,7 @@ repo = Repo(args.git_path) origin = repo.remotes['origin'] -def update_current_branch(): +def update_current_branch(ref_name): commit = repo.head.commit commit_count = 1 while commit: @@ -101,7 +101,7 @@ def update_current_branch(): if len(head.parents) == 2: head = head.parents[1] commits = parse_git_revisions(args.git_path, '%s..%s' - % (commit.hexsha, head.hexsha)) + % (commit.hexsha, head.hexsha), ref_name) commits = [c for c in commits if c.info.hexsha not in IGNORED_COMMITS] for git_commit in reversed(commits): prepend_to_changelog_files(repo, args.git_path, git_commit, @@ -145,6 +145,6 @@ else: branch.checkout() origin.pull(rebase=True) print('branch pulled and checked out') - update_current_branch() + update_current_branch(name) assert not repo.index.diff(None) print('branch is done\n', flush=True) diff --git a/contrib/mklog.py b/contrib/mklog.py index 1604f05..5c93c70 100755 --- a/contrib/mklog.py +++ b/contrib/mklog.py @@ -169,13 +169,19 @@ def generate_changelog(data, no_functions=False, fill_pr_titles=False): if fill_pr_titles: out += get_pr_titles(prs) + # print list of PR entries before ChangeLog entries + if prs: + if not out: + out += '\n' + for pr in prs: + out += '\t%s\n' % pr + out += '\n' + # sort ChangeLog so that 'testsuite' is at the end for changelog in sorted(changelog_list, key=lambda x: 'testsuite' in x): files = changelogs[changelog] out += '%s:\n' % os.path.join(changelog, 'ChangeLog') out += '\n' - for pr in prs: - out += '\t%s\n' % pr # new and deleted files should be at the end for file in sorted(files, key=sort_changelog_files): assert file.path.startswith(changelog) diff --git a/contrib/prerequisites.md5 b/contrib/prerequisites.md5 index cc71e0f..272f7a4 100644 --- a/contrib/prerequisites.md5 +++ b/contrib/prerequisites.md5 @@ -1,4 +1,4 @@ 86ee6e54ebfc4a90b643a65e402c4048 gmp-6.1.0.tar.bz2 -b8a2f6b0e68bef46e53da2ac439e1cf4 mpfr-3.1.4.tar.bz2 +320c28198def956aeacdb240b46b8969 mpfr-3.1.6.tar.bz2 d6a1d5f8ddea3abd2cc3e98f58352d26 mpc-1.0.3.tar.gz 11436d6b205e516635b666090b94ab32 isl-0.18.tar.bz2 diff --git a/contrib/prerequisites.sha512 b/contrib/prerequisites.sha512 index cf6b93b..7160145 100644 --- a/contrib/prerequisites.sha512 +++ b/contrib/prerequisites.sha512 @@ -1,4 +1,4 @@ 3c82aeab9c1596d4da8afac2eec38e429e84f3211e1a572cf8fd2b546493c44c039b922a1133eaaa48bd7f3e11dbe795a384e21ed95cbe3ecc58d7ac02246117 gmp-6.1.0.tar.bz2 -51066066ff2c12ed2198605ecf68846b0c96b548adafa5b80e0c786d0df488411a5e8973358fce7192dc977ad4e68414cf14500e3c39746de62465eb145bb819 mpfr-3.1.4.tar.bz2 +0c310dd7956be527884f8059c195a5aca1042b089d0927ac6341e6310b1250a7059bc61aaaab4dfb76c6ab8b67e440878ca203f72674529bbcb46770ed9b6885 mpfr-3.1.6.tar.bz2 0028b76df130720c1fad7de937a0d041224806ce5ef76589f19c7b49d956071a683e2f20d154c192a231e69756b19e48208f2889b0c13950ceb7b3cfaf059a43 mpc-1.0.3.tar.gz 85d0b40f4dbf14cb99d17aa07048cdcab2dc3eb527d2fbb1e84c41b2de5f351025370e57448b63b2b8a8cf8a0843a089c3263f9baee1542d5c2e1cb37ed39d94 isl-0.18.tar.bz2 diff --git a/contrib/test_mklog.py b/contrib/test_mklog.py index 7e95ec1..a0670da 100755 --- a/contrib/test_mklog.py +++ b/contrib/test_mklog.py @@ -317,9 +317,10 @@ index 00000000000..dcc8999c446 EXPECTED5 = '''\ PR target/95046 - Vectorize V2SFmode operations + PR target/95046 + gcc/testsuite/ChangeLog: - PR target/95046 * gcc.target/i386/pr95046-6.c: New test. ''' @@ -377,9 +378,11 @@ index 00000000000..f3d6d11e61e ''' EXPECTED7 = '''\ -gcc/testsuite/ChangeLog: DR 2237 + +gcc/testsuite/ChangeLog: + * g++.dg/DRs/dr2237.C: New test. ''' diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 02e66c8..eeb293a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,506 @@ +2021-05-16 Christophe Lyon <christophe.lyon@linaro.org> + + * config/arm/arm.h (CPP_SPEC): Remove error message about + -mlittle-endian/-mbig-endian conflict. + +2021-05-15 Bill Schmidt <wschmidt@linux.ibm.com> + + * config/rs6000/rs6000-c.c (rs6000_target_modify_macros): Define + __ROP_PROTECT__ if -mrop-protect is selected. + +2021-05-15 Bill Schmidt <wschmidt@linux.ibm.com> + + * config/rs6000/rs6000-internal.h (rs6000_stack): Add + rop_hash_save_offset and rop_hash_size. + * config/rs6000/rs6000-logue.c (rs6000_stack_info): Compute + rop_hash_size and rop_hash_save_offset. + (debug_stack_info): Dump rop_hash_save_offset and rop_hash_size. + (rs6000_emit_prologue): Emit hashst[p] in prologue. + (rs6000_emit_epilogue): Emit hashchk[p] in epilogue. + * config/rs6000/rs6000.md (unspec): Add UNSPEC_HASHST and + UNSPEC_HASHCHK. + (hashst): New define_insn. + (hashchk): Likewise. + +2021-05-15 Bill Schmidt <wschmidt@linux.ibm.com> + + * config/rs6000/rs6000.c (rs6000_option_override_internal): + Disable shrink wrap when inserting ROP-protect instructions. + * config/rs6000/rs6000.opt (mrop-protect): New option. + (mprivileged): Likewise. + * doc/invoke.texi: Document mrop-protect and mprivileged. + +2021-05-15 Hans-Peter Nilsson <hp@axis.com> + + * reorg.c (fill_slots_from_thread): Reinstate code typoed out in + "Remove CC0". + +2021-05-15 Martin Jambor <mjambor@suse.cz> + + Revert: + 2021-05-13 Martin Jambor <mjambor@suse.cz> + + PR tree-optimization/100453 + * tree-sra.c (sra_modify_assign): All const base accesses do not + need refreshing, not just those from decl_pool. + (sra_modify_assign): Do not refresh into a const base decl. + +2021-05-15 Jakub Jelinek <jakub@redhat.com> + + PR rtl-optimization/100342 + * regcprop.c (copy_value): When copying a source reg in a wider + mode than it has recorded for the value, adjust recorded destination + mode too or punt if !REG_CAN_CHANGE_MODE_P. + +2021-05-14 Jason Merrill <jason@redhat.com> + + * intl.h: Add comments. + +2021-05-14 Kyrylo Tkachov <kyrylo.tkachov@arm.com> + + * config/aarch64/aarch64-simd.md + (aarch64_sqdml<SBINQOPS:as>l2_lane<mode>_internal): Split into... + (aarch64_sqdmlsl2_lane<mode>_internal): ... This... + (aarch64_sqdmlal2_lane<mode>_internal): ... And this. + (aarch64_sqdml<SBINQOPS:as>l2_laneq<mode>_internal): Split into ... + (aarch64_sqdmlsl2_laneq<mode>_internal): ... This... + (aarch64_sqdmlal2_laneq<mode>_internal): ... And this. + (aarch64_sqdml<SBINQOPS:as>l2_n<mode>_internal): Split into... + (aarch64_sqdmlsl2_n<mode>_internal): ... This... + (aarch64_sqdmlal2_n<mode>_internal): ... And this. + +2021-05-14 Prathamesh Kulkarni <prathamesh.kulkarni@linaro.org> + + PR target/66791 + * config/arm/arm_neon.h (vtst_s8): Replace call to vtst builtin with it's + boolean logic equivalent. + (vtst_s16): Likewise. + (vtst_s32): Likewise. + (vtst_u8): Likewise. + (vtst_u16): Likewise. + (vtst_u32): Likewise. + (vtst_p8): Likewise. + (vtst_p16): Likewise. + (vtstq_s8): Likewise. + (vtstq_s16): Likewise. + (vtstq_s32): Likewise. + (vtstq_u8): Likewise. + (vtstq_u16): Likewise. + (vtstq_u32): Likewise. + (vtstq_p8): Likewise. + (vtstq_p16): Likewise. + * config/arm/arm_neon_builtins.def: Remove entry for vtst. + * config/arm/neon.md (neon_vtst<mode>): Remove pattern. + +2021-05-14 Kyrylo Tkachov <kyrylo.tkachov@arm.com> + + * config/aarch64/aarch64-simd.md (aarch64_sqdmlal2<mode>): Merge into... + (aarch64_sqdml<SBINQOPS:as>l2<mode>): ... This. + (aarch64_sqdmlsl2<mode>): Delete. + (aarch64_sqdmlal2_lane<mode>): Merge this... + (aarch64_sqdmlsl2_lane<mode>): ... And this... + (aarch64_sqdml<SBINQOPS:as>l2_lane<mode>): ... Into this. + (aarch64_sqdmlal2_laneq<mode>): Merge this... + (aarch64_sqdmlsl2_laneq<mode>): ... And this... + (aarch64_sqdml<SBINQOPS:as>l2_laneq<mode>): ... Into this. + (aarch64_sqdmlal2_n<mode>): Merge this... + (aarch64_sqdmlsl2_n<mode>): ... And this... + (aarch64_sqdml<SBINQOPS:as>l2_n<mode>): ... Into this. + +2021-05-13 Martin Sebor <msebor@redhat.com> + + PR middle-end/100574 + * builtins.c (access_ref::get_ref): Improve detection of PHIs with + all null arguments. + +2021-05-13 Martin Sebor <msebor@redhat.com> + + PR tree-optimization/93100 + PR middle-end/98583 + * tree-ssa-uninit.c (check_defs): Exclude intrinsic functions that + don't modify referenced objects. + +2021-05-13 Martin Jambor <mjambor@suse.cz> + + PR tree-optimization/100453 + * tree-sra.c (sra_modify_assign): All const base accesses do not + need refreshing, not just those from decl_pool. + (sra_modify_assign): Do not refresh into a const base decl. + +2021-05-13 Martin Liska <mliska@suse.cz> + + * tree-ssa-dom.c: Remove m_simplifier. + +2021-05-13 Richard Earnshaw <rearnsha@arm.com> + + PR target/100563 + * config/arm/arm.c (arm_canonicalize_comparison): Correctly + canonicalize DImode inequality comparisons against the + maximum integral value. + +2021-05-13 Jakub Jelinek <jakub@redhat.com> + + PR tree-optimization/98856 + * config/i386/i386.c (ix86_shift_rotate_cost): Add CODE argument. + Expect V2DI and V4DI arithmetic right shifts to be emulated. + (ix86_rtx_costs, ix86_add_stmt_cost): Adjust ix86_shift_rotate_cost + caller. + * config/i386/i386-expand.c (expand_vec_perm_2perm_interleave, + expand_vec_perm_2perm_pblendv): New functions. + (ix86_expand_vec_perm_const_1): Use them. + * config/i386/sse.md (ashr<mode>3<mask_name>): Rename to ... + (<mask_codefor>ashr<mode>3<mask_name>): ... this. + (ashr<mode>3): New define_expand with VI248_AVX512BW iterator. + (ashrv4di3): New define_expand. + (ashrv2di3): Change condition to TARGET_SSE2, handle !TARGET_XOP + and !TARGET_AVX512VL expansion. + +2021-05-13 Uroš Bizjak <ubizjak@gmail.com> + + PR target/100581 + * config/i386/i386-expand.c (ix86_expand_sse_movcc): Force mode + sizes < 16 to a register when constructing vpcmov pattern. + * config/i386/mmx.md (*xop_pcmov_<mode>): Use MMXMODE124 mode. + +2021-05-13 Martin Liska <mliska@suse.cz> + + * gcov-io.c (gcov_write_block): Remove. + (gcov_write_words): Likewise. + (gcov_read_words): Re-implement using gcov_read_bytes. + (gcov_allocate): Remove. + (GCOV_BLOCK_SIZE): Likewise. + (struct gcov_var): Remove most of the fields. + (gcov_position): Implement with ftell. + (gcov_rewrite): Remove setting of start and offset fields. + (from_file): Re-format. + (gcov_open): Remove setbuf call. It should not be needed. + (gcov_close): Remove internal buffer handling. + (gcov_magic): Use __builtin_bswap32. + (gcov_write_counter): Use directly gcov_write_unsigned. + (gcov_write_string): Use direct fwrite and do not round + to 4 bytes. + (gcov_seek): Use directly fseek. + (gcov_write_tag): Use gcov_write_unsigned directly. + (gcov_write_length): Likewise. + (gcov_write_tag_length): Likewise. + (gcov_read_bytes): Use directly fread. + (gcov_read_unsigned): Use gcov_read_words. + (gcov_read_counter): Likewise. + (gcov_read_string): Use gcov_read_bytes. + * gcov-io.h (GCOV_WORD_SIZE): Adjust to reflect + that size is not in bytes, but words (4B). + (GCOV_TAG_FUNCTION_LENGTH): Likewise. + (GCOV_TAG_ARCS_LENGTH): Likewise. + (GCOV_TAG_ARCS_NUM): Likewise. + (GCOV_TAG_COUNTER_LENGTH): Likewise. + (GCOV_TAG_COUNTER_NUM): Likewise. + (GCOV_TAG_SUMMARY_LENGTH): Likewise. + +2021-05-13 liuhongt <hongtao.liu@intel.com> + + PR target/94680 + * config/i386/sse.md (ssedoublevecmode): Add attribute for + V64QI/V32HI/V16SI/V4DI. + (ssehalfvecmode): Add attribute for V2DI/V2DF. + (*vec_concatv4si_0): Extend to VI124_128. + (*vec_concat<mode>_0): New pre-reload splitter. + * config/i386/predicates.md (movq_parallel): New predicate. + +2021-05-13 Alexandre Oliva <oliva@adacore.com> + + * targhooks.c (default_zero_call_used_regs): Retry using + successfully-zeroed registers as sources. + +2021-05-12 Tobias Burnus <tobias@codesourcery.com> + + * omp-low.c (finish_taskreg_scan): Use the proper detach decl. + +2021-05-12 Aldy Hernandez <aldyh@redhat.com> + + PR c/100521 + * gimple-range.cc (range_of_builtin_call): Skip out on + processing __builtin_clz when varying. + +2021-05-12 Tom de Vries <tdevries@suse.de> + + PR target/96005 + * config/nvptx/nvptx-opts.h (enum ptx_version): New enum. + * config/nvptx/nvptx.c (nvptx_file_start): Print .version according + to ptx_version_option. + * config/nvptx/nvptx.h (TARGET_PTX_6_3): Define. + * config/nvptx/nvptx.md (define_insn "nvptx_shuffle<mode>") + (define_insn "nvptx_vote_ballot"): Use sync variant for + TARGET_PTX_6_3. + * config/nvptx/nvptx.opt (ptx_version): Add enum. + (mptx): Add option. + * doc/invoke.texi (Nvidia PTX Options): Add mptx item. + +2021-05-12 Richard Biener <rguenther@suse.de> + + PR tree-optimization/100566 + * tree-ssa-sccvn.c (dominated_by_p_w_unex): Properly handle + allow_back for all edge queries. + +2021-05-12 liuhongt <hongtao.liu@intel.com> + + PR target/99908 + * config/i386/sse.md (<sse4_1_avx2>_pblendvb): Add + splitters for pblendvb of NOT mask register. + +2021-05-12 Richard Biener <rguenther@suse.de> + + PR tree-optimization/100519 + * tree-ssa-reassoc.c (can_associate_p): Split into... + (can_associate_op_p): ... this + (can_associate_type_p): ... and this. + (is_reassociable_op): Call can_associate_op_p. + (break_up_subtract_bb): Call the appropriate predicates. + (reassociate_bb): Likewise. + +2021-05-12 Martin Liska <mliska@suse.cz> + + * lto-wrapper.c (merge_and_complain): Merge -flto=arg options. + (run_gcc): Use -flto argument detection for merged + fdecoded_options. + +2021-05-12 Martin Liska <mliska@suse.cz> + + * lto-wrapper.c (print_lto_docs_link): New function. + (run_gcc): Print warning about missing job server detection + after we know NR of partitions. Do the same for -flto{,=1}. + * opts.c (get_option_html_page): Support -flto option. + +2021-05-12 Martin Liska <mliska@suse.cz> + + * lto-wrapper.c (get_options_from_collect_gcc_options): Change + return type. + (append_option): Remove. + (find_option): Rework to use the vector type. + (remove_option): Remove. + (merge_and_complain): Use vectors for cl_decoded_option data + type arguments. + (append_compiler_options): Likewise. + (append_diag_options): Likewise. + (append_linker_options): Likewise. + (append_offload_options): Likewise. + (compile_offload_image): Likewise. + (compile_images_for_offload_targets): Likewise. + (find_and_merge_options): Likewise. + (run_gcc): Likewise. + +2021-05-12 Bernd Edlinger <bernd.edlinger@hotmail.de> + + PR debug/100515 + * dwarf2out.c (dwarf2out_finish): Set + have_multiple_function_sections with multi-range text_section. + +2021-05-12 Martin Liska <mliska@suse.cz> + + PR bootstrap/100560 + * Makefile.in: Remove version.h from linker command line. + +2021-05-12 Richard Biener <rguenther@suse.de> + + PR middle-end/100547 + * rtl.h (rtvec_alloc): Make argument size_t. + * rtl.c (rtvec_alloc): Verify the count is less than INT_MAX. + +2021-05-12 Jakub Jelinek <jakub@redhat.com> + + PR middle-end/100508 + * cfgexpand.c (expand_debug_expr): For DEBUG_EXPR_DECL with vector + type, don't reuse DECL_RTL if it has different mode, instead force + creation of a new DEBUG_EXPR. + +2021-05-12 Jakub Jelinek <jakub@redhat.com> + Marc Glisse <marc.glisse@inria.fr> + + PR tree-optimization/94589 + * match.pd ((X & Y) == X -> (X & ~Y) == 0, + (X | Y) == Y -> (X & ~Y) == 0): New GIMPLE simplifications. + +2021-05-12 Uroš Bizjak <ubizjak@gmail.com> + + PR target/98218 + * config/i386/i386-expand.c (ix86_expand_sse_movcc): Handle V2SF mode. + * config/i386/mmx.md (MMXMODE124): New mode iterator. + (V2FI): Ditto. + (mmxintvecmode): New mode attribute. + (mmxintvecmodelower): Ditto. + (*mmx_maskcmpv2sf3_comm): New insn pattern. + (*mmx_maskcmpv2sf3): Ditto. + (vec_cmpv2sfv2si): New expander. + (vcond<V2FI:mode>v2si): Ditto. + (mmx_vlendvps): New insn pattern. + (vcond<MMXMODE124:mode><MMXMODEI:mode>): Also handle V2SFmode. + (vcondu<MMXMODE124:mode><MMXMODEI:mode>): Ditto. + (vcond_mask_<mode><mmxintvecmodelower>): Ditto. + +2021-05-11 Martin Sebor <msebor@redhat.com> + + PR middle-end/21433 + * expr.c (expand_expr_real_1): Replace unreachable code with an assert. + +2021-05-11 Richard Biener <rguenther@suse.de> + + * gimple-fold.c (gimple_fold_call): Do not call + maybe_fold_reference on call arguments or the static chain. + (fold_stmt_1): Do not call maybe_fold_reference on GIMPLE_ASM + inputs. + +2021-05-11 Martin Liska <mliska@suse.cz> + + * builtins.def (DEF_HSAIL_BUILTIN): Remove. + (DEF_HSAIL_ATOMIC_BUILTIN): Likewise. + (DEF_HSAIL_SAT_BUILTIN): Likewise. + (DEF_HSAIL_INTR_BUILTIN): Likewise. + (DEF_HSAIL_CVT_ZEROI_SAT_BUILTIN): Likewise. + * doc/frontends.texi: Remove BRIG. + * doc/install.texi: Likewise. + * doc/invoke.texi: Likewise. + * doc/standards.texi: Likewise. + * brig-builtins.def: Removed. + * brig/ChangeLog: Removed. + * brig/Make-lang.in: Removed. + * brig/brig-builtins.h: Removed. + * brig/brig-c.h: Removed. + * brig/brig-lang.c: Removed. + * brig/brigfrontend/brig-arg-block-handler.cc: Removed. + * brig/brigfrontend/brig-atomic-inst-handler.cc: Removed. + * brig/brigfrontend/brig-basic-inst-handler.cc: Removed. + * brig/brigfrontend/brig-branch-inst-handler.cc: Removed. + * brig/brigfrontend/brig-cmp-inst-handler.cc: Removed. + * brig/brigfrontend/brig-code-entry-handler.cc: Removed. + * brig/brigfrontend/brig-code-entry-handler.h: Removed. + * brig/brigfrontend/brig-comment-handler.cc: Removed. + * brig/brigfrontend/brig-control-handler.cc: Removed. + * brig/brigfrontend/brig-copy-move-inst-handler.cc: Removed. + * brig/brigfrontend/brig-cvt-inst-handler.cc: Removed. + * brig/brigfrontend/brig-fbarrier-handler.cc: Removed. + * brig/brigfrontend/brig-function-handler.cc: Removed. + * brig/brigfrontend/brig-function.cc: Removed. + * brig/brigfrontend/brig-function.h: Removed. + * brig/brigfrontend/brig-inst-mod-handler.cc: Removed. + * brig/brigfrontend/brig-label-handler.cc: Removed. + * brig/brigfrontend/brig-lane-inst-handler.cc: Removed. + * brig/brigfrontend/brig-machine.c: Removed. + * brig/brigfrontend/brig-machine.h: Removed. + * brig/brigfrontend/brig-mem-inst-handler.cc: Removed. + * brig/brigfrontend/brig-module-handler.cc: Removed. + * brig/brigfrontend/brig-queue-inst-handler.cc: Removed. + * brig/brigfrontend/brig-seg-inst-handler.cc: Removed. + * brig/brigfrontend/brig-signal-inst-handler.cc: Removed. + * brig/brigfrontend/brig-to-generic.cc: Removed. + * brig/brigfrontend/brig-to-generic.h: Removed. + * brig/brigfrontend/brig-util.cc: Removed. + * brig/brigfrontend/brig-util.h: Removed. + * brig/brigfrontend/brig-variable-handler.cc: Removed. + * brig/brigfrontend/hsa-brig-format.h: Removed. + * brig/brigfrontend/phsa.h: Removed. + * brig/brigspec.c: Removed. + * brig/config-lang.in: Removed. + * brig/gccbrig.texi: Removed. + * brig/lang-specs.h: Removed. + * brig/lang.opt: Removed. + +2021-05-11 Richard Biener <rguenther@suse.de> + + PR ipa/100513 + * ipa-param-manipulation.c + (ipa_param_body_adjustments::modify_call_stmt): Avoid + altering SSA_NAME_DEF_STMT by adjusting the calls LHS + via gimple_call_lhs_ptr. + +2021-05-11 Alex Coplan <alex.coplan@arm.com> + + PR target/99725 + * config/arm/arm.c (cmse_nonsecure_call_inline_register_clear): + Avoid emitting CFA adjusts on the sp if we have the fp. + +2021-05-11 Richard Sandiford <richard.sandiford@arm.com> + + * config/aarch64/iterators.md (VMUL_CHANGE_NLANES): Delete. + (VMULD): New iterator. + (VCOND): Handle V4HF and V8HF. + (VCONQ): Fix entry for V2SF. + * config/aarch64/aarch64-simd.md (mul_lane<mode>3): Use VMULD + instead of VMUL. Use a 64-bit vector mode for the indexed operand. + (*aarch64_mul3_elt_<vswap_width_name><mode>): Merge with... + (mul_laneq<mode>3): ...this define_insn. Use VMUL instead of VDQSF. + Use a 128-bit vector mode for the indexed operand. Use stype for + the scheduling type. + +2021-05-11 Richard Biener <rguenther@suse.de> + + * gimple-fold.c (maybe_fold_reference): Only return + is_gimple_min_invariant values. + +2021-05-11 Richard Biener <rguenther@suse.de> + + PR middle-end/100509 + * gimple-fold.c (fold_gimple_assign): Only call + get_symbol_constant_value on register type symbols. + +2021-05-11 Srinath Parvathaneni <srinath.parvathaneni@arm.com> + Joe Ramsay <joe.ramsay@arm.com> + + PR target/100419 + * config/arm/arm_mve.h (__arm_vstrwq_scatter_offset): Fix wrong arguments. + (__arm_vcmpneq): Remove duplicate definition. + (__arm_vstrwq_scatter_offset_p): Likewise. + (__arm_vmaxq_x): Likewise. + (__arm_vmlsdavaq): Likewise. + (__arm_vmlsdavaxq): Likewise. + (__arm_vmlsdavq_p): Likewise. + (__arm_vmlsdavxq_p): Likewise. + (__arm_vrmlaldavhaq): Likewise. + (__arm_vstrbq_p): Likewise. + (__arm_vstrbq_scatter_offset): Likewise. + (__arm_vstrbq_scatter_offset_p): Likewise. + (__arm_vstrdq_scatter_offset): Likewise. + (__arm_vstrdq_scatter_offset_p): Likewise. + (__arm_vstrdq_scatter_shifted_offset): Likewise. + (__arm_vstrdq_scatter_shifted_offset_p): Likewise. + +2021-05-11 Jakub Jelinek <jakub@redhat.com> + + PR middle-end/100471 + * omp-low.c (lower_omp_task_reductions): For OMP_TASKLOOP, if data + is 0, bypass the reduction loop including + GOMP_taskgroup_reduction_unregister call. + +2021-05-11 Kewen Lin <linkw@linux.ibm.com> + + * config/rs6000/rs6000.c (struct rs6000_cost_data): New member + costing_for_scalar. + (rs6000_density_test): Early return if costing_for_scalar is true. + (rs6000_init_cost): Init costing_for_scalar of rs6000_cost_data. + +2021-05-11 Kewen Lin <linkw@linux.ibm.com> + + * doc/tm.texi: Regenerated. + * target.def (init_cost): Add new parameter costing_for_scalar. + * targhooks.c (default_init_cost): Adjust for new parameter. + * targhooks.h (default_init_cost): Likewise. + * tree-vect-loop.c (_loop_vec_info::_loop_vec_info): Likewise. + (vect_compute_single_scalar_iteration_cost): Likewise. + (vect_analyze_loop_2): Likewise. + * tree-vect-slp.c (_bb_vec_info::_bb_vec_info): Likewise. + (vect_bb_vectorization_profitable_p): Likewise. + * tree-vectorizer.h (init_cost): Likewise. + * config/aarch64/aarch64.c (aarch64_init_cost): Likewise. + * config/i386/i386.c (ix86_init_cost): Likewise. + * config/rs6000/rs6000.c (rs6000_init_cost): Likewise. + +2021-05-11 Kewen Lin <linkw@linux.ibm.com> + + * config/rs6000/rs6000.c (rs6000_vect_nonmem): Renamed to + vect_nonmem and moved into... + (struct rs6000_cost_data): ...here. + (rs6000_init_cost): Use vect_nonmem of cost_data instead. + (rs6000_add_stmt_cost): Likewise. + (rs6000_finish_cost): Likewise. + 2021-05-10 Eric Botcazou <ebotcazou@adacore.com> * range-op.cc (get_bool_state): Adjust head comment. diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index abf046d..915a0b2 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20210511 +20210517 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 487db22..1b5d3f4 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -2942,10 +2942,10 @@ build/genmatch$(build_exeext) : $(BUILD_CPPLIB) \ # These programs are not linked with the MD reader. build/gengtype$(build_exeext) : build/gengtype-lex.o build/gengtype-parse.o \ - build/gengtype-state.o version.h build/errors.o + build/gengtype-state.o build/errors.o gengtype$(exeext) : gengtype.o gengtype-lex.o gengtype-parse.o \ - gengtype-state.o version.h errors.o $(LIBDEPS) + gengtype-state.o errors.o $(LIBDEPS) +$(LINKER) $(ALL_LINKERFLAGS) $(LDFLAGS) -o $@ \ $(filter-out ($LIBDEPS), $^) $(LIBS) diff --git a/gcc/ada/ChangeLog b/gcc/ada/ChangeLog index b956fea..5146820 100644 --- a/gcc/ada/ChangeLog +++ b/gcc/ada/ChangeLog @@ -1,11 +1,48 @@ +2021-05-14 Martin Liska <mliska@suse.cz> + + * doc/Makefile: Add gnat-style target. + * doc/share/conf.py: Likewise. + * doc/gnat-style.rst: New file. + +2021-05-12 Bob Duff <duff@adacore.com> + + PR ada/100564 + * atree.adb (Change_Node): Do not call Zero_Slots on a Node_Id + when the Nkind has not yet been set; call the other Zero_Slots + that takes a range of slot offsets. Call the new Mutate_Kind + that takes an Old_Size, for the same reason -- the size cannot + be computed without the Nkind. + (Mutate_Nkind): New function that allows specifying the Old_Size. + (Size_In_Slots): Assert that the Nkind has proper (nonzero) value. + * atree.ads: Minor reformatting. + +2021-05-12 Martin Liska <mliska@suse.cz> + + * doc/share/conf.py: Do not use binary mode. + Do not use u' literals as Python3 uses unicode by default. + +2021-05-11 Martin Liska <mliska@suse.cz> + + * gcc-interface/ada-tree.h (BUILT_IN_LIKELY): Use builtins + from COROUTINES. + (BUILT_IN_UNLIKELY): Likewise. + +2021-05-11 Eric Botcazou <ebotcazou@adacore.com> + + * gnatvsn.adb (Version_String): Rename to... + (C_Version_String): ...this. + (Gnat_Version_String): Adjust to above renaming. + * version.c : Fix formatting glitches. + 2021-05-10 Martin Liska <mliska@suse.cz> PR bootstrap/100506 * Make-generated.in: Replace version.c with ada/version.c. - * gcc-interface/Make-lang.in: Add version.o to GNAT1_C_OBJS. - Add version.o to GNAT_ADA_OBJS and GNATBIND_OBJS. - * gcc-interface/Makefile.in: Add version.o to TOOLS_LIBS. - * gnatvsn.adb: Start using a new C symbol gnat_version_string. + * gcc-interface/Make-lang.in: Add version.o to GNAT1_C_OBJS and + GNATBIND_OBJS. + * gcc-interface/Makefile.in: Replace version.c with ada/version.c. + Add version.o to TOOLS_LIBS. + * gnatvsn.adb: Replace version_string with gnat_version_string. * version.c: New file. 2021-05-10 Eric Botcazou <ebotcazou@adacore.com> diff --git a/gcc/ada/atree.adb b/gcc/ada/atree.adb index 4d4dc43..608819b 100644 --- a/gcc/ada/atree.adb +++ b/gcc/ada/atree.adb @@ -216,6 +216,13 @@ package body Atree is -- cannot be used to modify an already-initialized Nkind field. See also -- Mutate_Nkind. + procedure Mutate_Nkind + (N : Node_Id; Val : Node_Kind; Old_Size : Field_Offset); + -- Called by the other Mutate_Nkind to do all the work. This is needed + -- because the call in Change_Node, which calls this one directly, happens + -- after zeroing N's slots, which destroys its Nkind, which prevents us + -- from properly computing Old_Size. + package Field_Checking is function Field_Present (Kind : Node_Kind; Field : Node_Field) return Boolean; @@ -868,9 +875,8 @@ package body Atree is end Init_Nkind; procedure Mutate_Nkind - (N : Node_Id; Val : Node_Kind) + (N : Node_Id; Val : Node_Kind; Old_Size : Field_Offset) is - Old_Size : constant Field_Offset := Size_In_Slots (N); New_Size : constant Field_Offset := Size_In_Slots_To_Alloc (Val); All_Node_Offsets : Node_Offsets.Table_Type renames @@ -905,6 +911,11 @@ package body Atree is pragma Debug (Validate_Node_Write (N)); end Mutate_Nkind; + procedure Mutate_Nkind (N : Node_Id; Val : Node_Kind) is + begin + Mutate_Nkind (N, Val, Old_Size => Size_In_Slots (N)); + end Mutate_Nkind; + Ekind_Offset : constant Field_Offset := Entity_Field_Descriptors (Ekind).Offset; @@ -998,13 +1009,19 @@ package body Atree is end if; if New_Size > Old_Size then - pragma Debug (Zero_Slots (N)); - Node_Offsets.Table (N) := Alloc_Slots (New_Size); - end if; + declare + New_Offset : constant Field_Offset := Alloc_Slots (New_Size); + begin + pragma Debug (Zero_Slots (N)); + Node_Offsets.Table (N) := New_Offset; + Zero_Slots (New_Offset, New_Offset + New_Size - 1); + end; - Zero_Slots (N); + else + Zero_Slots (N); + end if; - Mutate_Nkind (N, New_Kind); + Mutate_Nkind (N, New_Kind, Old_Size); Set_Sloc (N, Save_Sloc); Set_In_List (N, Save_In_List); @@ -2125,6 +2142,7 @@ package body Atree is function Size_In_Slots (N : Node_Or_Entity_Id) return Field_Offset is begin + pragma Assert (Nkind (N) /= N_Unused_At_Start); return (if Nkind (N) in N_Entity then Einfo.Entities.Max_Entity_Size else Sinfo.Nodes.Size (Nkind (N))); diff --git a/gcc/ada/atree.ads b/gcc/ada/atree.ads index efb8ca2..e2d3492 100644 --- a/gcc/ada/atree.ads +++ b/gcc/ada/atree.ads @@ -571,8 +571,7 @@ package Atree is (N : Entity_Id; Field : Entity_Field) return Boolean; -- True if the field value is the initial zero value - procedure Mutate_Nkind - (N : Node_Id; Val : Node_Kind) with Inline; + procedure Mutate_Nkind (N : Node_Id; Val : Node_Kind) with Inline; -- There is no Set_Nkind in Sinfo.Nodes. We use this instead. This is here, -- and has a different name, because it does some extra checking. Nkind is -- like a discriminant, in that it controls which fields exist, and that diff --git a/gcc/ada/doc/Makefile b/gcc/ada/doc/Makefile index 9a435eb..4adfd36 100644 --- a/gcc/ada/doc/Makefile +++ b/gcc/ada/doc/Makefile @@ -14,7 +14,7 @@ ALLSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) \ -c $(SOURCEDIR)/share \ -d $(BUILDDIR)/$*/doctrees \ $(SOURCEDIR) -DOC_LIST=gnat_rm gnat_ugn +DOC_LIST=gnat_rm gnat_ugn gnat-style FMT_LIST=html pdf txt info .PHONY: help clean diff --git a/gcc/ada/doc/gnat-style.rst b/gcc/ada/doc/gnat-style.rst new file mode 100644 index 0000000..527e7ba --- /dev/null +++ b/gcc/ada/doc/gnat-style.rst @@ -0,0 +1,691 @@ +GNAT Coding Style: A Guide for GNAT Developers +============================================== + +General +------- + +Most of GNAT is written in Ada using a consistent style to ensure +readability of the code. This document has been written to help +maintain this consistent style, while having a large group of developers +work on the compiler. + +For the coding style in the C parts of the compiler and run time, +see the GNU Coding Guidelines. + +This document is structured after the Ada Reference Manual. +Those familiar with that document should be able to quickly +lookup style rules for particular constructs. + +Lexical Elements +---------------- + +Character Set and Separators +**************************** + +.. index:: Character set +.. index:: ASCII +.. index:: Separators +.. index:: End-of-line +.. index:: Line length +.. index:: Indentation + +* The character set used should be plain 7-bit ASCII. + The only separators allowed are space and the end-of-line sequence. + No other control character or format effector (such as ``HT``, + ``VT``, ``FF`` ) + should be used. + The normal end-of-line sequence is used, which may be + ``LF``, ``CR/LF`` or ``CR``, + depending on the host system. An optional ``SUB`` + ( ``16#1A#`` ) may be present as the + last character in the file on hosts using that character as file terminator. + +* Files that are checked in or distributed should be in host format. + +* A line should never be longer than 79 characters, not counting the line + separator. + +* Lines must not have trailing blanks. + +* Indentation is 3 characters per level for ``if`` statements, loops, and + ``case`` statements. + For exact information on required spacing between lexical + elements, see file style.adb. + + .. index:: style.adb file + +Identifiers +*********** + +* Identifiers will start with an upper case letter, and each letter following + an underscore will be upper case. + + .. index:: Casing (for identifiers) + + Short acronyms may be all upper case. + All other letters are lower case. + An exception is for identifiers matching a foreign language. In particular, + we use all lower case where appropriate for C. + +* Use underscores to separate words in an identifier. + + .. index:: Underscores + +* Try to limit your use of abbreviations in identifiers. + It is ok to make a few abbreviations, explain what they mean, and then + use them frequently, but don't use lots of obscure abbreviations. An + example is the ``ALI`` word which stands for Ada Library + Information and is by convention always written in upper-case when + used in entity names. + + .. code-block:: ada + + procedure Find_ALI_Files; + +* Don't use the variable name ``I``, use ``J`` instead; ``I`` is too + easily confused with ``1`` in some fonts. Similarly don't use the + variable ``O``, which is too easily mistaken for the number ``0``. + +Numeric Literals +**************** + +* Numeric literals should include underscores where helpful for + readability. + + .. index:: Underscores + + .. code-block:: ada + + 1_000_000 + 16#8000_0000# + 3.14159_26535_89793_23846 + +Reserved Words +************** + +* Reserved words use all lower case. + + .. index:: Casing (for reserved words) + + .. code-block:: ada + + return else + +* The words ``Access``, ``Delta`` and ``Digits`` are + capitalized when used as attribute_designator. + +Comments +******** + +* A comment starts with ``--`` followed by two spaces. + The only exception to this rule (i.e. one space is tolerated) is when the + comment ends with a single space followed by ``--``. + It is also acceptable to have only one space between ``--`` and the start + of the comment when the comment is at the end of a line, + after some Ada code. + +* Every sentence in a comment should start with an upper-case letter (including + the first letter of the comment). + + .. index:: Casing (in comments) + +* When declarations are commented with 'hanging' comments, i.e. + comments after the declaration, there is no blank line before the + comment, and if it is absolutely necessary to have blank lines within + the comments, e.g. to make paragraph separations within a single comment, + these blank lines *do* have a ``--`` (unlike the + normal rule, which is to use entirely blank lines for separating + comment paragraphs). The comment starts at same level of indentation + as code it is commenting. + + .. index:: Blank lines (in comments) + .. index:: Indentation + + .. code-block:: ada + + z : Integer; + -- Integer value for storing value of z + -- + -- The previous line was a blank line. + +* Comments that are dubious or incomplete, or that comment on possibly + wrong or incomplete code, should be preceded or followed by ``???``. + +* Comments in a subprogram body must generally be surrounded by blank lines. + An exception is a comment that follows a line containing a single keyword + ( ``begin``, ``else``, ``loop`` ): + + .. code-block:: ada + + begin + -- Comment for the next statement + + A := 5; + + -- Comment for the B statement + + B := 6; + end; + +* In sequences of statements, comments at the end of the lines should be + aligned. + + .. index:: Alignment (in comments) + + .. code-block:: ada + + My_Identifier := 5; -- First comment + Other_Id := 6; -- Second comment + +* Short comments that fit on a single line are *not* ended with a + period. Comments taking more than a line are punctuated in the normal + manner. + +* Comments should focus on *why* instead of *what*. + Descriptions of what subprograms do go with the specification. + +* Comments describing a subprogram spec should specifically mention the + formal argument names. General rule: write a comment that does not + depend on the names of things. The names are supplementary, not + sufficient, as comments. + +* *Do not* put two spaces after periods in comments. + +Declarations and Types +---------------------- + +* In entity declarations, colons must be surrounded by spaces. Colons + should be aligned. + + .. index:: Alignment (in declarations) + + .. code-block:: ada + + Entity1 : Integer; + My_Entity : Integer; + +* Declarations should be grouped in a logical order. + Related groups of declarations may be preceded by a header comment. + +* All local subprograms in a subprogram or package body should be declared + before the first local subprogram body. + +* Do not declare local entities that hide global entities. + + .. index:: Hiding of outer entities + +* Do not declare multiple variables in one declaration that spans lines. + Start a new declaration on each line, instead. + +* The defining_identifiers of global declarations serve as + comments of a sort. So don't choose terse names, but look for names + that give useful information instead. + +* Local names can be shorter, because they are used only within + one context, where comments explain their purpose. + +* When starting an initialization or default expression on the line that follows + the declaration line, use 2 characters for indentation. + + .. code-block:: ada + + Entity1 : Integer := + Function_Name (Parameters, For_Call); + +* If an initialization or default expression needs to be continued on subsequent + lines, the continuations should be indented from the start of the expression. + + .. code-block:: ada + + Entity1 : Integer := Long_Function_Name + (parameters for call); + +Expressions and Names +--------------------- + +* Every operator must be surrounded by spaces. An exception is that + this rule does not apply to the exponentiation operator, for which + there are no specific layout rules. The reason for this exception + is that sometimes it makes clearer reading to leave out the spaces + around exponentiation. + + .. index:: Operators + + .. code-block:: ada + + E := A * B**2 + 3 * (C - D); + +* Use parentheses where they clarify the intended association of operands + with operators: + + .. index:: Parenthesization of expressions + + .. code-block:: ada + + (A / B) * C + +Statements +---------- + +Simple and Compound Statements +****************************** + +* Use only one statement or label per line. + +* A longer sequence_of_statements may be divided in logical + groups or separated from surrounding code using a blank line. + + +If Statements +************* + +* When the ``if``, ``elsif`` or ``else`` keywords fit on the + same line with the condition and the ``then`` keyword, then the + statement is formatted as follows: + + .. index:: Alignment (in an if statement) + + .. code-block:: ada + + if condition then + ... + elsif condition then + ... + else + ... + end if; + + When the above layout is not possible, ``then`` should be aligned + with ``if``, and conditions should preferably be split before an + ``and`` or ``or`` keyword a follows: + + .. code-block:: ada + + if long_condition_that_has_to_be_split + and then continued_on_the_next_line + then + ... + end if; + + The ``elsif``, ``else`` and ``end if`` always line up with + the ``if`` keyword. The preferred location for splitting the line + is before ``and`` or ``or``. The continuation of a condition is + indented with two spaces or as many as needed to make nesting clear. + As an exception, if conditions are closely related either of the + following is allowed: + + .. code-block:: ada + + if x = lakdsjfhlkashfdlkflkdsalkhfsalkdhflkjdsahf + or else + x = asldkjhalkdsjfhhfd + or else + x = asdfadsfadsf + then + ... + end if; + + if x = lakdsjfhlkashfdlkflkdsalkhfsalkdhflkjdsahf or else + x = asldkjhalkdsjfhhfd or else + x = asdfadsfadsf + then + ... + end if; + +* Conditions should use short-circuit forms ( ``and then``, + ``or else`` ), except when the operands are boolean variables + or boolean constants. + + .. index:: Short-circuit forms + +* Complex conditions in ``if`` statements are indented two characters: + + .. index:: Indentation (in if statements) + + .. code-block:: ada + + if this_complex_condition + and then that_other_one + and then one_last_one + then + ... + end if; + + There are some cases where complex conditionals can be laid out + in manners that do not follow these rules to preserve better + parallelism between branches, e.g. + + .. code-block:: ada + + if xyz.abc (gef) = 'c' + or else + xyz.abc (gef) = 'x' + then + ... + end if; + +* Every ``if`` block is preceded and followed by a blank line, except + where it begins or ends a sequence_of_statements. + + .. index:: Blank lines (in an if statement) + + .. code-block:: ada + + A := 5; + + if A = 5 then + null; + end if; + + A := 6; + +Case Statements +*************** + +* Layout is as below. For long ``case`` statements, the extra indentation + can be saved by aligning the ``when`` clauses with the opening ``case``. + + .. code-block:: ada + + case expression is + when condition => + ... + when condition => + ... + end case; + +Loop Statements +*************** + +* When possible, have ``for`` or ``while`` on one line with the + condition and the ``loop`` keyword. + + .. code-block:: ada + + for J in S'Range loop + ... + end loop; + + If the condition is too long, split the condition (see 'If + statements' above) and align ``loop`` with the ``for`` or + ``while`` keyword. + + .. index:: Alignment (in a loop statement) + + .. code-block:: ada + + while long_condition_that_has_to_be_split + and then continued_on_the_next_line + loop + ... + end loop; + + If the loop_statement has an identifier, it is laid out as follows: + + .. code-block:: ada + + Outer : while not condition loop + ... + end Outer; + +Block Statements +**************** + +* The ``declare`` (optional), ``begin`` and ``end`` words + are aligned, except when the block_statement is named. There + is a blank line before the ``begin`` keyword: + + .. index:: Alignment (in a block statement) + + .. code-block:: ada + + Some_Block : declare + ... + + begin + ... + end Some_Block; + +Subprograms +----------- + +Subprogram Declarations +*********************** + +* Do not write the ``in`` for parameters. + + .. code-block:: ada + + function Length (S : String) return Integer; + +* When the declaration line for a procedure or a function is too long to fit + the entire declaration (including the keyword procedure or function) on a + single line, then fold it, putting a single parameter on a line, aligning + the colons, as in: + + .. code-block:: ada + + procedure Set_Heading + (Source : String; + Count : Natural; + Pad : Character := Space; + Fill : Boolean := True); + + In the case of a function, if the entire spec does not fit on one line, then + the return may appear after the last parameter, as in: + + .. code-block:: ada + + function Head + (Source : String; + Count : Natural; + Pad : Character := Space) return String; + + Or it may appear on its own as a separate line. This form is preferred when + putting the return on the same line as the last parameter would result in + an overlong line. The return type may optionally be aligned with the types + of the parameters (usually we do this aligning if it results only in a small + number of extra spaces, and otherwise we don't attempt to align). So two + alternative forms for the above spec are: + + .. code-block:: ada + + function Head + (Source : String; + Count : Natural; + Pad : Character := Space) + return String; + + function Head + (Source : String; + Count : Natural; + Pad : Character := Space) + return String; + +Subprogram Bodies +***************** + +* Function and procedure bodies should usually be sorted alphabetically. Do + not attempt to sort them in some logical order by functionality. For a + sequence of subprogram specs, a general alphabetical sorting is also + usually appropriate, but occasionally it makes sense to group by major + function, with appropriate headers. + +* All subprograms have a header giving the function name, with the following + format: + + .. code-block:: ada + + ----------------- + -- My_Function -- + ----------------- + + procedure My_Function is + begin + ... + end My_Function; + + Note that the name in the header is preceded by a single space, + not two spaces as for other comments. These headers are used on + nested subprograms as well as outer level subprograms. They may + also be used as headers for sections of comments, or collections + of declarations that are related. + +* Every subprogram body must have a preceding subprogram_declaration, + which includes proper client documentation so that you do not need to + read the subprogram body in order to understand what the subprogram does and + how to call it. All subprograms should be documented, without exceptions. + + .. index:: Blank lines (in subprogram bodies) + +* A sequence of declarations may optionally be separated from the following + begin by a blank line. Just as we optionally allow blank lines in general + between declarations, this blank line should be present only if it improves + readability. Generally we avoid this blank line if the declarative part is + small (one or two lines) and the body has no blank lines, and we include it + if the declarative part is long or if the body has blank lines. + +* If the declarations in a subprogram contain at least one nested + subprogram body, then just before the ``begin`` of the enclosing + subprogram, there is a comment line and a blank line: + + .. code-block:: ada + + -- Start of processing for Enclosing_Subprogram + + begin + ... + end Enclosing_Subprogram; + +* When nested subprograms are present, variables that are referenced by any + nested subprogram should precede the nested subprogram specs. For variables + that are not referenced by nested procedures, the declarations can either also + be before any of the nested subprogram specs (this is the old style, more + generally used). Or then can come just before the begin, with a header. The + following example shows the two possible styles: + + .. code-block:: ada + + procedure Style1 is + Var_Referenced_In_Nested : Integer; + Var_Referenced_Only_In_Style1 : Integer; + + proc Nested; + -- Comments ... + + ------------ + -- Nested -- + ------------ + + procedure Nested is + begin + ... + end Nested; + + -- Start of processing for Style1 + + begin + ... + end Style1; + + procedure Style2 is + Var_Referenced_In_Nested : Integer; + + proc Nested; + -- Comments ... + + ------------ + -- Nested -- + ------------ + + procedure Nested is + begin + ... + end Nested; + + -- Local variables + + Var_Referenced_Only_In_Style2 : Integer; + + -- Start of processing for Style2 + + begin + ... + end Style2; + + For new code, we generally prefer Style2, but we do not insist on + modifying all legacy occurrences of Style1, which is still much + more common in the sources. + +Packages and Visibility Rules +----------------------------- + +* All program units and subprograms have their name at the end: + + .. code-block:: ada + + package P is + ... + end P; + +* We will use the style of ``use`` -ing ``with`` -ed packages, with + the context clauses looking like: + + .. index:: use clauses + + .. code-block:: ada + + with A; use A; + with B; use B; + +* Names declared in the visible part of packages should be + unique, to prevent name clashes when the packages are ``use`` d. + + .. index:: Name clash avoidance + + .. code-block:: ada + + package Entity is + type Entity_Kind is ...; + ... + end Entity; + +* After the file header comment, the context clause and unit specification + should be the first thing in a program_unit. + +* Preelaborate, Pure and Elaborate_Body pragmas should be added right after the + package name, indented an extra level and using the parameterless form: + + .. code-block:: ada + + package Preelaborate_Package is + pragma Preelaborate; + ... + end Preelaborate_Package; + +Program Structure and Compilation Issues +---------------------------------------- + +* Every GNAT source file must be compiled with the ``-gnatg`` + switch to check the coding style. + (Note that you should look at + style.adb to see the lexical rules enforced by ``-gnatg`` ). + + .. index:: -gnatg option (to gcc) + .. index:: style.adb file + +* Each source file should contain only one compilation unit. + +* Filenames should be 8 or fewer characters, followed by the ``.adb`` + extension for a body or ``.ads`` for a spec. + + .. index:: File name length + +* Unit names should be distinct when 'krunch'ed to 8 characters + (see krunch.ads) and the filenames should match the unit name, + except that they are all lower case. + + .. index:: krunch.ads file + +.. toctree:: + share/gnu_free_documentation_license diff --git a/gcc/ada/doc/share/conf.py b/gcc/ada/doc/share/conf.py index debd716..755c3a6 100644 --- a/gcc/ada/doc/share/conf.py +++ b/gcc/ada/doc/share/conf.py @@ -18,9 +18,11 @@ import latex_elements DOCS = { 'gnat_rm': { - 'title': u'GNAT Reference Manual'}, + 'title': 'GNAT Reference Manual'}, 'gnat_ugn': { - 'title': u'GNAT User\'s Guide for Native Platforms'}} + 'title': 'GNAT User\'s Guide for Native Platforms'}, + 'gnat-style': { + 'title': 'GNAT Coding Style: A Guide for GNAT Developers'}} # Then retrieve the source directory root_source_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) @@ -28,17 +30,17 @@ gnatvsn_spec = os.path.join(root_source_dir, '..', 'gnatvsn.ads') basever = os.path.join(root_source_dir, '..', '..', 'BASE-VER') texi_fsf = True # Set to False when FSF doc is switched to sphinx by default -with open(gnatvsn_spec, 'rb') as fd: +with open(gnatvsn_spec, 'r') as fd: gnatvsn_content = fd.read() def get_copyright(): - return u'2008-%s, Free Software Foundation' % time.strftime('%Y') + return '2008-%s, Free Software Foundation' % time.strftime('%Y') def get_gnat_version(): - m = re.search(br'Gnat_Static_Version_String : ' + - br'constant String := "([^\(\)]+)\(.*\)?";', + m = re.search(r'Gnat_Static_Version_String : ' + + r'constant String := "([^\(\)]+)\(.*\)?";', gnatvsn_content) if m: return m.group(1).strip().decode() @@ -57,12 +59,12 @@ def get_gnat_version(): def get_gnat_build_type(): - m = re.search(br'Build_Type : constant Gnat_Build_Type := (.+);', + m = re.search(r'Build_Type : constant Gnat_Build_Type := (.+);', gnatvsn_content) if m: - return {b'Gnatpro': 'PRO', - b'FSF': 'FSF', - b'GPL': 'GPL'}[m.group(1).strip()] + return {'Gnatpro': 'PRO', + 'FSF': 'FSF', + 'GPL': 'GPL'}[m.group(1).strip()] else: print('cannot compute GNAT build type') sys.exit(1) @@ -119,8 +121,8 @@ copyright_macros = { 'date': time.strftime("%b %d, %Y"), 'edition': 'GNAT %s Edition' % 'Pro' if get_gnat_build_type() == 'PRO' else 'GPL', - 'name': u'GNU Ada', - 'tool': u'GNAT', + 'name': 'GNU Ada', + 'tool': 'GNAT', 'version': version} latex_elements = { @@ -134,11 +136,11 @@ latex_elements = { 'tableofcontents': latex_elements.TOC % copyright_macros} latex_documents = [ - (master_doc, '%s.tex' % doc_name, project, u'AdaCore', 'manual')] + (master_doc, '%s.tex' % doc_name, project, 'AdaCore', 'manual')] texinfo_documents = [ (master_doc, doc_name, project, - u'AdaCore', doc_name, doc_name, '')] + 'AdaCore', doc_name, doc_name, '')] def setup(app): diff --git a/gcc/builtins.c b/gcc/builtins.c index 2f0efae..e1b2848 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -362,15 +362,6 @@ access_ref::get_ref (vec<access_ref> *all_refs, same_ref.offrng[1] = phi_arg_ref.offrng[1]; } - if (phi_ref.sizrng[0] < 0) - { - /* Fail if none of the PHI's arguments resulted in updating PHI_REF - (perhaps because they have all been already visited by prior - recursive calls). */ - psnlim->leave_phi (ref); - return NULL_TREE; - } - if (!same_ref.ref && same_ref.offrng[0] != 0) /* Clear BASE0 if not all the arguments refer to the same object and if not all their offsets are zero-based. This allows the final @@ -390,6 +381,15 @@ access_ref::get_ref (vec<access_ref> *all_refs, phi_ref.parmarray = parmarray; } + if (phi_ref.sizrng[0] < 0) + { + /* Fail if none of the PHI's arguments resulted in updating PHI_REF + (perhaps because they have all been already visited by prior + recursive calls). */ + psnlim->leave_phi (ref); + return NULL_TREE; + } + /* Avoid changing *THIS. */ if (pref && pref != this) *pref = phi_ref; diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index e73c3ee..7fc64a5 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,17 @@ +2021-05-14 Martin Liska <mliska@suse.cz> + + * c.opt: Add Warning keyword for 2 options. + +2021-05-13 Martin Liska <mliska@suse.cz> + + PR middle-end/100504 + * c-attribs.c (handle_target_clones_attribute): Expect a string + argument to target_clone argument. + +2021-05-11 Joseph Myers <joseph@codesourcery.com> + + * c-lex.c (interpret_float): Handle digit separators for C2X. + 2021-05-10 Martin Liska <mliska@suse.cz> * c-ada-spec.c (print_destructor): Use startswith diff --git a/gcc/c-family/c-attribs.c b/gcc/c-family/c-attribs.c index c1f652d..f54388e 100644 --- a/gcc/c-family/c-attribs.c +++ b/gcc/c-family/c-attribs.c @@ -5288,7 +5288,12 @@ handle_target_clones_attribute (tree *node, tree name, tree ARG_UNUSED (args), /* Ensure we have a function type. */ if (TREE_CODE (*node) == FUNCTION_DECL) { - if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node))) + if (TREE_CODE (TREE_VALUE (args)) != STRING_CST) + { + error ("%qE attribute argument not a string constant", name); + *no_add_attrs = true; + } + else if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (*node))) { warning (OPT_Wattributes, "%qE attribute ignored due to conflict " "with %qs attribute", name, "always_inline"); diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 7bd799d..b7daa2e 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -9059,6 +9059,12 @@ braced_list_to_string (tree type, tree ctor, bool member) if (!member && !tree_fits_uhwi_p (typesize)) return ctor; + /* If the target char size differes from the host char size, we'd risk + loosing data and getting object sizes wrong by converting to + host chars. */ + if (TYPE_PRECISION (char_type_node) != CHAR_BIT) + return ctor; + /* If the array has an explicit bound, use it to constrain the size of the string. If it doesn't, be sure to create a string that's as long as implied by the index of the last zero specified via diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index f1b4c3f..5fcf961 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -592,7 +592,7 @@ C ObjC RejectNegative Warning Alias(Werror=, implicit-function-declaration) This switch is deprecated; use -Werror=implicit-function-declaration instead. Wexceptions -C++ ObjC++ Var(warn_exceptions) Init(1) +C++ ObjC++ Var(warn_exceptions) Init(1) Warning Warn when an exception handler is shadowed by another handler. Wextra @@ -1741,7 +1741,7 @@ C++ ObjC Var(flag_module_version_ignore) Integer ; undocumented, Very dangerous, but occasionally useful Winvalid-imported-macros -C++ ObjC++ Var(warn_imported_macros) +C++ ObjC++ Var(warn_imported_macros) Warning Warn about macros that have conflicting header units definitions. flang-info-include-translate diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index d003883..a75832b 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,13 @@ +2021-05-13 Martin Sebor <msebor@redhat.com> + + PR c/100550 + * c-decl.c (get_parm_array_spec): Avoid erroneous VLA bounds. + +2021-05-12 Marcel Vollweiler <marcel@codesourcery.com> + + * c-parser.c (c_parser_omp_clause_map): Support map-type-modifier + 'close'. + 2021-05-10 Martin Liska <mliska@suse.cz> * c-aux-info.c (affix_data_type): Use startswith diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 3ea4708..53b2b5b 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -5856,6 +5856,9 @@ get_parm_array_spec (const struct c_parm *parm, tree attrs) spec += buf; break; } + else if (!INTEGRAL_TYPE_P (TREE_TYPE (nelts))) + /* Avoid invalid NELTS. */ + return attrs; /* Each variable VLA bound is represented by a dollar sign. */ spec += "$"; diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c index 5cdeb21..f79b839 100644 --- a/gcc/c/c-parser.c +++ b/gcc/c/c-parser.c @@ -15643,54 +15643,83 @@ c_parser_omp_clause_depend (c_parser *parser, tree list) map-kind: alloc | to | from | tofrom | release | delete - map ( always [,] map-kind: variable-list ) */ + map ( always [,] map-kind: variable-list ) + + OpenMP 5.0: + map ( [map-type-modifier[,] ...] map-kind: variable-list ) + + map-type-modifier: + always | close */ static tree c_parser_omp_clause_map (c_parser *parser, tree list) { location_t clause_loc = c_parser_peek_token (parser)->location; enum gomp_map_kind kind = GOMP_MAP_TOFROM; - int always = 0; - enum c_id_kind always_id_kind = C_ID_NONE; - location_t always_loc = UNKNOWN_LOCATION; - tree always_id = NULL_TREE; tree nl, c; matching_parens parens; if (!parens.require_open (parser)) return list; - if (c_parser_next_token_is (parser, CPP_NAME)) + int pos = 1; + int map_kind_pos = 0; + while (c_parser_peek_nth_token_raw (parser, pos)->type == CPP_NAME) + { + if (c_parser_peek_nth_token_raw (parser, pos + 1)->type == CPP_COLON) + { + map_kind_pos = pos; + break; + } + + if (c_parser_peek_nth_token_raw (parser, pos + 1)->type == CPP_COMMA) + pos++; + pos++; + } + + int always_modifier = 0; + int close_modifier = 0; + for (int pos = 1; pos < map_kind_pos; ++pos) { c_token *tok = c_parser_peek_token (parser); + + if (tok->type == CPP_COMMA) + { + c_parser_consume_token (parser); + continue; + } + const char *p = IDENTIFIER_POINTER (tok->value); - always_id_kind = tok->id_kind; - always_loc = tok->location; - always_id = tok->value; if (strcmp ("always", p) == 0) { - c_token *sectok = c_parser_peek_2nd_token (parser); - if (sectok->type == CPP_COMMA) + if (always_modifier) { - c_parser_consume_token (parser); - c_parser_consume_token (parser); - always = 2; + c_parser_error (parser, "too many %<always%> modifiers"); + parens.skip_until_found_close (parser); + return list; } - else if (sectok->type == CPP_NAME) + always_modifier++; + } + else if (strcmp ("close", p) == 0) + { + if (close_modifier) { - p = IDENTIFIER_POINTER (sectok->value); - if (strcmp ("alloc", p) == 0 - || strcmp ("to", p) == 0 - || strcmp ("from", p) == 0 - || strcmp ("tofrom", p) == 0 - || strcmp ("release", p) == 0 - || strcmp ("delete", p) == 0) - { - c_parser_consume_token (parser); - always = 1; - } + c_parser_error (parser, "too many %<close%> modifiers"); + parens.skip_until_found_close (parser); + return list; } + close_modifier++; } + else + { + c_parser_error (parser, "%<#pragma omp target%> with " + "modifier other than %<always%> or %<close%>" + "on %<map%> clause"); + parens.skip_until_found_close (parser); + return list; + } + + c_parser_consume_token (parser); } if (c_parser_next_token_is (parser, CPP_NAME) @@ -15700,11 +15729,11 @@ c_parser_omp_clause_map (c_parser *parser, tree list) if (strcmp ("alloc", p) == 0) kind = GOMP_MAP_ALLOC; else if (strcmp ("to", p) == 0) - kind = always ? GOMP_MAP_ALWAYS_TO : GOMP_MAP_TO; + kind = always_modifier ? GOMP_MAP_ALWAYS_TO : GOMP_MAP_TO; else if (strcmp ("from", p) == 0) - kind = always ? GOMP_MAP_ALWAYS_FROM : GOMP_MAP_FROM; + kind = always_modifier ? GOMP_MAP_ALWAYS_FROM : GOMP_MAP_FROM; else if (strcmp ("tofrom", p) == 0) - kind = always ? GOMP_MAP_ALWAYS_TOFROM : GOMP_MAP_TOFROM; + kind = always_modifier ? GOMP_MAP_ALWAYS_TOFROM : GOMP_MAP_TOFROM; else if (strcmp ("release", p) == 0) kind = GOMP_MAP_RELEASE; else if (strcmp ("delete", p) == 0) @@ -15719,35 +15748,6 @@ c_parser_omp_clause_map (c_parser *parser, tree list) c_parser_consume_token (parser); c_parser_consume_token (parser); } - else if (always) - { - if (always_id_kind != C_ID_ID) - { - c_parser_error (parser, "expected identifier"); - parens.skip_until_found_close (parser); - return list; - } - - tree t = lookup_name (always_id); - if (t == NULL_TREE) - { - undeclared_variable (always_loc, always_id); - t = error_mark_node; - } - if (t != error_mark_node) - { - tree u = build_omp_clause (clause_loc, OMP_CLAUSE_MAP); - OMP_CLAUSE_DECL (u) = t; - OMP_CLAUSE_CHAIN (u) = list; - OMP_CLAUSE_SET_MAP_KIND (u, kind); - list = u; - } - if (always == 1) - { - parens.skip_until_found_close (parser); - return list; - } - } nl = c_parser_omp_variable_list (parser, clause_loc, OMP_CLAUSE_MAP, list); diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 747ca3e..e3814ee 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -4512,7 +4512,12 @@ expand_debug_expr (tree exp) op0 = DECL_RTL_IF_SET (exp); if (op0) - return op0; + { + if (GET_MODE (op0) != mode) + gcc_assert (VECTOR_TYPE_P (TREE_TYPE (exp))); + else + return op0; + } op0 = gen_rtx_DEBUG_EXPR (mode); DEBUG_EXPR_TREE_DECL (op0) = exp; diff --git a/gcc/config/aarch64/aarch64-simd.md b/gcc/config/aarch64/aarch64-simd.md index 9962089..e59bc7b 100644 --- a/gcc/config/aarch64/aarch64-simd.md +++ b/gcc/config/aarch64/aarch64-simd.md @@ -5356,37 +5356,27 @@ [(set_attr "type" "neon_sat_mla_<Vetype>_scalar_long")] ) -(define_expand "aarch64_sqdmlal2<mode>" +(define_expand "aarch64_sqdml<SBINQOPS:as>l2<mode>" [(match_operand:<VWIDE> 0 "register_operand") - (match_operand:<VWIDE> 1 "register_operand") - (match_operand:VQ_HSI 2 "register_operand") - (match_operand:VQ_HSI 3 "register_operand")] - "TARGET_SIMD" -{ - rtx p = aarch64_simd_vect_par_cnst_half (<MODE>mode, <nunits>, true); - emit_insn (gen_aarch64_sqdmlal2<mode>_internal (operands[0], operands[1], - operands[2], operands[3], p)); - DONE; -}) - -(define_expand "aarch64_sqdmlsl2<mode>" - [(match_operand:<VWIDE> 0 "register_operand") - (match_operand:<VWIDE> 1 "register_operand") + (SBINQOPS:<VWIDE> + (match_operand:<VWIDE> 1 "register_operand") + (match_dup 1)) (match_operand:VQ_HSI 2 "register_operand") (match_operand:VQ_HSI 3 "register_operand")] "TARGET_SIMD" { rtx p = aarch64_simd_vect_par_cnst_half (<MODE>mode, <nunits>, true); - emit_insn (gen_aarch64_sqdmlsl2<mode>_internal (operands[0], operands[1], - operands[2], operands[3], p)); + emit_insn (gen_aarch64_sqdml<SBINQOPS:as>l2<mode>_internal (operands[0], + operands[1], operands[2], + operands[3], p)); DONE; }) ;; vqdml[sa]l2_lane -(define_insn "aarch64_sqdml<SBINQOPS:as>l2_lane<mode>_internal" +(define_insn "aarch64_sqdmlsl2_lane<mode>_internal" [(set (match_operand:<VWIDE> 0 "register_operand" "=w") - (SBINQOPS:<VWIDE> + (ss_minus:<VWIDE> (match_operand:<VWIDE> 1 "register_operand" "0") (ss_ashift:<VWIDE> (mult:<VWIDE> @@ -5405,14 +5395,40 @@ { operands[4] = aarch64_endian_lane_rtx (<VCOND>mode, INTVAL (operands[4])); return - "sqdml<SBINQOPS:as>l2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[%4]"; + "sqdmlsl2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[%4]"; } [(set_attr "type" "neon_sat_mla_<Vetype>_scalar_long")] ) -(define_insn "aarch64_sqdml<SBINQOPS:as>l2_laneq<mode>_internal" +(define_insn "aarch64_sqdmlal2_lane<mode>_internal" [(set (match_operand:<VWIDE> 0 "register_operand" "=w") - (SBINQOPS:<VWIDE> + (ss_plus:<VWIDE> + (ss_ashift:<VWIDE> + (mult:<VWIDE> + (sign_extend:<VWIDE> + (vec_select:<VHALF> + (match_operand:VQ_HSI 2 "register_operand" "w") + (match_operand:VQ_HSI 5 "vect_par_cnst_hi_half" ""))) + (sign_extend:<VWIDE> + (vec_duplicate:<VHALF> + (vec_select:<VEL> + (match_operand:<VCOND> 3 "register_operand" "<vwx>") + (parallel [(match_operand:SI 4 "immediate_operand" "i")]) + )))) + (const_int 1)) + (match_operand:<VWIDE> 1 "register_operand" "0")))] + "TARGET_SIMD" + { + operands[4] = aarch64_endian_lane_rtx (<VCOND>mode, INTVAL (operands[4])); + return + "sqdmlal2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[%4]"; + } + [(set_attr "type" "neon_sat_mla_<Vetype>_scalar_long")] +) + +(define_insn "aarch64_sqdmlsl2_laneq<mode>_internal" + [(set (match_operand:<VWIDE> 0 "register_operand" "=w") + (ss_minus:<VWIDE> (match_operand:<VWIDE> 1 "register_operand" "0") (ss_ashift:<VWIDE> (mult:<VWIDE> @@ -5431,74 +5447,74 @@ { operands[4] = aarch64_endian_lane_rtx (<VCONQ>mode, INTVAL (operands[4])); return - "sqdml<SBINQOPS:as>l2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[%4]"; + "sqdmlsl2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[%4]"; } [(set_attr "type" "neon_sat_mla_<Vetype>_scalar_long")] ) -(define_expand "aarch64_sqdmlal2_lane<mode>" - [(match_operand:<VWIDE> 0 "register_operand") - (match_operand:<VWIDE> 1 "register_operand") - (match_operand:VQ_HSI 2 "register_operand") - (match_operand:<VCOND> 3 "register_operand") - (match_operand:SI 4 "immediate_operand")] - "TARGET_SIMD" -{ - rtx p = aarch64_simd_vect_par_cnst_half (<MODE>mode, <nunits>, true); - emit_insn (gen_aarch64_sqdmlal2_lane<mode>_internal (operands[0], operands[1], - operands[2], operands[3], - operands[4], p)); - DONE; -}) - -(define_expand "aarch64_sqdmlal2_laneq<mode>" - [(match_operand:<VWIDE> 0 "register_operand") - (match_operand:<VWIDE> 1 "register_operand") - (match_operand:VQ_HSI 2 "register_operand") - (match_operand:<VCONQ> 3 "register_operand") - (match_operand:SI 4 "immediate_operand")] +(define_insn "aarch64_sqdmlal2_laneq<mode>_internal" + [(set (match_operand:<VWIDE> 0 "register_operand" "=w") + (ss_plus:<VWIDE> + (ss_ashift:<VWIDE> + (mult:<VWIDE> + (sign_extend:<VWIDE> + (vec_select:<VHALF> + (match_operand:VQ_HSI 2 "register_operand" "w") + (match_operand:VQ_HSI 5 "vect_par_cnst_hi_half" ""))) + (sign_extend:<VWIDE> + (vec_duplicate:<VHALF> + (vec_select:<VEL> + (match_operand:<VCONQ> 3 "register_operand" "<vwx>") + (parallel [(match_operand:SI 4 "immediate_operand" "i")]) + )))) + (const_int 1)) + (match_operand:<VWIDE> 1 "register_operand" "0")))] "TARGET_SIMD" -{ - rtx p = aarch64_simd_vect_par_cnst_half (<MODE>mode, <nunits>, true); - emit_insn (gen_aarch64_sqdmlal2_laneq<mode>_internal (operands[0], operands[1], - operands[2], operands[3], - operands[4], p)); - DONE; -}) + { + operands[4] = aarch64_endian_lane_rtx (<VCONQ>mode, INTVAL (operands[4])); + return + "sqdmlal2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[%4]"; + } + [(set_attr "type" "neon_sat_mla_<Vetype>_scalar_long")] +) -(define_expand "aarch64_sqdmlsl2_lane<mode>" +(define_expand "aarch64_sqdml<SBINQOPS:as>l2_lane<mode>" [(match_operand:<VWIDE> 0 "register_operand") - (match_operand:<VWIDE> 1 "register_operand") + (SBINQOPS:<VWIDE> + (match_operand:<VWIDE> 1 "register_operand") + (match_dup 1)) (match_operand:VQ_HSI 2 "register_operand") (match_operand:<VCOND> 3 "register_operand") (match_operand:SI 4 "immediate_operand")] "TARGET_SIMD" { rtx p = aarch64_simd_vect_par_cnst_half (<MODE>mode, <nunits>, true); - emit_insn (gen_aarch64_sqdmlsl2_lane<mode>_internal (operands[0], operands[1], - operands[2], operands[3], - operands[4], p)); + emit_insn (gen_aarch64_sqdml<SBINQOPS:as>l2_lane<mode>_internal (operands[0], + operands[1], operands[2], + operands[3], operands[4], p)); DONE; }) -(define_expand "aarch64_sqdmlsl2_laneq<mode>" +(define_expand "aarch64_sqdml<SBINQOPS:as>l2_laneq<mode>" [(match_operand:<VWIDE> 0 "register_operand") - (match_operand:<VWIDE> 1 "register_operand") + (SBINQOPS:<VWIDE> + (match_operand:<VWIDE> 1 "register_operand") + (match_dup 1)) (match_operand:VQ_HSI 2 "register_operand") (match_operand:<VCONQ> 3 "register_operand") (match_operand:SI 4 "immediate_operand")] "TARGET_SIMD" { rtx p = aarch64_simd_vect_par_cnst_half (<MODE>mode, <nunits>, true); - emit_insn (gen_aarch64_sqdmlsl2_laneq<mode>_internal (operands[0], operands[1], - operands[2], operands[3], - operands[4], p)); + emit_insn (gen_aarch64_sqdml<SBINQOPS:as>l2_laneq<mode>_internal (operands[0], + operands[1], operands[2], + operands[3], operands[4], p)); DONE; }) -(define_insn "aarch64_sqdml<SBINQOPS:as>l2_n<mode>_internal" +(define_insn "aarch64_sqdmlsl2_n<mode>_internal" [(set (match_operand:<VWIDE> 0 "register_operand" "=w") - (SBINQOPS:<VWIDE> + (ss_minus:<VWIDE> (match_operand:<VWIDE> 1 "register_operand" "0") (ss_ashift:<VWIDE> (mult:<VWIDE> @@ -5511,35 +5527,42 @@ (match_operand:<VEL> 3 "register_operand" "<vwx>")))) (const_int 1))))] "TARGET_SIMD" - "sqdml<SBINQOPS:as>l2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[0]" + "sqdmlsl2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[0]" [(set_attr "type" "neon_sat_mla_<Vetype>_scalar_long")] ) -(define_expand "aarch64_sqdmlal2_n<mode>" - [(match_operand:<VWIDE> 0 "register_operand") - (match_operand:<VWIDE> 1 "register_operand") - (match_operand:VQ_HSI 2 "register_operand") - (match_operand:<VEL> 3 "register_operand")] +(define_insn "aarch64_sqdmlal2_n<mode>_internal" + [(set (match_operand:<VWIDE> 0 "register_operand" "=w") + (ss_plus:<VWIDE> + (ss_ashift:<VWIDE> + (mult:<VWIDE> + (sign_extend:<VWIDE> + (vec_select:<VHALF> + (match_operand:VQ_HSI 2 "register_operand" "w") + (match_operand:VQ_HSI 4 "vect_par_cnst_hi_half" ""))) + (sign_extend:<VWIDE> + (vec_duplicate:<VHALF> + (match_operand:<VEL> 3 "register_operand" "<vwx>")))) + (const_int 1)) + (match_operand:<VWIDE> 1 "register_operand" "0")))] "TARGET_SIMD" -{ - rtx p = aarch64_simd_vect_par_cnst_half (<MODE>mode, <nunits>, true); - emit_insn (gen_aarch64_sqdmlal2_n<mode>_internal (operands[0], operands[1], - operands[2], operands[3], - p)); - DONE; -}) + "sqdmlal2\\t%<vw2>0<Vmwtype>, %<v>2<Vmtype>, %3.<Vetype>[0]" + [(set_attr "type" "neon_sat_mla_<Vetype>_scalar_long")] +) -(define_expand "aarch64_sqdmlsl2_n<mode>" +(define_expand "aarch64_sqdml<SBINQOPS:as>l2_n<mode>" [(match_operand:<VWIDE> 0 "register_operand") - (match_operand:<VWIDE> 1 "register_operand") + (SBINQOPS:<VWIDE> + (match_operand:<VWIDE> 1 "register_operand") + (match_dup 1)) (match_operand:VQ_HSI 2 "register_operand") (match_operand:<VEL> 3 "register_operand")] "TARGET_SIMD" { rtx p = aarch64_simd_vect_par_cnst_half (<MODE>mode, <nunits>, true); - emit_insn (gen_aarch64_sqdmlsl2_n<mode>_internal (operands[0], operands[1], - operands[2], operands[3], - p)); + emit_insn (gen_aarch64_sqdml<SBINQOPS:as>l2_n<mode>_internal (operands[0], + operands[1], operands[2], + operands[3], p)); DONE; }) diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 2521541..ffccaa7 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -373,7 +373,7 @@ extern void arm_emit_coreregs_64bit_shift (enum rtx_code, rtx, rtx, rtx, rtx, extern bool arm_fusion_enabled_p (tune_params::fuse_ops); extern bool arm_valid_symbolic_address_p (rtx); extern bool arm_validize_comparison (rtx *, rtx *, rtx *); -extern bool arm_expand_vector_compare (rtx, rtx_code, rtx, rtx, bool); +extern bool arm_expand_vector_compare (rtx, rtx_code, rtx, rtx, bool, bool); #endif /* RTX_CODE */ extern bool arm_gen_setmem (rtx *); diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 2962071..eee3671 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -5563,9 +5563,20 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1, return; *op1 = GEN_INT (i + 1); *code = *code == GT ? GE : LT; - return; } - break; + else + { + /* GT maxval is always false, LE maxval is always true. + We can't fold that away here as we must make a + comparison, but we can fold them to comparisons + with the same result that can be handled: + op0 GT maxval -> op0 LT minval + op0 LE maxval -> op0 GE minval + where minval = (-maxval - 1). */ + *op1 = GEN_INT (-maxval - 1); + *code = *code == GT ? LT : GE; + } + return; case GTU: case LEU: @@ -5578,9 +5589,19 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1, return; *op1 = GEN_INT (i + 1); *code = *code == GTU ? GEU : LTU; - return; } - break; + else + { + /* GTU ~0 is always false, LEU ~0 is always true. + We can't fold that away here as we must make a + comparison, but we can fold them to comparisons + with the same result that can be handled: + op0 GTU ~0 -> op0 LTU 0 + op0 LEU ~0 -> op0 GEU 0. */ + *op1 = const0_rtx; + *code = *code == GTU ? LTU : GEU; + } + return; default: gcc_unreachable (); @@ -30938,66 +30959,113 @@ arm_split_atomic_op (enum rtx_code code, rtx old_out, rtx new_out, rtx mem, and return true if TARGET contains the inverse. If !CAN_INVERT, always store the result in TARGET, never its inverse. + If VCOND_MVE, do not emit the vpsel instruction here, let arm_expand_vcond do + it with the right destination type to avoid emiting two vpsel, one here and + one in arm_expand_vcond. + Note that the handling of floating-point comparisons is not IEEE compliant. */ bool arm_expand_vector_compare (rtx target, rtx_code code, rtx op0, rtx op1, - bool can_invert) + bool can_invert, bool vcond_mve) { machine_mode cmp_result_mode = GET_MODE (target); machine_mode cmp_mode = GET_MODE (op0); bool inverted; - switch (code) - { - /* For these we need to compute the inverse of the requested - comparison. */ - case UNORDERED: - case UNLT: - case UNLE: - case UNGT: - case UNGE: - case UNEQ: - case NE: - code = reverse_condition_maybe_unordered (code); - if (!can_invert) - { - /* Recursively emit the inverted comparison into a temporary - and then store its inverse in TARGET. This avoids reusing - TARGET (which for integer NE could be one of the inputs). */ - rtx tmp = gen_reg_rtx (cmp_result_mode); - if (arm_expand_vector_compare (tmp, code, op0, op1, true)) - gcc_unreachable (); - emit_insn (gen_rtx_SET (target, gen_rtx_NOT (cmp_result_mode, tmp))); - return false; - } - inverted = true; - break; - default: + /* MVE supports more comparisons than Neon. */ + if (TARGET_HAVE_MVE) inverted = false; - break; - } + else + switch (code) + { + /* For these we need to compute the inverse of the requested + comparison. */ + case UNORDERED: + case UNLT: + case UNLE: + case UNGT: + case UNGE: + case UNEQ: + case NE: + code = reverse_condition_maybe_unordered (code); + if (!can_invert) + { + /* Recursively emit the inverted comparison into a temporary + and then store its inverse in TARGET. This avoids reusing + TARGET (which for integer NE could be one of the inputs). */ + rtx tmp = gen_reg_rtx (cmp_result_mode); + if (arm_expand_vector_compare (tmp, code, op0, op1, true, vcond_mve)) + gcc_unreachable (); + emit_insn (gen_rtx_SET (target, gen_rtx_NOT (cmp_result_mode, tmp))); + return false; + } + inverted = true; + break; + + default: + inverted = false; + break; + } switch (code) { - /* These are natively supported for zero comparisons, but otherwise - require the operands to be swapped. */ + /* These are natively supported by Neon for zero comparisons, but otherwise + require the operands to be swapped. For MVE, we can only compare + registers. */ case LE: case LT: - if (op1 != CONST0_RTX (cmp_mode)) - { - code = swap_condition (code); - std::swap (op0, op1); - } + if (!TARGET_HAVE_MVE) + if (op1 != CONST0_RTX (cmp_mode)) + { + code = swap_condition (code); + std::swap (op0, op1); + } /* Fall through. */ - /* These are natively supported for both register and zero operands. */ + /* These are natively supported by Neon for both register and zero + operands. MVE supports registers only. */ case EQ: case GE: case GT: - emit_insn (gen_neon_vc (code, cmp_mode, target, op0, op1)); + case NE: + if (TARGET_HAVE_MVE) + { + rtx vpr_p0; + if (vcond_mve) + vpr_p0 = target; + else + vpr_p0 = gen_reg_rtx (HImode); + + switch (GET_MODE_CLASS (cmp_mode)) + { + case MODE_VECTOR_INT: + emit_insn (gen_mve_vcmpq (code, cmp_mode, vpr_p0, op0, force_reg (cmp_mode, op1))); + break; + case MODE_VECTOR_FLOAT: + if (TARGET_HAVE_MVE_FLOAT) + emit_insn (gen_mve_vcmpq_f (code, cmp_mode, vpr_p0, op0, force_reg (cmp_mode, op1))); + else + gcc_unreachable (); + break; + default: + gcc_unreachable (); + } + + /* If we are not expanding a vcond, build the result here. */ + if (!vcond_mve) + { + rtx zero = gen_reg_rtx (cmp_result_mode); + rtx one = gen_reg_rtx (cmp_result_mode); + emit_move_insn (zero, CONST0_RTX (cmp_result_mode)); + emit_move_insn (one, CONST1_RTX (cmp_result_mode)); + emit_insn (gen_mve_vpselq (VPSELQ_S, cmp_result_mode, target, one, zero, vpr_p0)); + } + } + else + emit_insn (gen_neon_vc (code, cmp_mode, target, op0, op1)); return inverted; /* These are natively supported for register operands only. @@ -31005,16 +31073,54 @@ arm_expand_vector_compare (rtx target, rtx_code code, rtx op0, rtx op1, or canonicalized by target-independent code. */ case GEU: case GTU: - emit_insn (gen_neon_vc (code, cmp_mode, target, - op0, force_reg (cmp_mode, op1))); + if (TARGET_HAVE_MVE) + { + rtx vpr_p0; + if (vcond_mve) + vpr_p0 = target; + else + vpr_p0 = gen_reg_rtx (HImode); + + emit_insn (gen_mve_vcmpq (code, cmp_mode, vpr_p0, op0, force_reg (cmp_mode, op1))); + if (!vcond_mve) + { + rtx zero = gen_reg_rtx (cmp_result_mode); + rtx one = gen_reg_rtx (cmp_result_mode); + emit_move_insn (zero, CONST0_RTX (cmp_result_mode)); + emit_move_insn (one, CONST1_RTX (cmp_result_mode)); + emit_insn (gen_mve_vpselq (VPSELQ_S, cmp_result_mode, target, one, zero, vpr_p0)); + } + } + else + emit_insn (gen_neon_vc (code, cmp_mode, target, + op0, force_reg (cmp_mode, op1))); return inverted; /* These require the operands to be swapped and likewise do not support comparisons with zero. */ case LEU: case LTU: - emit_insn (gen_neon_vc (swap_condition (code), cmp_mode, - target, force_reg (cmp_mode, op1), op0)); + if (TARGET_HAVE_MVE) + { + rtx vpr_p0; + if (vcond_mve) + vpr_p0 = target; + else + vpr_p0 = gen_reg_rtx (HImode); + + emit_insn (gen_mve_vcmpq (swap_condition (code), cmp_mode, vpr_p0, force_reg (cmp_mode, op1), op0)); + if (!vcond_mve) + { + rtx zero = gen_reg_rtx (cmp_result_mode); + rtx one = gen_reg_rtx (cmp_result_mode); + emit_move_insn (zero, CONST0_RTX (cmp_result_mode)); + emit_move_insn (one, CONST1_RTX (cmp_result_mode)); + emit_insn (gen_mve_vpselq (VPSELQ_S, cmp_result_mode, target, one, zero, vpr_p0)); + } + } + else + emit_insn (gen_neon_vc (swap_condition (code), cmp_mode, + target, force_reg (cmp_mode, op1), op0)); return inverted; /* These need a combination of two comparisons. */ @@ -31026,8 +31132,8 @@ arm_expand_vector_compare (rtx target, rtx_code code, rtx op0, rtx op1, rtx gt_res = gen_reg_rtx (cmp_result_mode); rtx alt_res = gen_reg_rtx (cmp_result_mode); rtx_code alt_code = (code == LTGT ? LT : LE); - if (arm_expand_vector_compare (gt_res, GT, op0, op1, true) - || arm_expand_vector_compare (alt_res, alt_code, op0, op1, true)) + if (arm_expand_vector_compare (gt_res, GT, op0, op1, true, vcond_mve) + || arm_expand_vector_compare (alt_res, alt_code, op0, op1, true, vcond_mve)) gcc_unreachable (); emit_insn (gen_rtx_SET (target, gen_rtx_IOR (cmp_result_mode, gt_res, alt_res))); @@ -31045,13 +31151,47 @@ arm_expand_vector_compare (rtx target, rtx_code code, rtx op0, rtx op1, void arm_expand_vcond (rtx *operands, machine_mode cmp_result_mode) { - rtx mask = gen_reg_rtx (cmp_result_mode); + /* When expanding for MVE, we do not want to emit a (useless) vpsel in + arm_expand_vector_compare, and another one here. */ + bool vcond_mve=false; + rtx mask; + + if (TARGET_HAVE_MVE) + { + vcond_mve=true; + mask = gen_reg_rtx (HImode); + } + else + mask = gen_reg_rtx (cmp_result_mode); + bool inverted = arm_expand_vector_compare (mask, GET_CODE (operands[3]), - operands[4], operands[5], true); + operands[4], operands[5], true, vcond_mve); if (inverted) std::swap (operands[1], operands[2]); + if (TARGET_NEON) emit_insn (gen_neon_vbsl (GET_MODE (operands[0]), operands[0], mask, operands[1], operands[2])); + else + { + machine_mode cmp_mode = GET_MODE (operands[4]); + rtx vpr_p0 = mask; + rtx zero = gen_reg_rtx (cmp_mode); + rtx one = gen_reg_rtx (cmp_mode); + emit_move_insn (zero, CONST0_RTX (cmp_mode)); + emit_move_insn (one, CONST1_RTX (cmp_mode)); + switch (GET_MODE_CLASS (cmp_mode)) + { + case MODE_VECTOR_INT: + emit_insn (gen_mve_vpselq (VPSELQ_S, cmp_result_mode, operands[0], one, zero, vpr_p0)); + break; + case MODE_VECTOR_FLOAT: + if (TARGET_HAVE_MVE_FLOAT) + emit_insn (gen_mve_vpselq_f (cmp_mode, operands[0], one, zero, vpr_p0)); + break; + default: + gcc_unreachable (); + } + } } #define MAX_VECT_LEN 16 diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h index e430e4d..8e5bd57 100644 --- a/gcc/config/arm/arm.h +++ b/gcc/config/arm/arm.h @@ -89,9 +89,7 @@ extern tree arm_bf16_ptr_type_node; #undef CPP_SPEC -#define CPP_SPEC "%(subtarget_cpp_spec) \ -%{mbig-endian:%{mlittle-endian: \ - %e-mbig-endian and -mlittle-endian may not be used together}}" +#define CPP_SPEC "%(subtarget_cpp_spec)" #ifndef CC1_SPEC #define CC1_SPEC "" diff --git a/gcc/config/arm/arm_neon.h b/gcc/config/arm/arm_neon.h index dc28b92..dcd533f 100644 --- a/gcc/config/arm/arm_neon.h +++ b/gcc/config/arm/arm_neon.h @@ -2919,112 +2919,112 @@ __extension__ extern __inline uint8x8_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtst_s8 (int8x8_t __a, int8x8_t __b) { - return (uint8x8_t)__builtin_neon_vtstv8qi (__a, __b); + return (uint8x8_t) ((__a & __b) != 0); } __extension__ extern __inline uint16x4_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtst_s16 (int16x4_t __a, int16x4_t __b) { - return (uint16x4_t)__builtin_neon_vtstv4hi (__a, __b); + return (uint16x4_t) ((__a & __b) != 0); } __extension__ extern __inline uint32x2_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtst_s32 (int32x2_t __a, int32x2_t __b) { - return (uint32x2_t)__builtin_neon_vtstv2si (__a, __b); + return (uint32x2_t) ((__a & __b) != 0); } __extension__ extern __inline uint8x8_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtst_u8 (uint8x8_t __a, uint8x8_t __b) { - return (uint8x8_t)__builtin_neon_vtstv8qi ((int8x8_t) __a, (int8x8_t) __b); + return (uint8x8_t) ((__a & __b) != 0); } __extension__ extern __inline uint16x4_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtst_u16 (uint16x4_t __a, uint16x4_t __b) { - return (uint16x4_t)__builtin_neon_vtstv4hi ((int16x4_t) __a, (int16x4_t) __b); + return (uint16x4_t) ((__a & __b) != 0); } __extension__ extern __inline uint32x2_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtst_u32 (uint32x2_t __a, uint32x2_t __b) { - return (uint32x2_t)__builtin_neon_vtstv2si ((int32x2_t) __a, (int32x2_t) __b); + return (uint32x2_t) ((__a & __b) != 0); } __extension__ extern __inline uint8x8_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtst_p8 (poly8x8_t __a, poly8x8_t __b) { - return (uint8x8_t)__builtin_neon_vtstv8qi ((int8x8_t) __a, (int8x8_t) __b); + return (uint8x8_t) ((__a & __b) != 0); } __extension__ extern __inline uint16x4_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtst_p16 (poly16x4_t __a, poly16x4_t __b) { - return (uint16x4_t)__builtin_neon_vtstv4hi ((int16x4_t) __a, (int16x4_t) __b); + return (uint16x4_t) ((__a & __b) != 0); } __extension__ extern __inline uint8x16_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtstq_s8 (int8x16_t __a, int8x16_t __b) { - return (uint8x16_t)__builtin_neon_vtstv16qi (__a, __b); + return (uint8x16_t) ((__a & __b) != 0); } __extension__ extern __inline uint16x8_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtstq_s16 (int16x8_t __a, int16x8_t __b) { - return (uint16x8_t)__builtin_neon_vtstv8hi (__a, __b); + return (uint16x8_t) ((__a & __b) != 0); } __extension__ extern __inline uint32x4_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtstq_s32 (int32x4_t __a, int32x4_t __b) { - return (uint32x4_t)__builtin_neon_vtstv4si (__a, __b); + return (uint32x4_t) ((__a & __b) != 0); } __extension__ extern __inline uint8x16_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtstq_u8 (uint8x16_t __a, uint8x16_t __b) { - return (uint8x16_t)__builtin_neon_vtstv16qi ((int8x16_t) __a, (int8x16_t) __b); + return (uint8x16_t) ((__a & __b) != 0); } __extension__ extern __inline uint16x8_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtstq_u16 (uint16x8_t __a, uint16x8_t __b) { - return (uint16x8_t)__builtin_neon_vtstv8hi ((int16x8_t) __a, (int16x8_t) __b); + return (uint16x8_t) ((__a & __b) != 0); } __extension__ extern __inline uint32x4_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtstq_u32 (uint32x4_t __a, uint32x4_t __b) { - return (uint32x4_t)__builtin_neon_vtstv4si ((int32x4_t) __a, (int32x4_t) __b); + return (uint32x4_t) ((__a & __b) != 0); } __extension__ extern __inline uint8x16_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtstq_p8 (poly8x16_t __a, poly8x16_t __b) { - return (uint8x16_t)__builtin_neon_vtstv16qi ((int8x16_t) __a, (int8x16_t) __b); + return (uint8x16_t) ((__a & __b) != 0); } __extension__ extern __inline uint16x8_t __attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) vtstq_p16 (poly16x8_t __a, poly16x8_t __b) { - return (uint16x8_t)__builtin_neon_vtstv8hi ((int16x8_t) __a, (int16x8_t) __b); + return (uint16x8_t) ((__a & __b) != 0); } __extension__ extern __inline int8x8_t diff --git a/gcc/config/arm/arm_neon_builtins.def b/gcc/config/arm/arm_neon_builtins.def index 97e4f9c..70438ac 100644 --- a/gcc/config/arm/arm_neon_builtins.def +++ b/gcc/config/arm/arm_neon_builtins.def @@ -142,7 +142,6 @@ VAR2 (UNOP, vcgez, v4hf, v8hf) VAR2 (UNOP, vcgtz, v4hf, v8hf) VAR2 (UNOP, vclez, v4hf, v8hf) VAR2 (UNOP, vcltz, v4hf, v8hf) -VAR6 (BINOP, vtst, v8qi, v4hi, v2si, v16qi, v8hi, v4si) VAR6 (BINOP, vabds, v8qi, v4hi, v2si, v16qi, v8hi, v4si) VAR6 (BINOP, vabdu, v8qi, v4hi, v2si, v16qi, v8hi, v4si) VAR2 (BINOP, vabdf, v2sf, v4sf) diff --git a/gcc/config/arm/iterators.md b/gcc/config/arm/iterators.md index 95df8bd..3042baf 100644 --- a/gcc/config/arm/iterators.md +++ b/gcc/config/arm/iterators.md @@ -231,6 +231,9 @@ ;; Vector modes for 16-bit floating-point support. (define_mode_iterator VH [V8HF V4HF]) +;; Modes with 16-bit elements only. +(define_mode_iterator V16 [V4HI V4HF V8HI V8HF]) + ;; 16-bit floating-point vector modes suitable for moving (includes BFmode). (define_mode_iterator VHFBF [V8HF V4HF V4BF V8BF]) @@ -571,6 +574,8 @@ ;; (Opposite) mode to convert to/from for vector-half mode conversions. (define_mode_attr VH_CVTTO [(V4HI "V4HF") (V4HF "V4HI") (V8HI "V8HF") (V8HF "V8HI")]) +(define_mode_attr VH_cvtto [(V4HI "v4hf") (V4HF "v4hi") + (V8HI "v8hf") (V8HF "v8hi")]) ;; Define element mode for each vector mode. (define_mode_attr V_elem [(V8QI "QI") (V16QI "QI") @@ -720,6 +725,7 @@ (define_mode_attr v_cmp_result [(V8QI "v8qi") (V16QI "v16qi") (V4HI "v4hi") (V8HI "v8hi") (V2SI "v2si") (V4SI "v4si") + (V4HF "v4hi") (V8HF "v8hi") (DI "di") (V2DI "v2di") (V2SF "v2si") (V4SF "v4si")]) @@ -1288,12 +1294,11 @@ (VCREATEQ_U "u") (VCREATEQ_S "s") (VSHRQ_N_S "s") (VSHRQ_N_U "u") (VCVTQ_N_FROM_F_S "s") (VSHLQ_U "u") (VCVTQ_N_FROM_F_U "u") (VADDLVQ_P_S "s") (VSHLQ_S "s") - (VADDLVQ_P_U "u") (VCMPNEQ_S "s") + (VADDLVQ_P_U "u") (VABDQ_M_S "s") (VABDQ_M_U "u") (VABDQ_S "s") (VABDQ_U "u") (VADDQ_N_S "s") (VADDQ_N_U "u") (VADDVQ_P_S "s") (VADDVQ_P_U "u") (VBRSRQ_N_S "s") - (VBRSRQ_N_U "u") (VCMPEQQ_S "s") - (VCMPEQQ_N_S "s") (VCMPNEQ_N_S "s") + (VBRSRQ_N_U "u") (VHADDQ_N_S "s") (VHADDQ_N_U "u") (VHADDQ_S "s") (VHADDQ_U "u") (VHSUBQ_N_S "s") (VHSUBQ_N_U "u") (VHSUBQ_S "s") (VMAXQ_S "s") (VMAXQ_U "u") (VHSUBQ_U "u") @@ -1549,16 +1554,12 @@ (define_int_iterator VSHRQ_N [VSHRQ_N_S VSHRQ_N_U]) (define_int_iterator VCVTQ_N_FROM_F [VCVTQ_N_FROM_F_S VCVTQ_N_FROM_F_U]) (define_int_iterator VADDLVQ_P [VADDLVQ_P_S VADDLVQ_P_U]) -(define_int_iterator VCMPNEQ [VCMPNEQ_S]) (define_int_iterator VSHLQ [VSHLQ_S VSHLQ_U]) (define_int_iterator VABDQ [VABDQ_S VABDQ_U]) (define_int_iterator VADDQ_N [VADDQ_N_S VADDQ_N_U]) (define_int_iterator VADDVAQ [VADDVAQ_S VADDVAQ_U]) (define_int_iterator VADDVQ_P [VADDVQ_P_U VADDVQ_P_S]) (define_int_iterator VBRSRQ_N [VBRSRQ_N_U VBRSRQ_N_S]) -(define_int_iterator VCMPEQQ [VCMPEQQ_S]) -(define_int_iterator VCMPEQQ_N [VCMPEQQ_N_S]) -(define_int_iterator VCMPNEQ_N [VCMPNEQ_N_S]) (define_int_iterator VHADDQ [VHADDQ_S VHADDQ_U]) (define_int_iterator VHADDQ_N [VHADDQ_N_U VHADDQ_N_S]) (define_int_iterator VHSUBQ [VHSUBQ_S VHSUBQ_U]) diff --git a/gcc/config/arm/mve.md b/gcc/config/arm/mve.md index 45df211..133ebe9 100644 --- a/gcc/config/arm/mve.md +++ b/gcc/config/arm/mve.md @@ -813,7 +813,7 @@ ;; ;; [vcmpneq_, vcmpcsq_, vcmpeqq_, vcmpgeq_, vcmpgtq_, vcmphiq_, vcmpleq_, vcmpltq_]) ;; -(define_insn "mve_vcmp<mve_cmp_op>q_<mode>" +(define_insn "@mve_vcmp<mve_cmp_op>q_<mode>" [ (set (match_operand:HI 0 "vpr_register_operand" "=Up") (MVE_COMPARISONS:HI (match_operand:MVE_2 1 "s_register_operand" "w") @@ -1903,7 +1903,7 @@ ;; ;; [vcmpeqq_f, vcmpgeq_f, vcmpgtq_f, vcmpleq_f, vcmpltq_f, vcmpneq_f]) ;; -(define_insn "mve_vcmp<mve_cmp_op>q_f<mode>" +(define_insn "@mve_vcmp<mve_cmp_op>q_f<mode>" [ (set (match_operand:HI 0 "vpr_register_operand" "=Up") (MVE_FP_COMPARISONS:HI (match_operand:MVE_0 1 "s_register_operand" "w") @@ -1917,7 +1917,7 @@ ;; ;; [vcmpeqq_n_f, vcmpgeq_n_f, vcmpgtq_n_f, vcmpleq_n_f, vcmpltq_n_f, vcmpneq_n_f]) ;; -(define_insn "mve_vcmp<mve_cmp_op>q_n_f<mode>" +(define_insn "@mve_vcmp<mve_cmp_op>q_n_f<mode>" [ (set (match_operand:HI 0 "vpr_register_operand" "=Up") (MVE_FP_COMPARISONS:HI (match_operand:MVE_0 1 "s_register_operand" "w") @@ -3282,7 +3282,7 @@ ;; ;; [vpselq_u, vpselq_s]) ;; -(define_insn "mve_vpselq_<supf><mode>" +(define_insn "@mve_vpselq_<supf><mode>" [ (set (match_operand:MVE_1 0 "s_register_operand" "=w") (unspec:MVE_1 [(match_operand:MVE_1 1 "s_register_operand" "w") @@ -4377,7 +4377,7 @@ ;; ;; [vpselq_f]) ;; -(define_insn "mve_vpselq_f<mode>" +(define_insn "@mve_vpselq_f<mode>" [ (set (match_operand:MVE_0 0 "s_register_operand" "=w") (unspec:MVE_0 [(match_operand:MVE_0 1 "s_register_operand" "w") diff --git a/gcc/config/arm/neon.md b/gcc/config/arm/neon.md index 2a1e304..cc82d06 100644 --- a/gcc/config/arm/neon.md +++ b/gcc/config/arm/neon.md @@ -1416,93 +1416,6 @@ [(set_attr "type" "neon_qsub<q>")] ) -(define_expand "vec_cmp<mode><v_cmp_result>" - [(set (match_operand:<V_cmp_result> 0 "s_register_operand") - (match_operator:<V_cmp_result> 1 "comparison_operator" - [(match_operand:VDQW 2 "s_register_operand") - (match_operand:VDQW 3 "reg_or_zero_operand")]))] - "TARGET_NEON && (!<Is_float_mode> || flag_unsafe_math_optimizations)" -{ - arm_expand_vector_compare (operands[0], GET_CODE (operands[1]), - operands[2], operands[3], false); - DONE; -}) - -(define_expand "vec_cmpu<mode><mode>" - [(set (match_operand:VDQIW 0 "s_register_operand") - (match_operator:VDQIW 1 "comparison_operator" - [(match_operand:VDQIW 2 "s_register_operand") - (match_operand:VDQIW 3 "reg_or_zero_operand")]))] - "TARGET_NEON" -{ - arm_expand_vector_compare (operands[0], GET_CODE (operands[1]), - operands[2], operands[3], false); - DONE; -}) - -;; Conditional instructions. These are comparisons with conditional moves for -;; vectors. They perform the assignment: -;; -;; Vop0 = (Vop4 <op3> Vop5) ? Vop1 : Vop2; -;; -;; where op3 is <, <=, ==, !=, >= or >. Operations are performed -;; element-wise. - -(define_expand "vcond<mode><mode>" - [(set (match_operand:VDQW 0 "s_register_operand") - (if_then_else:VDQW - (match_operator 3 "comparison_operator" - [(match_operand:VDQW 4 "s_register_operand") - (match_operand:VDQW 5 "reg_or_zero_operand")]) - (match_operand:VDQW 1 "s_register_operand") - (match_operand:VDQW 2 "s_register_operand")))] - "TARGET_NEON && (!<Is_float_mode> || flag_unsafe_math_optimizations)" -{ - arm_expand_vcond (operands, <V_cmp_result>mode); - DONE; -}) - -(define_expand "vcond<V_cvtto><mode>" - [(set (match_operand:<V_CVTTO> 0 "s_register_operand") - (if_then_else:<V_CVTTO> - (match_operator 3 "comparison_operator" - [(match_operand:V32 4 "s_register_operand") - (match_operand:V32 5 "reg_or_zero_operand")]) - (match_operand:<V_CVTTO> 1 "s_register_operand") - (match_operand:<V_CVTTO> 2 "s_register_operand")))] - "TARGET_NEON && (!<Is_float_mode> || flag_unsafe_math_optimizations)" -{ - arm_expand_vcond (operands, <V_cmp_result>mode); - DONE; -}) - -(define_expand "vcondu<mode><v_cmp_result>" - [(set (match_operand:VDQW 0 "s_register_operand") - (if_then_else:VDQW - (match_operator 3 "arm_comparison_operator" - [(match_operand:<V_cmp_result> 4 "s_register_operand") - (match_operand:<V_cmp_result> 5 "reg_or_zero_operand")]) - (match_operand:VDQW 1 "s_register_operand") - (match_operand:VDQW 2 "s_register_operand")))] - "TARGET_NEON" -{ - arm_expand_vcond (operands, <V_cmp_result>mode); - DONE; -}) - -(define_expand "vcond_mask_<mode><v_cmp_result>" - [(set (match_operand:VDQW 0 "s_register_operand") - (if_then_else:VDQW - (match_operand:<V_cmp_result> 3 "s_register_operand") - (match_operand:VDQW 1 "s_register_operand") - (match_operand:VDQW 2 "s_register_operand")))] - "TARGET_NEON" -{ - emit_insn (gen_neon_vbsl<mode> (operands[0], operands[3], operands[1], - operands[2])); - DONE; -}) - ;; Patterns for builtins. ; good for plain vadd, vaddq. @@ -2578,16 +2491,6 @@ DONE; }) -(define_insn "neon_vtst<mode>" - [(set (match_operand:VDQIW 0 "s_register_operand" "=w") - (unspec:VDQIW [(match_operand:VDQIW 1 "s_register_operand" "w") - (match_operand:VDQIW 2 "s_register_operand" "w")] - UNSPEC_VTST))] - "TARGET_NEON" - "vtst.<V_sz_elem>\t%<V_reg>0, %<V_reg>1, %<V_reg>2" - [(set_attr "type" "neon_tst<q>")] -) - (define_insn "neon_vtst_combine<mode>" [(set (match_operand:VDQIW 0 "s_register_operand" "=w") (plus:VDQIW diff --git a/gcc/config/arm/unspecs.md b/gcc/config/arm/unspecs.md index 07ca53b..0778db1 100644 --- a/gcc/config/arm/unspecs.md +++ b/gcc/config/arm/unspecs.md @@ -596,8 +596,6 @@ VCVTQ_N_FROM_F_U VADDLVQ_P_S VADDLVQ_P_U - VCMPNEQ_U - VCMPNEQ_S VSHLQ_S VSHLQ_U VABDQ_S @@ -605,9 +603,6 @@ VADDVAQ_S VADDVQ_P_S VBRSRQ_N_S - VCMPEQQ_S - VCMPEQQ_N_S - VCMPNEQ_N_S VHADDQ_S VHADDQ_N_S VHSUBQ_S @@ -645,9 +640,6 @@ VADDVAQ_U VADDVQ_P_U VBRSRQ_N_U - VCMPEQQ_U - VCMPEQQ_N_U - VCMPNEQ_N_U VHADDQ_U VHADDQ_N_U VHSUBQ_U @@ -680,14 +672,6 @@ VSHLQ_R_U VSUBQ_U VSUBQ_N_U - VCMPGEQ_N_S - VCMPGEQ_S - VCMPGTQ_N_S - VCMPGTQ_S - VCMPLEQ_N_S - VCMPLEQ_S - VCMPLTQ_N_S - VCMPLTQ_S VHCADDQ_ROT270_S VHCADDQ_ROT90_S VMAXAQ_S @@ -702,10 +686,6 @@ VQRDMULHQ_N_S VQRDMULHQ_S VQSHLUQ_N_S - VCMPCSQ_N_U - VCMPCSQ_U - VCMPHIQ_N_U - VCMPHIQ_U VABDQ_M_S VABDQ_M_U VABDQ_F diff --git a/gcc/config/arm/vec-common.md b/gcc/config/arm/vec-common.md index 0b2b3b1..265fa40 100644 --- a/gcc/config/arm/vec-common.md +++ b/gcc/config/arm/vec-common.md @@ -362,3 +362,127 @@ DONE; } }) + +(define_expand "vec_cmp<mode><v_cmp_result>" + [(set (match_operand:<V_cmp_result> 0 "s_register_operand") + (match_operator:<V_cmp_result> 1 "comparison_operator" + [(match_operand:VDQWH 2 "s_register_operand") + (match_operand:VDQWH 3 "reg_or_zero_operand")]))] + "ARM_HAVE_<MODE>_ARITH + && !TARGET_REALLY_IWMMXT + && (!<Is_float_mode> || flag_unsafe_math_optimizations)" +{ + arm_expand_vector_compare (operands[0], GET_CODE (operands[1]), + operands[2], operands[3], false, false); + DONE; +}) + +(define_expand "vec_cmpu<mode><mode>" + [(set (match_operand:VDQIW 0 "s_register_operand") + (match_operator:VDQIW 1 "comparison_operator" + [(match_operand:VDQIW 2 "s_register_operand") + (match_operand:VDQIW 3 "reg_or_zero_operand")]))] + "ARM_HAVE_<MODE>_ARITH + && !TARGET_REALLY_IWMMXT" +{ + arm_expand_vector_compare (operands[0], GET_CODE (operands[1]), + operands[2], operands[3], false, false); + DONE; +}) + +;; Conditional instructions. These are comparisons with conditional moves for +;; vectors. They perform the assignment: +;; +;; Vop0 = (Vop4 <op3> Vop5) ? Vop1 : Vop2; +;; +;; where op3 is <, <=, ==, !=, >= or >. Operations are performed +;; element-wise. + +(define_expand "vcond<mode><mode>" + [(set (match_operand:VDQWH 0 "s_register_operand") + (if_then_else:VDQWH + (match_operator 3 "comparison_operator" + [(match_operand:VDQWH 4 "s_register_operand") + (match_operand:VDQWH 5 "reg_or_zero_operand")]) + (match_operand:VDQWH 1 "s_register_operand") + (match_operand:VDQWH 2 "s_register_operand")))] + "ARM_HAVE_<MODE>_ARITH + && !TARGET_REALLY_IWMMXT + && (!<Is_float_mode> || flag_unsafe_math_optimizations)" +{ + arm_expand_vcond (operands, <V_cmp_result>mode); + DONE; +}) + +(define_expand "vcond<V_cvtto><mode>" + [(set (match_operand:<V_CVTTO> 0 "s_register_operand") + (if_then_else:<V_CVTTO> + (match_operator 3 "comparison_operator" + [(match_operand:V32 4 "s_register_operand") + (match_operand:V32 5 "reg_or_zero_operand")]) + (match_operand:<V_CVTTO> 1 "s_register_operand") + (match_operand:<V_CVTTO> 2 "s_register_operand")))] + "ARM_HAVE_<MODE>_ARITH + && !TARGET_REALLY_IWMMXT + && (!<Is_float_mode> || flag_unsafe_math_optimizations)" +{ + arm_expand_vcond (operands, <V_cmp_result>mode); + DONE; +}) + +(define_expand "vcond<VH_cvtto><mode>" + [(set (match_operand:<VH_CVTTO> 0 "s_register_operand") + (if_then_else:<VH_CVTTO> + (match_operator 3 "comparison_operator" + [(match_operand:V16 4 "s_register_operand") + (match_operand:V16 5 "reg_or_zero_operand")]) + (match_operand:<VH_CVTTO> 1 "s_register_operand") + (match_operand:<VH_CVTTO> 2 "s_register_operand")))] + "ARM_HAVE_<MODE>_ARITH + && !TARGET_REALLY_IWMMXT + && (!<Is_float_mode> || flag_unsafe_math_optimizations)" +{ + arm_expand_vcond (operands, <V_cmp_result>mode); + DONE; +}) + +(define_expand "vcondu<mode><v_cmp_result>" + [(set (match_operand:VDQW 0 "s_register_operand") + (if_then_else:VDQW + (match_operator 3 "arm_comparison_operator" + [(match_operand:<V_cmp_result> 4 "s_register_operand") + (match_operand:<V_cmp_result> 5 "reg_or_zero_operand")]) + (match_operand:VDQW 1 "s_register_operand") + (match_operand:VDQW 2 "s_register_operand")))] + "ARM_HAVE_<MODE>_ARITH + && !TARGET_REALLY_IWMMXT" +{ + arm_expand_vcond (operands, <V_cmp_result>mode); + DONE; +}) + +(define_expand "vcond_mask_<mode><v_cmp_result>" + [(set (match_operand:VDQWH 0 "s_register_operand") + (if_then_else:VDQWH + (match_operand:<V_cmp_result> 3 "s_register_operand") + (match_operand:VDQWH 1 "s_register_operand") + (match_operand:VDQWH 2 "s_register_operand")))] + "ARM_HAVE_<MODE>_ARITH + && !TARGET_REALLY_IWMMXT + && (!<Is_float_mode> || flag_unsafe_math_optimizations)" +{ + if (TARGET_NEON) + { + emit_insn (gen_neon_vbsl (<MODE>mode, operands[0], operands[3], + operands[1], operands[2])); + } + else if (TARGET_HAVE_MVE) + { + emit_insn (gen_mve_vpselq (VPSELQ_S, <MODE>mode, operands[0], + operands[1], operands[2], operands[3])); + } + else + gcc_unreachable (); + + DONE; +}) diff --git a/gcc/config/i386/i386-expand.c b/gcc/config/i386/i386-expand.c index 5cfde5b..0fa8d45 100644 --- a/gcc/config/i386/i386-expand.c +++ b/gcc/config/i386/i386-expand.c @@ -3661,7 +3661,8 @@ ix86_expand_sse_movcc (rtx dest, rtx cmp, rtx op_true, rtx op_false) { op_true = force_reg (mode, op_true); - if (!nonimmediate_operand (op_false, mode)) + if (GET_MODE_SIZE (mode) < 16 + || !nonimmediate_operand (op_false, mode)) op_false = force_reg (mode, op_false); emit_insn (gen_rtx_SET (dest, gen_rtx_IF_THEN_ELSE (mode, cmp, @@ -3680,6 +3681,13 @@ ix86_expand_sse_movcc (rtx dest, rtx cmp, rtx op_true, rtx op_false) switch (mode) { + case E_V2SFmode: + if (TARGET_SSE4_1) + { + gen = gen_mmx_blendvps; + op_true = force_reg (mode, op_true); + } + break; case E_V4SFmode: if (TARGET_SSE4_1) gen = gen_sse4_1_blendvps; @@ -18654,6 +18662,242 @@ expand_vec_perm_vperm2f128_vblend (struct expand_vec_perm_d *d) return true; } +/* A subroutine of ix86_expand_vec_perm_const_1. Try to implement + a two vector permutation using two single vector permutations and + {,v}{,p}unpckl{ps,pd,bw,wd,dq}. If two_insn, succeed only if one + of dfirst or dsecond is identity permutation. */ + +static bool +expand_vec_perm_2perm_interleave (struct expand_vec_perm_d *d, bool two_insn) +{ + unsigned i, nelt = d->nelt, nelt2 = nelt / 2, lane = nelt; + struct expand_vec_perm_d dfirst, dsecond, dfinal; + bool ident1 = true, ident2 = true; + + if (d->one_operand_p) + return false; + + if (GET_MODE_SIZE (d->vmode) == 16) + { + if (!TARGET_SSE) + return false; + if (d->vmode != V4SFmode && d->vmode != V2DFmode && !TARGET_SSE2) + return false; + } + else if (GET_MODE_SIZE (d->vmode) == 32) + { + if (!TARGET_AVX) + return false; + if (d->vmode != V8SFmode && d->vmode != V4DFmode && !TARGET_AVX2) + return false; + lane = nelt2; + } + else + return false; + + for (i = 1; i < nelt; i++) + if ((d->perm[i] >= nelt) != ((d->perm[0] >= nelt) ^ (i & 1))) + return false; + + dfirst = *d; + dsecond = *d; + dfinal = *d; + dfirst.op1 = dfirst.op0; + dfirst.one_operand_p = true; + dsecond.op0 = dsecond.op1; + dsecond.one_operand_p = true; + + for (i = 0; i < nelt; i++) + if (d->perm[i] >= nelt) + { + dsecond.perm[i / 2 + (i >= lane ? lane / 2 : 0)] = d->perm[i] - nelt; + if (d->perm[i] - nelt != i / 2 + (i >= lane ? lane / 2 : 0)) + ident2 = false; + dsecond.perm[i / 2 + (i >= lane ? lane : lane / 2)] + = d->perm[i] - nelt; + } + else + { + dfirst.perm[i / 2 + (i >= lane ? lane / 2 : 0)] = d->perm[i]; + if (d->perm[i] != i / 2 + (i >= lane ? lane / 2 : 0)) + ident1 = false; + dfirst.perm[i / 2 + (i >= lane ? lane : lane / 2)] = d->perm[i]; + } + + if (two_insn && !ident1 && !ident2) + return false; + + if (!d->testing_p) + { + if (!ident1) + dfinal.op0 = dfirst.target = gen_reg_rtx (d->vmode); + if (!ident2) + dfinal.op1 = dsecond.target = gen_reg_rtx (d->vmode); + if (d->perm[0] >= nelt) + std::swap (dfinal.op0, dfinal.op1); + } + + bool ok; + rtx_insn *seq1 = NULL, *seq2 = NULL; + + if (!ident1) + { + start_sequence (); + ok = expand_vec_perm_1 (&dfirst); + seq1 = get_insns (); + end_sequence (); + + if (!ok) + return false; + } + + if (!ident2) + { + start_sequence (); + ok = expand_vec_perm_1 (&dsecond); + seq2 = get_insns (); + end_sequence (); + + if (!ok) + return false; + } + + if (d->testing_p) + return true; + + for (i = 0; i < nelt; i++) + { + dfinal.perm[i] = i / 2; + if (i >= lane) + dfinal.perm[i] += lane / 2; + if ((i & 1) != 0) + dfinal.perm[i] += nelt; + } + emit_insn (seq1); + emit_insn (seq2); + ok = expand_vselect_vconcat (dfinal.target, dfinal.op0, dfinal.op1, + dfinal.perm, dfinal.nelt, false); + gcc_assert (ok); + return true; +} + +/* A subroutine of ix86_expand_vec_perm_const_1. Try to simplify + the permutation using two single vector permutations and the SSE4_1 pblendv + instruction. If two_insn, succeed only if one of dfirst or dsecond is + identity permutation. */ + +static bool +expand_vec_perm_2perm_pblendv (struct expand_vec_perm_d *d, bool two_insn) +{ + unsigned i, nelt = d->nelt; + struct expand_vec_perm_d dfirst, dsecond, dfinal; + machine_mode vmode = d->vmode; + bool ident1 = true, ident2 = true; + + /* Use the same checks as in expand_vec_perm_blend. */ + if (d->one_operand_p) + return false; + if (TARGET_AVX2 && GET_MODE_SIZE (vmode) == 32) + ; + else if (TARGET_AVX && (vmode == V4DFmode || vmode == V8SFmode)) + ; + else if (TARGET_SSE4_1 && GET_MODE_SIZE (vmode) == 16) + ; + else + return false; + + dfirst = *d; + dsecond = *d; + dfinal = *d; + dfirst.op1 = dfirst.op0; + dfirst.one_operand_p = true; + dsecond.op0 = dsecond.op1; + dsecond.one_operand_p = true; + + for (i = 0; i < nelt; ++i) + if (d->perm[i] >= nelt) + { + dfirst.perm[i] = 0xff; + dsecond.perm[i] = d->perm[i] - nelt; + if (d->perm[i] != i + nelt) + ident2 = false; + } + else + { + dsecond.perm[i] = 0xff; + dfirst.perm[i] = d->perm[i]; + if (d->perm[i] != i) + ident1 = false; + } + + if (two_insn && !ident1 && !ident2) + return false; + + /* For now. Ideally treat 0xff as a wildcard. */ + for (i = 0; i < nelt; ++i) + if (dfirst.perm[i] == 0xff) + { + if (GET_MODE_SIZE (vmode) == 32 + && dfirst.perm[i ^ (nelt / 2)] != 0xff) + dfirst.perm[i] = dfirst.perm[i ^ (nelt / 2)] ^ (nelt / 2); + else + dfirst.perm[i] = i; + } + else + { + if (GET_MODE_SIZE (vmode) == 32 + && dsecond.perm[i ^ (nelt / 2)] != 0xff) + dsecond.perm[i] = dsecond.perm[i ^ (nelt / 2)] ^ (nelt / 2); + else + dsecond.perm[i] = i; + } + + if (!d->testing_p) + { + if (!ident1) + dfinal.op0 = dfirst.target = gen_reg_rtx (d->vmode); + if (!ident2) + dfinal.op1 = dsecond.target = gen_reg_rtx (d->vmode); + } + + bool ok; + rtx_insn *seq1 = NULL, *seq2 = NULL; + + if (!ident1) + { + start_sequence (); + ok = expand_vec_perm_1 (&dfirst); + seq1 = get_insns (); + end_sequence (); + + if (!ok) + return false; + } + + if (!ident2) + { + start_sequence (); + ok = expand_vec_perm_1 (&dsecond); + seq2 = get_insns (); + end_sequence (); + + if (!ok) + return false; + } + + if (d->testing_p) + return true; + + for (i = 0; i < nelt; ++i) + dfinal.perm[i] = (d->perm[i] >= nelt ? i + nelt : i); + + emit_insn (seq1); + emit_insn (seq2); + ok = expand_vec_perm_blend (&dfinal); + gcc_assert (ok); + return true; +} + /* A subroutine of ix86_expand_vec_perm_const_1. Implement a V4DF permutation using two vperm2f128, followed by a vshufpd insn blending the two vectors together. */ @@ -19765,6 +20009,12 @@ ix86_expand_vec_perm_const_1 (struct expand_vec_perm_d *d) if (expand_vec_perm_pblendv (d)) return true; + if (expand_vec_perm_2perm_interleave (d, true)) + return true; + + if (expand_vec_perm_2perm_pblendv (d, true)) + return true; + /* Try sequences of three instructions. */ if (expand_vec_perm_even_odd_pack (d)) @@ -19782,6 +20032,12 @@ ix86_expand_vec_perm_const_1 (struct expand_vec_perm_d *d) if (expand_vec_perm_vperm2f128_vblend (d)) return true; + if (expand_vec_perm_2perm_interleave (d, false)) + return true; + + if (expand_vec_perm_2perm_pblendv (d, false)) + return true; + /* Try sequences of four instructions. */ if (expand_vec_perm_even_odd_trunc (d)) diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 915f89f..befe69e 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -18000,8 +18000,8 @@ ix86_gimple_fold_builtin (gimple_stmt_iterator *gsi) tree cmp_type = truth_type_for (type); gimple_seq stmts = NULL; tree cmp = gimple_build (&stmts, tcode, cmp_type, arg0, arg1); - gsi_insert_before (gsi, stmts, GSI_SAME_STMT); - gimple *g = gimple_build_assign (gimple_call_lhs (stmt), + gsi_insert_seq_before (gsi, stmts, GSI_SAME_STMT); + gimple* g = gimple_build_assign (gimple_call_lhs (stmt), VEC_COND_EXPR, cmp, minus_one_vec, zero_vec); gimple_set_location (g, loc); @@ -19732,6 +19732,7 @@ ix86_division_cost (const struct processor_costs *cost, static int ix86_shift_rotate_cost (const struct processor_costs *cost, + enum rtx_code code, enum machine_mode mode, bool constant_op1, HOST_WIDE_INT op1_val, bool speed, @@ -19770,6 +19771,19 @@ ix86_shift_rotate_cost (const struct processor_costs *cost, count = 7; return ix86_vec_cost (mode, cost->sse_op * count); } + /* V*DImode arithmetic right shift is emulated. */ + else if (code == ASHIFTRT + && (mode == V2DImode || mode == V4DImode) + && !TARGET_XOP + && !TARGET_AVX512VL) + { + int count = 4; + if (constant_op1 && op1_val == 63 && TARGET_SSE4_2) + count = 2; + else if (constant_op1) + count = 3; + return ix86_vec_cost (mode, cost->sse_op * count); + } else return ix86_vec_cost (mode, cost->sse_op); } @@ -19939,13 +19953,15 @@ ix86_rtx_costs (rtx x, machine_mode mode, int outer_code_i, int opno, case LSHIFTRT: case ROTATERT: bool skip_op0, skip_op1; - *total = ix86_shift_rotate_cost (cost, mode, CONSTANT_P (XEXP (x, 1)), + *total = ix86_shift_rotate_cost (cost, code, mode, + CONSTANT_P (XEXP (x, 1)), CONST_INT_P (XEXP (x, 1)) ? INTVAL (XEXP (x, 1)) : -1, speed, GET_CODE (XEXP (x, 1)) == AND, SUBREG_P (XEXP (x, 1)) - && GET_CODE (XEXP (XEXP (x, 1), 0)) == AND, + && GET_CODE (XEXP (XEXP (x, 1), + 0)) == AND, &skip_op0, &skip_op1); if (skip_op0 || skip_op1) { @@ -22383,11 +22399,16 @@ ix86_add_stmt_cost (class vec_info *vinfo, void *data, int count, case LROTATE_EXPR: case RROTATE_EXPR: { + tree op1 = gimple_assign_rhs1 (stmt_info->stmt); tree op2 = gimple_assign_rhs2 (stmt_info->stmt); stmt_cost = ix86_shift_rotate_cost - (ix86_cost, mode, + (ix86_cost, + (subcode == RSHIFT_EXPR + && !TYPE_UNSIGNED (TREE_TYPE (op1))) + ? ASHIFTRT : LSHIFTRT, mode, TREE_CODE (op2) == INTEGER_CST, - cst_and_fits_in_hwi (op2) ? int_cst_value (op2) : -1, + cst_and_fits_in_hwi (op2) + ? int_cst_value (op2) : -1, true, false, false, NULL, NULL); } break; diff --git a/gcc/config/i386/mmx.md b/gcc/config/i386/mmx.md index f085708..7fc2e5d 100644 --- a/gcc/config/i386/mmx.md +++ b/gcc/config/i386/mmx.md @@ -49,6 +49,7 @@ ;; All 8-byte vector modes handled by MMX (define_mode_iterator MMXMODE [V8QI V4HI V2SI V1DI V2SF]) +(define_mode_iterator MMXMODE124 [V8QI V4HI V2SI V2SF]) ;; Mix-n-match (define_mode_iterator MMXMODE12 [V8QI V4HI]) @@ -56,12 +57,22 @@ (define_mode_iterator MMXMODE24 [V4HI V2SI]) (define_mode_iterator MMXMODE248 [V4HI V2SI V1DI]) +;; All V2S* modes +(define_mode_iterator V2FI [V2SF V2SI]) + ;; Mapping from integer vector mode to mnemonic suffix (define_mode_attr mmxvecsize [(V8QI "b") (V4HI "w") (V2SI "d") (V1DI "q")]) (define_mode_attr mmxdoublemode [(V8QI "V8HI") (V4HI "V4SI")]) +;; Mapping of vector float modes to an integer mode of the same size +(define_mode_attr mmxintvecmode + [(V2SF "V2SI") (V2SI "V2SI") (V4HI "V4HI") (V8QI "V8QI")]) + +(define_mode_attr mmxintvecmodelower + [(V2SF "v2si") (V2SI "v2si") (V4HI "v4hi") (V8QI "v8qi")]) + (define_mode_attr Yv_Yw [(V8QI "Yw") (V4HI "Yw") (V2SI "Yv") (V1DI "Yv") (V2SF "Yv")]) @@ -714,6 +725,85 @@ (set_attr "prefix_extra" "1") (set_attr "mode" "V2SF")]) +(define_insn "*mmx_maskcmpv2sf3_comm" + [(set (match_operand:V2SF 0 "register_operand" "=x,x") + (match_operator:V2SF 3 "sse_comparison_operator" + [(match_operand:V2SF 1 "register_operand" "%0,x") + (match_operand:V2SF 2 "register_operand" "x,x")]))] + "TARGET_MMX_WITH_SSE + && GET_RTX_CLASS (GET_CODE (operands[3])) == RTX_COMM_COMPARE" + "@ + cmp%D3ps\t{%2, %0|%0, %2} + vcmp%D3ps\t{%2, %1, %0|%0, %1, %2}" + [(set_attr "isa" "noavx,avx") + (set_attr "type" "ssecmp") + (set_attr "length_immediate" "1") + (set_attr "prefix" "orig,vex") + (set_attr "mode" "V4SF")]) + +(define_insn "*mmx_maskcmpv2sf3" + [(set (match_operand:V2SF 0 "register_operand" "=x,x") + (match_operator:V2SF 3 "sse_comparison_operator" + [(match_operand:V2SF 1 "register_operand" "0,x") + (match_operand:V2SF 2 "register_operand" "x,x")]))] + "TARGET_MMX_WITH_SSE" + "@ + cmp%D3ps\t{%2, %0|%0, %2} + vcmp%D3ps\t{%2, %1, %0|%0, %1, %2}" + [(set_attr "isa" "noavx,avx") + (set_attr "type" "ssecmp") + (set_attr "length_immediate" "1") + (set_attr "prefix" "orig,vex") + (set_attr "mode" "V4SF")]) + +(define_expand "vec_cmpv2sfv2si" + [(set (match_operand:V2SI 0 "register_operand") + (match_operator:V2SI 1 "" + [(match_operand:V2SF 2 "register_operand") + (match_operand:V2SF 3 "register_operand")]))] + "TARGET_MMX_WITH_SSE" +{ + bool ok = ix86_expand_fp_vec_cmp (operands); + gcc_assert (ok); + DONE; +}) + +(define_expand "vcond<mode>v2sf" + [(set (match_operand:V2FI 0 "register_operand") + (if_then_else:V2FI + (match_operator 3 "" + [(match_operand:V2SF 4 "register_operand") + (match_operand:V2SF 5 "register_operand")]) + (match_operand:V2FI 1) + (match_operand:V2FI 2)))] + "TARGET_MMX_WITH_SSE" +{ + bool ok = ix86_expand_fp_vcond (operands); + gcc_assert (ok); + DONE; +}) + +(define_insn "mmx_blendvps" + [(set (match_operand:V2SF 0 "register_operand" "=Yr,*x,x") + (unspec:V2SF + [(match_operand:V2SF 1 "register_operand" "0,0,x") + (match_operand:V2SF 2 "register_operand" "Yr,*x,x") + (match_operand:V2SF 3 "register_operand" "Yz,Yz,x")] + UNSPEC_BLENDV))] + "TARGET_SSE4_1 && TARGET_MMX_WITH_SSE" + "@ + blendvps\t{%3, %2, %0|%0, %2, %3} + blendvps\t{%3, %2, %0|%0, %2, %3} + vblendvps\t{%3, %2, %1, %0|%0, %1, %2, %3}" + [(set_attr "isa" "noavx,noavx,avx") + (set_attr "type" "ssemov") + (set_attr "length_immediate" "1") + (set_attr "prefix_data16" "1,1,*") + (set_attr "prefix_extra" "1") + (set_attr "prefix" "orig,orig,vex") + (set_attr "btver2_decode" "vector") + (set_attr "mode" "V4SF")]) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; Parallel single-precision floating point logical operations @@ -1657,42 +1747,46 @@ DONE; }) -(define_expand "vcond<mode><mode>" - [(set (match_operand:MMXMODEI 0 "register_operand") - (if_then_else:MMXMODEI +(define_expand "vcond<MMXMODE124:mode><MMXMODEI:mode>" + [(set (match_operand:MMXMODE124 0 "register_operand") + (if_then_else:MMXMODE124 (match_operator 3 "" [(match_operand:MMXMODEI 4 "register_operand") (match_operand:MMXMODEI 5 "register_operand")]) - (match_operand:MMXMODEI 1) - (match_operand:MMXMODEI 2)))] - "TARGET_MMX_WITH_SSE" + (match_operand:MMXMODE124 1) + (match_operand:MMXMODE124 2)))] + "TARGET_MMX_WITH_SSE + && (GET_MODE_NUNITS (<MMXMODE124:MODE>mode) + == GET_MODE_NUNITS (<MMXMODEI:MODE>mode))" { bool ok = ix86_expand_int_vcond (operands); gcc_assert (ok); DONE; }) -(define_expand "vcondu<mode><mode>" - [(set (match_operand:MMXMODEI 0 "register_operand") - (if_then_else:MMXMODEI +(define_expand "vcondu<MMXMODE124:mode><MMXMODEI:mode>" + [(set (match_operand:MMXMODE124 0 "register_operand") + (if_then_else:MMXMODE124 (match_operator 3 "" [(match_operand:MMXMODEI 4 "register_operand") (match_operand:MMXMODEI 5 "register_operand")]) - (match_operand:MMXMODEI 1) - (match_operand:MMXMODEI 2)))] - "TARGET_MMX_WITH_SSE" + (match_operand:MMXMODE124 1) + (match_operand:MMXMODE124 2)))] + "TARGET_MMX_WITH_SSE + && (GET_MODE_NUNITS (<MMXMODE124:MODE>mode) + == GET_MODE_NUNITS (<MMXMODEI:MODE>mode))" { bool ok = ix86_expand_int_vcond (operands); gcc_assert (ok); DONE; }) -(define_expand "vcond_mask_<mode><mode>" - [(set (match_operand:MMXMODEI 0 "register_operand") - (vec_merge:MMXMODEI - (match_operand:MMXMODEI 1 "register_operand") - (match_operand:MMXMODEI 2 "register_operand") - (match_operand:MMXMODEI 3 "register_operand")))] +(define_expand "vcond_mask_<mode><mmxintvecmodelower>" + [(set (match_operand:MMXMODE124 0 "register_operand") + (vec_merge:MMXMODE124 + (match_operand:MMXMODE124 1 "register_operand") + (match_operand:MMXMODE124 2 "register_operand") + (match_operand:<mmxintvecmode> 3 "register_operand")))] "TARGET_MMX_WITH_SSE" { ix86_expand_sse_movcc (operands[0], operands[3], @@ -1722,11 +1816,11 @@ ;; XOP parallel XMM conditional moves (define_insn "*xop_pcmov_<mode>" - [(set (match_operand:MMXMODEI 0 "register_operand" "=x") - (if_then_else:MMXMODEI - (match_operand:MMXMODEI 3 "register_operand" "x") - (match_operand:MMXMODEI 1 "register_operand" "x") - (match_operand:MMXMODEI 2 "register_operand" "x")))] + [(set (match_operand:MMXMODE124 0 "register_operand" "=x") + (if_then_else:MMXMODE124 + (match_operand:MMXMODE124 3 "register_operand" "x") + (match_operand:MMXMODE124 1 "register_operand" "x") + (match_operand:MMXMODE124 2 "register_operand" "x")))] "TARGET_XOP && TARGET_MMX_WITH_SSE" "vpcmov\t{%3, %2, %1, %0|%0, %1, %2, %3}" [(set_attr "type" "sse4arg")]) diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md index 6dfbb08..abd307e 100644 --- a/gcc/config/i386/predicates.md +++ b/gcc/config/i386/predicates.md @@ -1535,6 +1535,38 @@ (and (match_code "mem") (match_test "MEM_ALIGN (op) < GET_MODE_BITSIZE (mode)"))) +;; Return true if OP is a parallel for an mov{d,q,dqa,ps,pd} vec_select, +;; where one of the two operands of the vec_concat is const0_operand. +(define_predicate "movq_parallel" + (match_code "parallel") +{ + unsigned nelt = XVECLEN (op, 0); + unsigned nelt2 = nelt >> 1; + unsigned i; + + if (nelt < 2) + return false; + + /* Validate that all of the elements are constants, + lower halves of permute are lower halves of the first operand, + upper halves of permute come from any of the second operand. */ + for (i = 0; i < nelt; ++i) + { + rtx er = XVECEXP (op, 0, i); + unsigned HOST_WIDE_INT ei; + + if (!CONST_INT_P (er)) + return 0; + ei = INTVAL (er); + if (i < nelt2 && ei != i) + return 0; + if (i >= nelt2 && (ei < nelt || ei >= nelt << 1)) + return 0; + } + + return 1; +}) + ;; Return true if OP is a vzeroall operation, known to be a PARALLEL. (define_predicate "vzeroall_operation" (match_code "parallel") diff --git a/gcc/config/i386/sse.md b/gcc/config/i386/sse.md index 244fb13..62f4e15f 100644 --- a/gcc/config/i386/sse.md +++ b/gcc/config/i386/sse.md @@ -811,19 +811,22 @@ ;; Mapping of vector modes to a vector mode of double size (define_mode_attr ssedoublevecmode - [(V32QI "V64QI") (V16HI "V32HI") (V8SI "V16SI") (V4DI "V8DI") + [(V64QI "V128QI") (V32HI "V64HI") (V16SI "V32SI") (V8DI "V16DI") + (V32QI "V64QI") (V16HI "V32HI") (V8SI "V16SI") (V4DI "V8DI") (V16QI "V32QI") (V8HI "V16HI") (V4SI "V8SI") (V2DI "V4DI") + (V16SF "V32SF") (V8DF "V16DF") (V8SF "V16SF") (V4DF "V8DF") (V4SF "V8SF") (V2DF "V4DF")]) ;; Mapping of vector modes to a vector mode of half size +;; instead of V1DI/V1DF, DI/DF are used for V2DI/V2DF although they are scalar. (define_mode_attr ssehalfvecmode [(V64QI "V32QI") (V32HI "V16HI") (V16SI "V8SI") (V8DI "V4DI") (V4TI "V2TI") (V32QI "V16QI") (V16HI "V8HI") (V8SI "V4SI") (V4DI "V2DI") - (V16QI "V8QI") (V8HI "V4HI") (V4SI "V2SI") + (V16QI "V8QI") (V8HI "V4HI") (V4SI "V2SI") (V2DI "DI") (V16SF "V8SF") (V8DF "V4DF") (V8SF "V4SF") (V4DF "V2DF") - (V4SF "V2SF")]) + (V4SF "V2SF") (V2DF "DF")]) (define_mode_attr ssehalfvecmodelower [(V64QI "v32qi") (V32HI "v16hi") (V16SI "v8si") (V8DI "v4di") (V4TI "v2ti") @@ -12465,7 +12468,7 @@ (set_attr "prefix" "orig,vex") (set_attr "mode" "<sseinsnmode>")]) -(define_insn "ashr<mode>3<mask_name>" +(define_insn "<mask_codefor>ashr<mode>3<mask_name>" [(set (match_operand:VI248_AVX512BW_AVX512VL 0 "register_operand" "=v,v") (ashiftrt:VI248_AVX512BW_AVX512VL (match_operand:VI248_AVX512BW_AVX512VL 1 "nonimmediate_operand" "v,vm") @@ -12479,6 +12482,126 @@ (const_string "0"))) (set_attr "mode" "<sseinsnmode>")]) +(define_expand "ashr<mode>3" + [(set (match_operand:VI248_AVX512BW 0 "register_operand") + (ashiftrt:VI248_AVX512BW + (match_operand:VI248_AVX512BW 1 "nonimmediate_operand") + (match_operand:DI 2 "nonmemory_operand")))] + "TARGET_AVX512F") + +(define_expand "ashrv4di3" + [(set (match_operand:V4DI 0 "register_operand") + (ashiftrt:V4DI + (match_operand:V4DI 1 "nonimmediate_operand") + (match_operand:DI 2 "nonmemory_operand")))] + "TARGET_AVX2" +{ + if (!TARGET_AVX512VL) + { + if (CONST_INT_P (operands[2]) && UINTVAL (operands[2]) >= 63) + { + rtx zero = force_reg (V4DImode, CONST0_RTX (V4DImode)); + emit_insn (gen_avx2_gtv4di3 (operands[0], zero, operands[1])); + DONE; + } + if (operands[2] == const0_rtx) + { + emit_move_insn (operands[0], operands[1]); + DONE; + } + operands[1] = force_reg (V4DImode, operands[1]); + if (CONST_INT_P (operands[2])) + { + vec_perm_builder sel (8, 8, 1); + sel.quick_grow (8); + rtx arg0, arg1; + rtx op1 = lowpart_subreg (V8SImode, operands[1], V4DImode); + rtx target = gen_reg_rtx (V8SImode); + if (INTVAL (operands[2]) > 32) + { + arg0 = gen_reg_rtx (V8SImode); + arg1 = gen_reg_rtx (V8SImode); + emit_insn (gen_ashrv8si3 (arg1, op1, GEN_INT (31))); + emit_insn (gen_ashrv8si3 (arg0, op1, + GEN_INT (INTVAL (operands[2]) - 32))); + sel[0] = 1; + sel[1] = 9; + sel[2] = 3; + sel[3] = 11; + sel[4] = 5; + sel[5] = 13; + sel[6] = 7; + sel[7] = 15; + } + else if (INTVAL (operands[2]) == 32) + { + arg0 = op1; + arg1 = gen_reg_rtx (V8SImode); + emit_insn (gen_ashrv8si3 (arg1, op1, GEN_INT (31))); + sel[0] = 1; + sel[1] = 9; + sel[2] = 3; + sel[3] = 11; + sel[4] = 5; + sel[5] = 13; + sel[6] = 7; + sel[7] = 15; + } + else + { + arg0 = gen_reg_rtx (V4DImode); + arg1 = gen_reg_rtx (V8SImode); + emit_insn (gen_lshrv4di3 (arg0, operands[1], operands[2])); + emit_insn (gen_ashrv8si3 (arg1, op1, operands[2])); + arg0 = lowpart_subreg (V8SImode, arg0, V4DImode); + sel[0] = 0; + sel[1] = 9; + sel[2] = 2; + sel[3] = 11; + sel[4] = 4; + sel[5] = 13; + sel[6] = 6; + sel[7] = 15; + } + vec_perm_indices indices (sel, 2, 8); + bool ok = targetm.vectorize.vec_perm_const (V8SImode, target, + arg0, arg1, indices); + gcc_assert (ok); + emit_move_insn (operands[0], + lowpart_subreg (V4DImode, target, V8SImode)); + DONE; + } + + rtx zero = force_reg (V4DImode, CONST0_RTX (V4DImode)); + rtx zero_or_all_ones = gen_reg_rtx (V4DImode); + emit_insn (gen_avx2_gtv4di3 (zero_or_all_ones, zero, operands[1])); + rtx lshr_res = gen_reg_rtx (V4DImode); + emit_insn (gen_lshrv4di3 (lshr_res, operands[1], operands[2])); + rtx ashl_res = gen_reg_rtx (V4DImode); + rtx amount; + if (TARGET_64BIT) + { + amount = gen_reg_rtx (DImode); + emit_insn (gen_subdi3 (amount, force_reg (DImode, GEN_INT (64)), + operands[2])); + } + else + { + rtx temp = gen_reg_rtx (SImode); + emit_insn (gen_subsi3 (temp, force_reg (SImode, GEN_INT (64)), + lowpart_subreg (SImode, operands[2], + DImode))); + amount = gen_reg_rtx (V4SImode); + emit_insn (gen_vec_setv4si_0 (amount, CONST0_RTX (V4SImode), + temp)); + } + amount = lowpart_subreg (DImode, amount, GET_MODE (amount)); + emit_insn (gen_ashlv4di3 (ashl_res, zero_or_all_ones, amount)); + emit_insn (gen_iorv4di3 (operands[0], lshr_res, ashl_res)); + DONE; + } +}) + (define_insn "<mask_codefor><insn><mode>3<mask_name>" [(set (match_operand:VI248_AVX512BW_2 0 "register_operand" "=v,v") (any_lshift:VI248_AVX512BW_2 @@ -15939,11 +16062,11 @@ (set_attr "prefix" "orig,maybe_evex,orig,orig,maybe_evex") (set_attr "mode" "TI,TI,V4SF,V2SF,V2SF")]) -(define_insn "*vec_concatv4si_0" - [(set (match_operand:V4SI 0 "register_operand" "=v,x") - (vec_concat:V4SI - (match_operand:V2SI 1 "nonimmediate_operand" "vm,?!*y") - (match_operand:V2SI 2 "const0_operand" " C,C")))] +(define_insn "*vec_concat<mode>_0" + [(set (match_operand:VI124_128 0 "register_operand" "=v,x") + (vec_concat:VI124_128 + (match_operand:<ssehalfvecmode> 1 "nonimmediate_operand" "vm,?!*y") + (match_operand:<ssehalfvecmode> 2 "const0_operand" " C,C")))] "TARGET_SSE2" "@ %vmovq\t{%1, %0|%0, %1} @@ -17734,6 +17857,35 @@ (set_attr "btver2_decode" "vector,vector,vector") (set_attr "mode" "<sseinsnmode>")]) +(define_split + [(set (match_operand:VI1_AVX2 0 "register_operand") + (unspec:VI1_AVX2 + [(match_operand:VI1_AVX2 1 "vector_operand") + (match_operand:VI1_AVX2 2 "register_operand") + (not:VI1_AVX2 (match_operand:VI1_AVX2 3 "register_operand"))] + UNSPEC_BLENDV))] + "TARGET_SSE4_1" + [(set (match_dup 0) + (unspec:VI1_AVX2 + [(match_dup 2) (match_dup 1) (match_dup 3)] + UNSPEC_BLENDV))]) + +(define_split + [(set (match_operand:VI1_AVX2 0 "register_operand") + (unspec:VI1_AVX2 + [(match_operand:VI1_AVX2 1 "vector_operand") + (match_operand:VI1_AVX2 2 "register_operand") + (subreg:VI1_AVX2 (not (match_operand 3 "register_operand")) 0)] + UNSPEC_BLENDV))] + "TARGET_SSE4_1 + && GET_MODE_CLASS (GET_MODE (operands[3])) == MODE_VECTOR_INT + && GET_MODE_SIZE (GET_MODE (operands[3])) == <MODE_SIZE>" + [(set (match_dup 0) + (unspec:VI1_AVX2 + [(match_dup 2) (match_dup 1) (match_dup 4)] + UNSPEC_BLENDV))] + "operands[4] = gen_lowpart (<MODE>mode, operands[3]);") + (define_insn_and_split "*<sse4_1_avx2>_pblendvb_lt" [(set (match_operand:VI1_AVX2 0 "register_operand" "=Yr,*x,x") (unspec:VI1_AVX2 @@ -20297,10 +20449,132 @@ (ashiftrt:V2DI (match_operand:V2DI 1 "register_operand") (match_operand:DI 2 "nonmemory_operand")))] - "TARGET_XOP || TARGET_AVX512VL" + "TARGET_SSE2" { if (!TARGET_AVX512VL) { + if (TARGET_SSE4_2 + && CONST_INT_P (operands[2]) + && UINTVAL (operands[2]) >= 63) + { + rtx zero = force_reg (V2DImode, CONST0_RTX (V2DImode)); + emit_insn (gen_sse4_2_gtv2di3 (operands[0], zero, operands[1])); + DONE; + } + if (operands[2] == const0_rtx) + { + emit_move_insn (operands[0], operands[1]); + DONE; + } + if (CONST_INT_P (operands[2]) + && (!TARGET_XOP || UINTVAL (operands[2]) >= 63)) + { + vec_perm_builder sel (4, 4, 1); + sel.quick_grow (4); + rtx arg0, arg1; + rtx op1 = lowpart_subreg (V4SImode, operands[1], V2DImode); + rtx target = gen_reg_rtx (V4SImode); + if (UINTVAL (operands[2]) >= 63) + { + arg0 = arg1 = gen_reg_rtx (V4SImode); + emit_insn (gen_ashrv4si3 (arg0, op1, GEN_INT (31))); + sel[0] = 1; + sel[1] = 1; + sel[2] = 3; + sel[3] = 3; + } + else if (INTVAL (operands[2]) > 32) + { + arg0 = gen_reg_rtx (V4SImode); + arg1 = gen_reg_rtx (V4SImode); + emit_insn (gen_ashrv4si3 (arg1, op1, GEN_INT (31))); + emit_insn (gen_ashrv4si3 (arg0, op1, + GEN_INT (INTVAL (operands[2]) - 32))); + sel[0] = 1; + sel[1] = 5; + sel[2] = 3; + sel[3] = 7; + } + else if (INTVAL (operands[2]) == 32) + { + arg0 = op1; + arg1 = gen_reg_rtx (V4SImode); + emit_insn (gen_ashrv4si3 (arg1, op1, GEN_INT (31))); + sel[0] = 1; + sel[1] = 5; + sel[2] = 3; + sel[3] = 7; + } + else + { + arg0 = gen_reg_rtx (V2DImode); + arg1 = gen_reg_rtx (V4SImode); + emit_insn (gen_lshrv2di3 (arg0, operands[1], operands[2])); + emit_insn (gen_ashrv4si3 (arg1, op1, operands[2])); + arg0 = lowpart_subreg (V4SImode, arg0, V2DImode); + sel[0] = 0; + sel[1] = 5; + sel[2] = 2; + sel[3] = 7; + } + vec_perm_indices indices (sel, arg0 != arg1 ? 2 : 1, 4); + bool ok = targetm.vectorize.vec_perm_const (V4SImode, target, + arg0, arg1, indices); + gcc_assert (ok); + emit_move_insn (operands[0], + lowpart_subreg (V2DImode, target, V4SImode)); + DONE; + } + if (!TARGET_XOP) + { + rtx zero = force_reg (V2DImode, CONST0_RTX (V2DImode)); + rtx zero_or_all_ones; + if (TARGET_SSE4_2) + { + zero_or_all_ones = gen_reg_rtx (V2DImode); + emit_insn (gen_sse4_2_gtv2di3 (zero_or_all_ones, zero, + operands[1])); + } + else + { + rtx temp = gen_reg_rtx (V4SImode); + emit_insn (gen_ashrv4si3 (temp, lowpart_subreg (V4SImode, + operands[1], + V2DImode), + GEN_INT (31))); + zero_or_all_ones = gen_reg_rtx (V4SImode); + emit_insn (gen_sse2_pshufd_1 (zero_or_all_ones, temp, + const1_rtx, const1_rtx, + GEN_INT (3), GEN_INT (3))); + zero_or_all_ones = lowpart_subreg (V2DImode, zero_or_all_ones, + V4SImode); + } + rtx lshr_res = gen_reg_rtx (V2DImode); + emit_insn (gen_lshrv2di3 (lshr_res, operands[1], operands[2])); + rtx ashl_res = gen_reg_rtx (V2DImode); + rtx amount; + if (TARGET_64BIT) + { + amount = gen_reg_rtx (DImode); + emit_insn (gen_subdi3 (amount, force_reg (DImode, GEN_INT (64)), + operands[2])); + } + else + { + rtx temp = gen_reg_rtx (SImode); + emit_insn (gen_subsi3 (temp, force_reg (SImode, GEN_INT (64)), + lowpart_subreg (SImode, operands[2], + DImode))); + amount = gen_reg_rtx (V4SImode); + emit_insn (gen_vec_setv4si_0 (amount, CONST0_RTX (V4SImode), + temp)); + } + amount = lowpart_subreg (DImode, amount, GET_MODE (amount)); + emit_insn (gen_ashlv2di3 (ashl_res, zero_or_all_ones, amount)); + emit_insn (gen_iorv2di3 (operands[0], lshr_res, ashl_res)); + DONE; + } + rtx reg = gen_reg_rtx (V2DImode); rtx par; bool negate = false; @@ -22129,6 +22403,24 @@ (set_attr "prefix" "maybe_evex") (set_attr "mode" "<sseinsnmode>")]) +(define_insn_and_split "*vec_concat<mode>_0_1" + [(set (match_operand:V 0 "register_operand") + (vec_select:V + (vec_concat:<ssedoublevecmode> + (match_operand:V 1 "nonimmediate_operand") + (match_operand:V 2 "const0_operand")) + (match_parallel 3 "movq_parallel" + [(match_operand 4 "const_int_operand")])))] + "ix86_pre_reload_split ()" + "#" + "&& 1" + [(set (match_dup 0) + (vec_concat:V (match_dup 1) (match_dup 5)))] +{ + operands[1] = gen_lowpart (<ssehalfvecmode>mode, operands[1]); + operands[5] = CONST0_RTX (<ssehalfvecmode>mode); +}) + (define_insn "vcvtph2ps<mask_name>" [(set (match_operand:V4SF 0 "register_operand" "=v") (vec_select:V4SF diff --git a/gcc/config/nvptx/nvptx-opts.h b/gcc/config/nvptx/nvptx-opts.h index ce88245..bfa926e 100644 --- a/gcc/config/nvptx/nvptx-opts.h +++ b/gcc/config/nvptx/nvptx-opts.h @@ -26,5 +26,11 @@ enum ptx_isa PTX_ISA_SM35 }; +enum ptx_version +{ + PTX_VERSION_3_1, + PTX_VERSION_6_3 +}; + #endif diff --git a/gcc/config/nvptx/nvptx.c b/gcc/config/nvptx/nvptx.c index 7a7a913..ebbfa92 100644 --- a/gcc/config/nvptx/nvptx.c +++ b/gcc/config/nvptx/nvptx.c @@ -5309,7 +5309,10 @@ static void nvptx_file_start (void) { fputs ("// BEGIN PREAMBLE\n", asm_out_file); - fputs ("\t.version\t3.1\n", asm_out_file); + if (TARGET_PTX_6_3) + fputs ("\t.version\t6.3\n", asm_out_file); + else + fputs ("\t.version\t3.1\n", asm_out_file); if (TARGET_SM35) fputs ("\t.target\tsm_35\n", asm_out_file); else diff --git a/gcc/config/nvptx/nvptx.h b/gcc/config/nvptx/nvptx.h index 2451703..fdaacdd 100644 --- a/gcc/config/nvptx/nvptx.h +++ b/gcc/config/nvptx/nvptx.h @@ -98,6 +98,8 @@ #define TARGET_SM35 (ptx_isa_option >= PTX_ISA_SM35) +#define TARGET_PTX_6_3 (ptx_version_option >= PTX_VERSION_6_3) + /* Registers. Since ptx is a virtual target, we just define a few hard registers for special purposes and leave pseudos unallocated. We have to have some available hard registers, to keep gcc setup diff --git a/gcc/config/nvptx/nvptx.md b/gcc/config/nvptx/nvptx.md index 0f15609..00bb8fe 100644 --- a/gcc/config/nvptx/nvptx.md +++ b/gcc/config/nvptx/nvptx.md @@ -1452,14 +1452,24 @@ (match_operand:SI 3 "const_int_operand" "n")] UNSPEC_SHUFFLE))] "" - "%.\\tshfl%S3.b32\\t%0, %1, %2, 31;") + { + if (TARGET_PTX_6_3) + return "%.\\tshfl.sync%S3.b32\\t%0, %1, %2, 31, 0xffffffff;"; + else + return "%.\\tshfl%S3.b32\\t%0, %1, %2, 31;"; + }) (define_insn "nvptx_vote_ballot" [(set (match_operand:SI 0 "nvptx_register_operand" "=R") (unspec:SI [(match_operand:BI 1 "nvptx_register_operand" "R")] UNSPEC_VOTE_BALLOT))] "" - "%.\\tvote.ballot.b32\\t%0, %1;") + { + if (TARGET_PTX_6_3) + return "%.\\tvote.sync.ballot.b32\\t%0, %1, 0xffffffff;"; + else + return "%.\\tvote.ballot.b32\\t%0, %1;"; + }) ;; Patterns for OpenMP SIMD-via-SIMT lowering diff --git a/gcc/config/nvptx/nvptx.opt b/gcc/config/nvptx/nvptx.opt index 51363e4..468c6ca 100644 --- a/gcc/config/nvptx/nvptx.opt +++ b/gcc/config/nvptx/nvptx.opt @@ -65,3 +65,17 @@ Enum(ptx_isa) String(sm_35) Value(PTX_ISA_SM35) misa= Target RejectNegative ToLower Joined Enum(ptx_isa) Var(ptx_isa_option) Init(PTX_ISA_SM35) Specify the version of the ptx ISA to use. + +Enum +Name(ptx_version) Type(int) +Known PTX versions (for use with the -mptx= option): + +EnumValue +Enum(ptx_version) String(3.1) Value(PTX_VERSION_3_1) + +EnumValue +Enum(ptx_version) String(6.3) Value(PTX_VERSION_6_3) + +mptx= +Target RejectNegative ToLower Joined Enum(ptx_version) Var(ptx_version_option) Init(PTX_VERSION_3_1) +Specify the version of the ptx version to use. diff --git a/gcc/config/rs6000/rs6000-c.c b/gcc/config/rs6000/rs6000-c.c index 0f8a629..afcb5bb 100644 --- a/gcc/config/rs6000/rs6000-c.c +++ b/gcc/config/rs6000/rs6000-c.c @@ -602,6 +602,9 @@ rs6000_target_modify_macros (bool define_p, HOST_WIDE_INT flags, /* Whether pc-relative code is being generated. */ if ((flags & OPTION_MASK_PCREL) != 0) rs6000_define_or_undefine_macro (define_p, "__PCREL__"); + /* Tell the user -mrop-protect is in play. */ + if (rs6000_rop_protect) + rs6000_define_or_undefine_macro (define_p, "__ROP_PROTECT__"); } void diff --git a/gcc/config/rs6000/rs6000-internal.h b/gcc/config/rs6000/rs6000-internal.h index 428a786..88cf9bd 100644 --- a/gcc/config/rs6000/rs6000-internal.h +++ b/gcc/config/rs6000/rs6000-internal.h @@ -39,6 +39,7 @@ typedef struct rs6000_stack { int gp_save_offset; /* offset to save GP regs from initial SP */ int fp_save_offset; /* offset to save FP regs from initial SP */ int altivec_save_offset; /* offset to save AltiVec regs from initial SP */ + int rop_hash_save_offset; /* offset to save ROP hash from initial SP */ int lr_save_offset; /* offset to save LR from initial SP */ int cr_save_offset; /* offset to save CR from initial SP */ int vrsave_save_offset; /* offset to save VRSAVE from initial SP */ @@ -53,6 +54,7 @@ typedef struct rs6000_stack { int gp_size; /* size of saved GP registers */ int fp_size; /* size of saved FP registers */ int altivec_size; /* size of saved AltiVec registers */ + int rop_hash_size; /* size of ROP hash slot */ int cr_size; /* size to hold CR if not in fixed area */ int vrsave_size; /* size to hold VRSAVE */ int altivec_padding_size; /* size of altivec alignment padding */ diff --git a/gcc/config/rs6000/rs6000-logue.c b/gcc/config/rs6000/rs6000-logue.c index b0ac183..13c00e7 100644 --- a/gcc/config/rs6000/rs6000-logue.c +++ b/gcc/config/rs6000/rs6000-logue.c @@ -595,19 +595,21 @@ rs6000_savres_strategy (rs6000_stack_t *info, +---------------------------------------+ | Parameter save area (+padding*) (P) | 32 +---------------------------------------+ - | Alloca space (A) | 32+P + | Optional ROP hash slot (R) | 32+P +---------------------------------------+ - | Local variable space (L) | 32+P+A + | Alloca space (A) | 32+P+R +---------------------------------------+ - | Save area for AltiVec registers (W) | 32+P+A+L + | Local variable space (L) | 32+P+R+A +---------------------------------------+ - | AltiVec alignment padding (Y) | 32+P+A+L+W + | Save area for AltiVec registers (W) | 32+P+R+A+L +---------------------------------------+ - | Save area for GP registers (G) | 32+P+A+L+W+Y + | AltiVec alignment padding (Y) | 32+P+R+A+L+W +---------------------------------------+ - | Save area for FP registers (F) | 32+P+A+L+W+Y+G + | Save area for GP registers (G) | 32+P+R+A+L+W+Y +---------------------------------------+ - old SP->| back chain to caller's caller | 32+P+A+L+W+Y+G+F + | Save area for FP registers (F) | 32+P+R+A+L+W+Y+G + +---------------------------------------+ + old SP->| back chain to caller's caller | 32+P+R+A+L+W+Y+G+F +---------------------------------------+ * If the alloca area is present, the parameter save area is @@ -716,6 +718,19 @@ rs6000_stack_info (void) /* Does this function call anything (apart from sibling calls)? */ info->calls_p = (!crtl->is_leaf || cfun->machine->ra_needs_full_frame); + info->rop_hash_size = 0; + + if (TARGET_POWER10 + && info->calls_p + && DEFAULT_ABI == ABI_ELFv2 + && rs6000_rop_protect) + info->rop_hash_size = 8; + else if (rs6000_rop_protect && DEFAULT_ABI != ABI_ELFv2) + { + /* We can't check this in rs6000_option_override_internal since + DEFAULT_ABI isn't established yet. */ + error ("%qs requires the ELFv2 ABI", "-mrop-protect"); + } /* Determine if we need to save the condition code registers. */ if (save_reg_p (CR2_REGNO) @@ -808,6 +823,11 @@ rs6000_stack_info (void) /* Adjust for AltiVec case. */ info->ehrd_offset = info->altivec_save_offset - ehrd_size; + + /* Adjust for ROP protection. */ + info->rop_hash_save_offset + = info->altivec_save_offset - info->rop_hash_size; + info->ehrd_offset -= info->rop_hash_size; } else info->ehrd_offset = info->gp_save_offset - ehrd_size; @@ -849,6 +869,7 @@ rs6000_stack_info (void) + info->gp_size + info->altivec_size + info->altivec_padding_size + + info->rop_hash_size + ehrd_size + ehcr_size + info->cr_size @@ -987,6 +1008,10 @@ debug_stack_info (rs6000_stack_t *info) fprintf (stderr, "\tvrsave_save_offset = %5d\n", info->vrsave_save_offset); + if (info->rop_hash_size) + fprintf (stderr, "\trop_hash_save_offset = %5d\n", + info->rop_hash_save_offset); + if (info->lr_save_p) fprintf (stderr, "\tlr_save_offset = %5d\n", info->lr_save_offset); @@ -1026,6 +1051,9 @@ debug_stack_info (rs6000_stack_t *info) fprintf (stderr, "\taltivec_padding_size= %5d\n", info->altivec_padding_size); + if (info->rop_hash_size) + fprintf (stderr, "\trop_hash_size = %5d\n", info->rop_hash_size); + if (info->cr_size) fprintf (stderr, "\tcr_size = %5d\n", info->cr_size); @@ -3252,6 +3280,22 @@ rs6000_emit_prologue (void) } } + /* The ROP hash store must occur before a stack frame is created, + since the hash operates on r1. */ + /* NOTE: The hashst isn't needed if we're going to do a sibcall, + but there's no way to know that here. Harmless except for + performance, of course. */ + if (TARGET_POWER10 && rs6000_rop_protect && info->rop_hash_size != 0) + { + gcc_assert (DEFAULT_ABI == ABI_ELFv2); + rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); + rtx addr = gen_rtx_PLUS (Pmode, stack_ptr, + GEN_INT (info->rop_hash_save_offset)); + rtx mem = gen_rtx_MEM (Pmode, addr); + rtx reg0 = gen_rtx_REG (Pmode, 0); + emit_insn (gen_hashst (mem, reg0)); + } + /* If we need to save CR, put it into r12 or r11. Choose r12 except when r12 will be needed by out-of-line gpr save. */ cr_save_regno = ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_ELFv2) @@ -4980,6 +5024,22 @@ rs6000_emit_epilogue (enum epilogue_type epilogue_type) emit_insn (gen_add3_insn (sp_reg_rtx, sp_reg_rtx, sa)); } + /* The ROP hash check must occur after the stack pointer is restored + (since the hash involves r1), and is not performed for a sibcall. */ + if (TARGET_POWER10 + && rs6000_rop_protect + && info->rop_hash_size != 0 + && epilogue_type != EPILOGUE_TYPE_SIBCALL) + { + gcc_assert (DEFAULT_ABI == ABI_ELFv2); + rtx stack_ptr = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM); + rtx addr = gen_rtx_PLUS (Pmode, stack_ptr, + GEN_INT (info->rop_hash_save_offset)); + rtx mem = gen_rtx_MEM (Pmode, addr); + rtx reg0 = gen_rtx_REG (Pmode, 0); + emit_insn (gen_hashchk (reg0, mem)); + } + if (epilogue_type != EPILOGUE_TYPE_SIBCALL && restoring_FPRs_inline) { if (cfa_restores) diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index d1b76f6..53a9f54 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -4040,6 +4040,10 @@ rs6000_option_override_internal (bool global_init_p) && ((rs6000_isa_flags_explicit & OPTION_MASK_QUAD_MEMORY_ATOMIC) == 0)) rs6000_isa_flags |= OPTION_MASK_QUAD_MEMORY_ATOMIC; + /* If we are inserting ROP-protect instructions, disable shrink wrap. */ + if (rs6000_rop_protect) + flag_shrink_wrap = 0; + /* If we can shrink-wrap the TOC register save separately, then use -msave-toc-indirect unless explicitly disabled. */ if ((rs6000_isa_flags_explicit & OPTION_MASK_SAVE_TOC_INDIRECT) == 0 diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md index c8cdc42..0bfeb24 100644 --- a/gcc/config/rs6000/rs6000.md +++ b/gcc/config/rs6000/rs6000.md @@ -154,6 +154,8 @@ UNSPEC_CNTTZDM UNSPEC_PDEPD UNSPEC_PEXTD + UNSPEC_HASHST + UNSPEC_HASHCHK ]) ;; @@ -14948,6 +14950,35 @@ "TARGET_P9_MISC && TARGET_64BIT" "cmpeqb %0,%1,%2" [(set_attr "type" "logical")]) + + +;; ROP mitigation instructions. + +(define_insn "hashst" + [(set (match_operand:DI 0 "simple_offsettable_mem_operand" "=m") + (unspec_volatile:DI [(match_operand:DI 1 "int_reg_operand" "r")] + UNSPEC_HASHST))] + "TARGET_POWER10 && rs6000_rop_protect" +{ + static char templ[32]; + const char *p = rs6000_privileged ? "p" : ""; + sprintf (templ, "hashst%s %%1,%%0", p); + return templ; +} + [(set_attr "type" "store")]) + +(define_insn "hashchk" + [(unspec_volatile [(match_operand:DI 0 "int_reg_operand" "r") + (match_operand:DI 1 "simple_offsettable_mem_operand" "m")] + UNSPEC_HASHCHK)] + "TARGET_POWER10 && rs6000_rop_protect" +{ + static char templ[32]; + const char *p = rs6000_privileged ? "p" : ""; + sprintf (templ, "hashchk%s %%0,%%1", p); + return templ; +} + [(set_attr "type" "load")]) (include "sync.md") diff --git a/gcc/config/rs6000/rs6000.opt b/gcc/config/rs6000/rs6000.opt index 0dbdf75..2685fa7 100644 --- a/gcc/config/rs6000/rs6000.opt +++ b/gcc/config/rs6000/rs6000.opt @@ -619,3 +619,11 @@ Generate (do not generate) MMA instructions. mrelative-jumptables Target Undocumented Var(rs6000_relative_jumptables) Init(1) Save + +mrop-protect +Target Var(rs6000_rop_protect) Init(0) +Enable instructions that guard against return-oriented programming attacks. + +mprivileged +Target Var(rs6000_privileged) Init(0) +Generate code that will run in privileged state. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 122808e..48425b9 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,61 @@ +2021-05-14 Jason Merrill <jason@redhat.com> + + PR c++/95870 + * pt.c (enclosing_instantiation_of): Just compare + DECL_SOURCE_LOCATION. + (regenerate_decl_from_template): Copy DECL_SOURCE_LOCATION. + +2021-05-14 Marek Polacek <polacek@redhat.com> + Jason Merrill <jason@redhat.com> + + PR c++/99032 + * cp-tree.h (any_non_type_attribute_p): Declare. + * decl.c (grokdeclarator): Diagnose when an attribute appertains to + a friend declaration that is not a definition. + * decl2.c (any_non_type_attribute_p): New. + * parser.c (cp_parser_decl_specifier_seq): Diagnose standard attributes + in the middle of decl-specifiers. + (cp_parser_elaborated_type_specifier): Diagnose when an attribute + appertains to a friend declaration that is not a definition. + (cp_parser_member_declaration): Likewise. + +2021-05-12 Marek Polacek <polacek@redhat.com> + + * pt.c (tsubst_copy_and_build): Add warn_int_in_bool_context + sentinel. + +2021-05-12 Marcel Vollweiler <marcel@codesourcery.com> + + * parser.c (cp_parser_omp_clause_map): Support map-type-modifier + 'close'. + +2021-05-11 Jason Merrill <jason@redhat.com> + + PR c++/100517 + * typeck.c (build_reinterpret_cast_1): Check intype on + cast to vector. + +2021-05-11 Patrick Palka <ppalka@redhat.com> + + PR c++/51577 + * name-lookup.c (maybe_save_operator_binding): Unconditionally + enable for all function templates, not just generic lambdas. + Handle compound-assignment operator expressions. + * typeck.c (build_x_compound_expr): Call maybe_save_operator_binding + in the type-dependent case. + (build_x_modify_expr): Likewise. Move declaration of 'op' closer + to its first use. + +2021-05-11 Patrick Palka <ppalka@redhat.com> + + PR c++/100138 + * constraint.cc (tsubst_constraint): Set up cp_unevaluated. + (satisfy_atom): Set up iloc_sentinel before calling + cxx_constant_value. + * pt.c (tsubst_pack_expansion): When returning a rebuilt pack + expansion, carry over PACK_EXPANSION_LOCAL_P and + PACK_EXPANSION_SIZEOF_P from the original pack expansion. + 2021-05-10 Richard Biener <rguenther@suse.de> PR middle-end/100464 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 122dadf..580db91 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6763,6 +6763,7 @@ extern tree grokbitfield (const cp_declarator *, cp_decl_specifier_seq *, tree, tree, tree); extern tree splice_template_attributes (tree *, tree); extern bool any_dependent_type_attributes_p (tree); +extern bool any_non_type_attribute_p (tree); extern tree cp_reconstruct_complex_type (tree, tree); extern bool attributes_naming_typedef_ok (tree); extern void cplus_decl_attributes (tree *, tree, int); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index bc3928d..17511f0 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -13741,6 +13741,11 @@ grokdeclarator (const cp_declarator *declarator, if (friendp) { + if (attrlist && !funcdef_flag + /* Hack to allow attributes like vector_size on a friend. */ + && any_non_type_attribute_p (*attrlist)) + error_at (id_loc, "attribute appertains to a friend " + "declaration that is not a definition"); /* Friends are treated specially. */ if (ctype == current_class_type) ; /* We already issued a permerror. */ diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 89f874a..8e4dd6b 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -1331,6 +1331,20 @@ any_dependent_type_attributes_p (tree attrs) return false; } +/* True if ATTRS contains any attribute that does not require a type. */ + +bool +any_non_type_attribute_p (tree attrs) +{ + for (tree a = attrs; a; a = TREE_CHAIN (a)) + { + const attribute_spec *as = lookup_attribute_spec (get_attribute_name (a)); + if (as && !as->type_required) + return true; + } + return false; +} + /* Return true iff ATTRS are acceptable attributes to be applied in-place to a typedef which gives a previously unnamed class or enum a name for linkage purposes. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 0fe29c6..c0b5795 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -15146,6 +15146,16 @@ cp_parser_decl_specifier_seq (cp_parser* parser, if (!found_decl_spec) break; + if (decl_specs->std_attributes) + { + error_at (decl_specs->locations[ds_std_attribute], + "standard attributes in middle of decl-specifiers"); + inform (decl_specs->locations[ds_std_attribute], + "standard attributes must precede the decl-specifiers to " + "apply to the declaration, or follow them to apply to " + "the type"); + } + decl_specs->any_specifiers_p = true; /* After we see one decl-specifier, further decl-specifiers are always optional. */ @@ -19764,11 +19774,15 @@ cp_parser_elaborated_type_specifier (cp_parser* parser, && ! processing_explicit_instantiation) warning (OPT_Wattributes, "attributes ignored on template instantiation"); + else if (is_friend && attributes) + error ("attribute appertains to a friend declaration that is not " + "a definition"); else if (is_declaration && cp_parser_declares_only_class_p (parser)) cplus_decl_attributes (&type, attributes, (int) ATTR_FLAG_TYPE_IN_PLACE); else warning (OPT_Wattributes, - "attributes ignored on elaborated-type-specifier that is not a forward declaration"); + "attributes ignored on elaborated-type-specifier that is " + "not a forward declaration"); } if (tag_type == enum_type) @@ -26054,6 +26068,13 @@ cp_parser_member_declaration (cp_parser* parser) error_at (decl_spec_token_start->location, "friend declaration does not name a class or " "function"); + /* Give an error if an attribute cannot appear here, as per + [dcl.attr.grammar]/5. But not when declares_class_or_enum: + we ignore attributes in elaborated-type-specifiers. */ + else if (!declares_class_or_enum && decl_specifiers.attributes) + error_at (decl_spec_token_start->location, + "attribute appertains to a friend declaration " + "that is not a definition"); else make_friend_class (current_class_type, type, /*complain=*/true); @@ -37859,40 +37880,90 @@ cp_parser_omp_clause_depend (cp_parser *parser, tree list, location_t loc) map-kind: alloc | to | from | tofrom | release | delete - map ( always [,] map-kind: variable-list ) */ + map ( always [,] map-kind: variable-list ) + + OpenMP 5.0: + map ( [map-type-modifier[,] ...] map-kind: variable-list ) + + map-type-modifier: + always | close */ static tree cp_parser_omp_clause_map (cp_parser *parser, tree list) { tree nlist, c; enum gomp_map_kind kind = GOMP_MAP_TOFROM; - bool always = false; if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) return list; - if (cp_lexer_next_token_is (parser->lexer, CPP_NAME)) + int pos = 1; + int map_kind_pos = 0; + while (cp_lexer_peek_nth_token (parser->lexer, pos)->type == CPP_NAME + || cp_lexer_peek_nth_token (parser->lexer, pos)->keyword == RID_DELETE) { - tree id = cp_lexer_peek_token (parser->lexer)->u.value; - const char *p = IDENTIFIER_POINTER (id); + if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COLON) + { + map_kind_pos = pos; + break; + } + + if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COMMA) + pos++; + pos++; + } + bool always_modifier = false; + bool close_modifier = false; + for (int pos = 1; pos < map_kind_pos; ++pos) + { + cp_token *tok = cp_lexer_peek_token (parser->lexer); + if (tok->type == CPP_COMMA) + { + cp_lexer_consume_token (parser->lexer); + continue; + } + + const char *p = IDENTIFIER_POINTER (tok->u.value); if (strcmp ("always", p) == 0) { - int nth = 2; - if (cp_lexer_peek_nth_token (parser->lexer, 2)->type == CPP_COMMA) - nth++; - if ((cp_lexer_peek_nth_token (parser->lexer, nth)->type == CPP_NAME - || (cp_lexer_peek_nth_token (parser->lexer, nth)->keyword - == RID_DELETE)) - && (cp_lexer_peek_nth_token (parser->lexer, nth + 1)->type - == CPP_COLON)) + if (always_modifier) { - always = true; - cp_lexer_consume_token (parser->lexer); - if (nth == 3) - cp_lexer_consume_token (parser->lexer); + cp_parser_error (parser, "too many %<always%> modifiers"); + cp_parser_skip_to_closing_parenthesis (parser, + /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/true); + return list; } + always_modifier = true; } + else if (strcmp ("close", p) == 0) + { + if (close_modifier) + { + cp_parser_error (parser, "too many %<close%> modifiers"); + cp_parser_skip_to_closing_parenthesis (parser, + /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/true); + return list; + } + close_modifier = true; + } + else + { + cp_parser_error (parser, "%<#pragma omp target%> with " + "modifier other than %<always%> or %<close%>" + "on %<map%> clause"); + cp_parser_skip_to_closing_parenthesis (parser, + /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/true); + return list; + } + + cp_lexer_consume_token (parser->lexer); } if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) @@ -37904,11 +37975,11 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list) if (strcmp ("alloc", p) == 0) kind = GOMP_MAP_ALLOC; else if (strcmp ("to", p) == 0) - kind = always ? GOMP_MAP_ALWAYS_TO : GOMP_MAP_TO; + kind = always_modifier ? GOMP_MAP_ALWAYS_TO : GOMP_MAP_TO; else if (strcmp ("from", p) == 0) - kind = always ? GOMP_MAP_ALWAYS_FROM : GOMP_MAP_FROM; + kind = always_modifier ? GOMP_MAP_ALWAYS_FROM : GOMP_MAP_FROM; else if (strcmp ("tofrom", p) == 0) - kind = always ? GOMP_MAP_ALWAYS_TOFROM : GOMP_MAP_TOFROM; + kind = always_modifier ? GOMP_MAP_ALWAYS_TOFROM : GOMP_MAP_TOFROM; else if (strcmp ("release", p) == 0) kind = GOMP_MAP_RELEASE; else diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 85c05af..bf99635 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -14437,55 +14437,22 @@ most_general_lambda (tree t) } /* We're instantiating a variable from template function TCTX. Return the - corresponding current enclosing scope. This gets complicated because lambda - functions in templates are regenerated rather than instantiated, but generic - lambda functions are subsequently instantiated. */ + corresponding current enclosing scope. We can match them up using + DECL_SOURCE_LOCATION because lambdas only ever have one source location, and + the DECL_SOURCE_LOCATION for a function instantiation is updated to match + the template definition in regenerate_decl_from_template. */ static tree -enclosing_instantiation_of (tree otctx) +enclosing_instantiation_of (tree tctx) { - tree tctx = otctx; tree fn = current_function_decl; - int lambda_count = 0; - for (; tctx && (lambda_fn_in_template_p (tctx) - || regenerated_lambda_fn_p (tctx)); - tctx = decl_function_context (tctx)) - ++lambda_count; - - if (!tctx) - { - /* Match using DECL_SOURCE_LOCATION, which is unique for all lambdas. - - For GCC 11 the above condition limits this to the previously failing - case where all enclosing functions are lambdas (95870). FIXME. */ - for (tree ofn = fn; ofn; ofn = decl_function_context (ofn)) - if (DECL_SOURCE_LOCATION (ofn) == DECL_SOURCE_LOCATION (otctx)) - return ofn; - gcc_unreachable (); - } + /* We shouldn't ever need to do this for other artificial functions. */ + gcc_assert (!DECL_ARTIFICIAL (tctx) || LAMBDA_FUNCTION_P (tctx)); for (; fn; fn = decl_function_context (fn)) - { - tree ofn = fn; - int flambda_count = 0; - for (; fn && regenerated_lambda_fn_p (fn); - fn = decl_function_context (fn)) - ++flambda_count; - if ((fn && DECL_TEMPLATE_INFO (fn)) - ? most_general_template (fn) != most_general_template (tctx) - : fn != tctx) - continue; - if (flambda_count != lambda_count) - { - gcc_assert (flambda_count > lambda_count); - for (; flambda_count > lambda_count; --flambda_count) - ofn = decl_function_context (ofn); - } - gcc_assert (DECL_NAME (ofn) == DECL_NAME (otctx) - || DECL_CONV_FN_P (ofn)); - return ofn; - } + if (DECL_SOURCE_LOCATION (fn) == DECL_SOURCE_LOCATION (tctx)) + return fn; gcc_unreachable (); } @@ -19801,6 +19768,7 @@ tsubst_copy_and_build (tree t, warning_sentinel s(warn_useless_cast); warning_sentinel s2(warn_ignored_qualifiers); + warning_sentinel s3(warn_int_in_bool_context); switch (TREE_CODE (t)) { case CAST_EXPR: @@ -25491,6 +25459,9 @@ regenerate_decl_from_template (tree decl, tree tmpl, tree args) int args_depth; int parms_depth; + /* Use the source location of the definition. */ + DECL_SOURCE_LOCATION (decl) = DECL_SOURCE_LOCATION (tmpl); + args_depth = TMPL_ARGS_DEPTH (args); parms_depth = TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl)); if (args_depth > parms_depth) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 852617d..dc0bde8 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -1240,7 +1240,8 @@ See RS/6000 and PowerPC Options. -mgnu-attribute -mno-gnu-attribute @gol -mstack-protector-guard=@var{guard} -mstack-protector-guard-reg=@var{reg} @gol -mstack-protector-guard-offset=@var{offset} -mprefixed -mno-prefixed @gol --mpcrel -mno-pcrel -mmma -mno-mmma} +-mpcrel -mno-pcrel -mmma -mno-mmma -mrop-protect -mno-rop-protect @gol +-mprivileged -mno-privileged} @emph{RX Options} @gccoptlist{-m64bit-doubles -m32bit-doubles -fpu -nofpu@gol @@ -26284,6 +26285,12 @@ Generate code for given the specified PTX ISA (e.g.@: @samp{sm_35}). ISA strings must be lower-case. Valid ISA strings include @samp{sm_30} and @samp{sm_35}. The default ISA is sm_35. +@item -mptx=@var{version-string} +@opindex mptx +Generate code for given the specified PTX version (e.g.@: @samp{6.3}). +Valid version strings include @samp{3.1} and @samp{6.3}. The default PTX +version is 3.1. + @item -mmainkernel @opindex mmainkernel Link in code for a __main kernel. This is for stand-alone instead of @@ -27023,7 +27030,8 @@ following options: -mmulhw -mdlmzb -mmfpgpr -mvsx @gol -mcrypto -mhtm -mpower8-fusion -mpower8-vector @gol -mquad-memory -mquad-memory-atomic -mfloat128 @gol --mfloat128-hardware -mprefixed -mpcrel -mmma} +-mfloat128-hardware -mprefixed -mpcrel -mmma @gol +-mrop-protect} The particular options set for any particular CPU varies between compiler versions, depending on what setting seems to produce optimal @@ -28028,6 +28036,20 @@ store instructions when the option @option{-mcpu=future} is used. Generate (do not generate) the MMA instructions when the option @option{-mcpu=future} is used. +@item -mrop-protect +@itemx -mno-rop-protect +@opindex mrop-protect +@opindex mno-rop-protect +Generate (do not generate) ROP protection instructions when the target +processor supports them. Currently this option disables the shrink-wrap +optimization (@option{-fshrink-wrap}). + +@item -mprivileged +@itemx -mno-privileged +@opindex mprivileged +@opindex mno-privileged +Generate (do not generate) code that will run in privileged state. + @item -mblock-ops-unaligned-vsx @itemx -mno-block-ops-unaligned-vsx @opindex block-ops-unaligned-vsx diff --git a/gcc/doc/sourcebuild.texi b/gcc/doc/sourcebuild.texi index 3a1d653..ceb6b99 100644 --- a/gcc/doc/sourcebuild.texi +++ b/gcc/doc/sourcebuild.texi @@ -2125,9 +2125,9 @@ ARM Target supports options suitable for accessing the SIMD32 intrinsics from @code{arm_acle.h}. Some multilibs may be incompatible with these options. -@item arm_qbit_ok -@anchor{arm_qbit_ok} -ARM Target supports options suitable for accessing the Q-bit manipulation +@item arm_sat_ok +@anchor{arm_sat_ok} +ARM Target supports options suitable for accessing the saturation intrinsics from @code{arm_acle.h}. Some multilibs may be incompatible with these options. diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index 8fc8298..b99598e 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -31956,6 +31956,7 @@ dwarf2out_finish (const char *filename) add_AT_addr (main_comp_unit_die, DW_AT_entry_pc, const0_rtx, true); add_ranges (NULL); + have_multiple_function_sections = true; } } @@ -10949,61 +10949,8 @@ expand_expr_real_1 (tree exp, rtx target, machine_mode tmode, goto normal_inner_ref; case COMPONENT_REF: - /* If the operand is a CONSTRUCTOR, we can just extract the - appropriate field if it is present. */ - if (TREE_CODE (treeop0) == CONSTRUCTOR) - { - unsigned HOST_WIDE_INT idx; - tree field, value; - scalar_int_mode field_mode; - - FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (treeop0), - idx, field, value) - if (field == treeop1 - /* We can normally use the value of the field in the - CONSTRUCTOR. However, if this is a bitfield in - an integral mode that we can fit in a HOST_WIDE_INT, - we must mask only the number of bits in the bitfield, - since this is done implicitly by the constructor. If - the bitfield does not meet either of those conditions, - we can't do this optimization. */ - && (! DECL_BIT_FIELD (field) - || (is_int_mode (DECL_MODE (field), &field_mode) - && (GET_MODE_PRECISION (field_mode) - <= HOST_BITS_PER_WIDE_INT)))) - { - if (DECL_BIT_FIELD (field) - && modifier == EXPAND_STACK_PARM) - target = 0; - op0 = expand_expr (value, target, tmode, modifier); - if (DECL_BIT_FIELD (field)) - { - HOST_WIDE_INT bitsize = TREE_INT_CST_LOW (DECL_SIZE (field)); - scalar_int_mode imode - = SCALAR_INT_TYPE_MODE (TREE_TYPE (field)); - - if (TYPE_UNSIGNED (TREE_TYPE (field))) - { - op1 = gen_int_mode ((HOST_WIDE_INT_1 << bitsize) - 1, - imode); - op0 = expand_and (imode, op0, op1, target); - } - else - { - int count = GET_MODE_PRECISION (imode) - bitsize; - - op0 = expand_shift (LSHIFT_EXPR, imode, op0, count, - target, 0); - op0 = expand_shift (RSHIFT_EXPR, imode, op0, count, - target, 0); - } - } - - return op0; - } - } - goto normal_inner_ref; - + gcc_assert (TREE_CODE (treeop0) != CONSTRUCTOR); + /* Fall through. */ case BIT_FIELD_REF: case ARRAY_RANGE_REF: normal_inner_ref: diff --git a/gcc/fortran/ChangeLog b/gcc/fortran/ChangeLog index 71cc3d8..781dedd 100644 --- a/gcc/fortran/ChangeLog +++ b/gcc/fortran/ChangeLog @@ -1,3 +1,28 @@ +2021-05-14 Tobias Burnus <tobias@codesourcery.com> + + * dump-parse-tree.c (show_omp_node, show_code_node): Handle + EXEC_OMP_PARALLEL_MASTER. + * frontend-passes.c (gfc_code_walker): Likewise. + * gfortran.h (enum gfc_statement): Add ST_OMP_PARALLEL_MASTER and + ST_OMP_END_PARALLEL_MASTER. + (enum gfc_exec_op): Add EXEC_OMP_PARALLEL_MASTER.. + * match.h (gfc_match_omp_parallel_master): Handle it. + * openmp.c (gfc_match_omp_parallel_master, resolve_omp_clauses, + omp_code_to_statement, gfc_resolve_omp_directive): Likewise. + * parse.c (decode_omp_directive, case_exec_markers, + gfc_ascii_statement, parse_omp_structured_block, + parse_executable): Likewise. + * resolve.c (gfc_resolve_blocks, gfc_resolve_code): Likewise. + * st.c (gfc_free_statement): Likewise. + * trans-openmp.c (gfc_trans_omp_parallel_master, + gfc_trans_omp_workshare, gfc_trans_omp_directive): Likewise. + * trans.c (trans_code): Likewise. + +2021-05-14 Tobias Burnus <tobias@codesourcery.com> + + * resolve.c (resolve_symbol): Handle implicit SAVE of main-program + for vars in 'omp threadprivate' and 'omp declare target'. + 2021-05-10 Martin Liska <mliska@suse.cz> * decl.c (variable_decl): Use startswith diff --git a/gcc/fortran/dump-parse-tree.c b/gcc/fortran/dump-parse-tree.c index b50265a..874e6d4 100644 --- a/gcc/fortran/dump-parse-tree.c +++ b/gcc/fortran/dump-parse-tree.c @@ -1856,6 +1856,7 @@ show_omp_node (int level, gfc_code *c) case EXEC_OMP_PARALLEL: name = "PARALLEL"; break; case EXEC_OMP_PARALLEL_DO: name = "PARALLEL DO"; break; case EXEC_OMP_PARALLEL_DO_SIMD: name = "PARALLEL DO SIMD"; break; + case EXEC_OMP_PARALLEL_MASTER: name = "PARALLEL MASTER"; break; case EXEC_OMP_PARALLEL_SECTIONS: name = "PARALLEL SECTIONS"; break; case EXEC_OMP_PARALLEL_WORKSHARE: name = "PARALLEL WORKSHARE"; break; case EXEC_OMP_SCAN: name = "SCAN"; break; @@ -1927,6 +1928,7 @@ show_omp_node (int level, gfc_code *c) case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_PARALLEL_WORKSHARE: case EXEC_OMP_SCAN: @@ -3139,6 +3141,7 @@ show_code_node (int level, gfc_code *c) case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_PARALLEL_WORKSHARE: case EXEC_OMP_SCAN: diff --git a/gcc/fortran/frontend-passes.c b/gcc/fortran/frontend-passes.c index 93ac4b4..ffe2db4 100644 --- a/gcc/fortran/frontend-passes.c +++ b/gcc/fortran/frontend-passes.c @@ -5542,6 +5542,7 @@ gfc_code_walker (gfc_code **c, walk_code_fn_t codefn, walk_expr_fn_t exprfn, case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: in_omp_workshare = false; diff --git a/gcc/fortran/gfortran.h b/gcc/fortran/gfortran.h index 4f5d2f8..bab785b 100644 --- a/gcc/fortran/gfortran.h +++ b/gcc/fortran/gfortran.h @@ -266,7 +266,8 @@ enum gfc_statement ST_OMP_REQUIRES, ST_PROCEDURE, ST_GENERIC, ST_CRITICAL, ST_END_CRITICAL, ST_GET_FCN_CHARACTERISTICS, ST_LOCK, ST_UNLOCK, ST_EVENT_POST, ST_EVENT_WAIT, ST_FAIL_IMAGE, ST_FORM_TEAM, ST_CHANGE_TEAM, - ST_END_TEAM, ST_SYNC_TEAM, ST_NONE + ST_END_TEAM, ST_SYNC_TEAM, ST_OMP_PARALLEL_MASTER, + ST_OMP_END_PARALLEL_MASTER, ST_NONE }; /* Types of interfaces that we can have. Assignment interfaces are @@ -2704,7 +2705,8 @@ enum gfc_exec_op EXEC_OMP_TARGET_ENTER_DATA, EXEC_OMP_TARGET_EXIT_DATA, EXEC_OMP_TARGET_PARALLEL, EXEC_OMP_TARGET_PARALLEL_DO, EXEC_OMP_TARGET_PARALLEL_DO_SIMD, EXEC_OMP_TARGET_SIMD, - EXEC_OMP_TASKLOOP, EXEC_OMP_TASKLOOP_SIMD, EXEC_OMP_SCAN, EXEC_OMP_DEPOBJ + EXEC_OMP_TASKLOOP, EXEC_OMP_TASKLOOP_SIMD, EXEC_OMP_SCAN, EXEC_OMP_DEPOBJ, + EXEC_OMP_PARALLEL_MASTER }; typedef struct gfc_code diff --git a/gcc/fortran/match.h b/gcc/fortran/match.h index b72ec67..09c5723 100644 --- a/gcc/fortran/match.h +++ b/gcc/fortran/match.h @@ -174,6 +174,7 @@ match gfc_match_omp_ordered_depend (void); match gfc_match_omp_parallel (void); match gfc_match_omp_parallel_do (void); match gfc_match_omp_parallel_do_simd (void); +match gfc_match_omp_parallel_master (void); match gfc_match_omp_parallel_sections (void); match gfc_match_omp_parallel_workshare (void); match gfc_match_omp_requires (void); diff --git a/gcc/fortran/openmp.c b/gcc/fortran/openmp.c index 7eeabff..294b6d0 100644 --- a/gcc/fortran/openmp.c +++ b/gcc/fortran/openmp.c @@ -3770,6 +3770,13 @@ gfc_match_omp_parallel_do_simd (void) match +gfc_match_omp_parallel_master (void) +{ + return match_omp (EXEC_OMP_PARALLEL_MASTER, OMP_PARALLEL_CLAUSES); +} + + +match gfc_match_omp_parallel_sections (void) { return match_omp (EXEC_OMP_PARALLEL_SECTIONS, @@ -4833,6 +4840,7 @@ resolve_omp_clauses (gfc_code *code, gfc_omp_clauses *omp_clauses, case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_PARALLEL_WORKSHARE: case EXEC_OMP_DISTRIBUTE_PARALLEL_DO: @@ -6796,6 +6804,8 @@ omp_code_to_statement (gfc_code *code) { case EXEC_OMP_PARALLEL: return ST_OMP_PARALLEL; + case EXEC_OMP_PARALLEL_MASTER: + return ST_OMP_PARALLEL_MASTER; case EXEC_OMP_PARALLEL_SECTIONS: return ST_OMP_PARALLEL_SECTIONS; case EXEC_OMP_SECTIONS: @@ -7312,6 +7322,7 @@ gfc_resolve_omp_directive (gfc_code *code, gfc_namespace *ns) case EXEC_OMP_CANCEL: case EXEC_OMP_PARALLEL_WORKSHARE: case EXEC_OMP_PARALLEL: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_SECTIONS: case EXEC_OMP_SINGLE: diff --git a/gcc/fortran/parse.c b/gcc/fortran/parse.c index 9bbe9e8..6efb3fd 100644 --- a/gcc/fortran/parse.c +++ b/gcc/fortran/parse.c @@ -925,6 +925,8 @@ decode_omp_directive (void) matchs ("end parallel do simd", gfc_match_omp_eos_error, ST_OMP_END_PARALLEL_DO_SIMD); matcho ("end parallel do", gfc_match_omp_eos_error, ST_OMP_END_PARALLEL_DO); + matcho ("end parallel master", gfc_match_omp_eos_error, + ST_OMP_END_PARALLEL_MASTER); matcho ("end parallel sections", gfc_match_omp_eos_error, ST_OMP_END_PARALLEL_SECTIONS); matcho ("end parallel workshare", gfc_match_omp_eos_error, @@ -990,6 +992,8 @@ decode_omp_directive (void) matchs ("parallel do simd", gfc_match_omp_parallel_do_simd, ST_OMP_PARALLEL_DO_SIMD); matcho ("parallel do", gfc_match_omp_parallel_do, ST_OMP_PARALLEL_DO); + matcho ("parallel master", gfc_match_omp_parallel_master, + ST_OMP_PARALLEL_MASTER); matcho ("parallel sections", gfc_match_omp_parallel_sections, ST_OMP_PARALLEL_SECTIONS); matcho ("parallel workshare", gfc_match_omp_parallel_workshare, @@ -1605,7 +1609,7 @@ next_statement (void) #define case_exec_markers case ST_DO: case ST_FORALL_BLOCK: \ case ST_IF_BLOCK: case ST_BLOCK: case ST_ASSOCIATE: \ case ST_WHERE_BLOCK: case ST_SELECT_CASE: case ST_SELECT_TYPE: \ - case ST_SELECT_RANK: case ST_OMP_PARALLEL: \ + case ST_SELECT_RANK: case ST_OMP_PARALLEL: case ST_OMP_PARALLEL_MASTER: \ case ST_OMP_PARALLEL_SECTIONS: case ST_OMP_SECTIONS: case ST_OMP_ORDERED: \ case ST_OMP_CRITICAL: case ST_OMP_MASTER: case ST_OMP_SINGLE: \ case ST_OMP_DO: case ST_OMP_PARALLEL_DO: case ST_OMP_ATOMIC: \ @@ -2349,6 +2353,9 @@ gfc_ascii_statement (gfc_statement st) case ST_OMP_END_PARALLEL_DO_SIMD: p = "!$OMP END PARALLEL DO SIMD"; break; + case ST_OMP_END_PARALLEL_MASTER: + p = "!$OMP END PARALLEL MASTER"; + break; case ST_OMP_END_PARALLEL_SECTIONS: p = "!$OMP END PARALLEL SECTIONS"; break; @@ -2443,6 +2450,9 @@ gfc_ascii_statement (gfc_statement st) case ST_OMP_PARALLEL_DO_SIMD: p = "!$OMP PARALLEL DO SIMD"; break; + case ST_OMP_PARALLEL_MASTER: + p = "!$OMP PARALLEL MASTER"; + break; case ST_OMP_PARALLEL_SECTIONS: p = "!$OMP PARALLEL SECTIONS"; break; @@ -5255,6 +5265,9 @@ parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only) case ST_OMP_PARALLEL: omp_end_st = ST_OMP_END_PARALLEL; break; + case ST_OMP_PARALLEL_MASTER: + omp_end_st = ST_OMP_END_PARALLEL_MASTER; + break; case ST_OMP_PARALLEL_SECTIONS: omp_end_st = ST_OMP_END_PARALLEL_SECTIONS; break; @@ -5379,6 +5392,7 @@ parse_omp_structured_block (gfc_statement omp_st, bool workshare_stmts_only) break; case ST_OMP_PARALLEL: + case ST_OMP_PARALLEL_MASTER: case ST_OMP_PARALLEL_SECTIONS: parse_omp_structured_block (st, false); break; @@ -5580,6 +5594,7 @@ parse_executable (gfc_statement st) break; case ST_OMP_PARALLEL: + case ST_OMP_PARALLEL_MASTER: case ST_OMP_PARALLEL_SECTIONS: case ST_OMP_SECTIONS: case ST_OMP_ORDERED: diff --git a/gcc/fortran/resolve.c b/gcc/fortran/resolve.c index 5a81387..747516f 100644 --- a/gcc/fortran/resolve.c +++ b/gcc/fortran/resolve.c @@ -10802,6 +10802,7 @@ gfc_resolve_blocks (gfc_code *b, gfc_namespace *ns) case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_PARALLEL_WORKSHARE: case EXEC_OMP_SECTIONS: @@ -11763,6 +11764,7 @@ gfc_resolve_code (gfc_code *code, gfc_namespace *ns) case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_TARGET_PARALLEL: case EXEC_OMP_TARGET_PARALLEL_DO: @@ -11938,6 +11940,12 @@ start: if (resolve_ordinary_assign (code, ns)) { + if (omp_workshare_flag) + { + gfc_error ("Expected intrinsic assignment in OMP WORKSHARE " + "at %L", &code->loc); + break; + } if (code->op == EXEC_COMPCALL) goto compcall; else @@ -12243,6 +12251,7 @@ start: case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_PARALLEL_WORKSHARE: omp_workshare_save = omp_workshare_flag; @@ -16039,7 +16048,8 @@ resolve_symbol (gfc_symbol *sym) && !(sym->ns->save_all && !sym->attr.automatic) && sym->module == NULL && (sym->ns->proc_name == NULL - || sym->ns->proc_name->attr.flavor != FL_MODULE)) + || (sym->ns->proc_name->attr.flavor != FL_MODULE + && !sym->ns->proc_name->attr.is_main_program))) gfc_error ("Threadprivate at %L isn't SAVEd", &sym->declared_at); /* Check omp declare target restrictions. */ @@ -16050,7 +16060,8 @@ resolve_symbol (gfc_symbol *sym) && (!sym->attr.in_common && sym->module == NULL && (sym->ns->proc_name == NULL - || sym->ns->proc_name->attr.flavor != FL_MODULE))) + || (sym->ns->proc_name->attr.flavor != FL_MODULE + && !sym->ns->proc_name->attr.is_main_program)))) gfc_error ("!$OMP DECLARE TARGET variable %qs at %L isn't SAVEd", sym->name, &sym->declared_at); diff --git a/gcc/fortran/st.c b/gcc/fortran/st.c index 9e76199..02a81da 100644 --- a/gcc/fortran/st.c +++ b/gcc/fortran/st.c @@ -230,6 +230,7 @@ gfc_free_statement (gfc_code *p) case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_PARALLEL_WORKSHARE: case EXEC_OMP_SCAN: diff --git a/gcc/fortran/trans-openmp.c b/gcc/fortran/trans-openmp.c index aa3a82e..5666cd6 100644 --- a/gcc/fortran/trans-openmp.c +++ b/gcc/fortran/trans-openmp.c @@ -5554,6 +5554,28 @@ gfc_trans_omp_parallel_do_simd (gfc_code *code, stmtblock_t *pblock, } static tree +gfc_trans_omp_parallel_master (gfc_code *code) +{ + stmtblock_t block; + tree stmt, omp_clauses; + + gfc_start_block (&block); + omp_clauses = gfc_trans_omp_clauses (&block, code->ext.omp_clauses, + code->loc); + pushlevel (); + stmt = gfc_trans_omp_master (code); + if (TREE_CODE (stmt) != BIND_EXPR) + stmt = build3_v (BIND_EXPR, NULL, stmt, poplevel (1, 0)); + else + poplevel (0, 0); + stmt = build2_loc (gfc_get_location (&code->loc), OMP_PARALLEL, + void_type_node, stmt, omp_clauses); + OMP_PARALLEL_COMBINED (stmt) = 1; + gfc_add_expr_to_block (&block, stmt); + return gfc_finish_block (&block); +} + +static tree gfc_trans_omp_parallel_sections (gfc_code *code) { stmtblock_t block; @@ -6092,6 +6114,7 @@ gfc_trans_omp_workshare (gfc_code *code, gfc_omp_clauses *clauses) case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_PARALLEL_WORKSHARE: case EXEC_OMP_CRITICAL: @@ -6273,6 +6296,8 @@ gfc_trans_omp_directive (gfc_code *code) return gfc_trans_omp_parallel_do (code, NULL, NULL); case EXEC_OMP_PARALLEL_DO_SIMD: return gfc_trans_omp_parallel_do_simd (code, NULL, NULL); + case EXEC_OMP_PARALLEL_MASTER: + return gfc_trans_omp_parallel_master (code); case EXEC_OMP_PARALLEL_SECTIONS: return gfc_trans_omp_parallel_sections (code); case EXEC_OMP_PARALLEL_WORKSHARE: diff --git a/gcc/fortran/trans.c b/gcc/fortran/trans.c index 624c713..9f296bd 100644 --- a/gcc/fortran/trans.c +++ b/gcc/fortran/trans.c @@ -2174,6 +2174,7 @@ trans_code (gfc_code * code, tree cond) case EXEC_OMP_PARALLEL: case EXEC_OMP_PARALLEL_DO: case EXEC_OMP_PARALLEL_DO_SIMD: + case EXEC_OMP_PARALLEL_MASTER: case EXEC_OMP_PARALLEL_SECTIONS: case EXEC_OMP_PARALLEL_WORKSHARE: case EXEC_OMP_SECTIONS: diff --git a/gcc/gcov-io.c b/gcc/gcov-io.c index 21ca394..4b1e11d 100644 --- a/gcc/gcov-io.c +++ b/gcc/gcov-io.c @@ -27,40 +27,14 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see /* Routines declared in gcov-io.h. This file should be #included by another source file, after having #included gcov-io.h. */ -#if !IN_GCOV -static void gcov_write_block (unsigned); -static gcov_unsigned_t *gcov_write_words (unsigned); -#endif -static const gcov_unsigned_t *gcov_read_words (unsigned); -#if !IN_LIBGCOV -static void gcov_allocate (unsigned); -#endif - -/* Optimum number of gcov_unsigned_t's read from or written to disk. */ -#define GCOV_BLOCK_SIZE (1 << 10) +static gcov_unsigned_t *gcov_read_words (void *buffer, unsigned); struct gcov_var { FILE *file; - gcov_position_t start; /* Position of first byte of block */ - unsigned offset; /* Read/write position within the block. */ - unsigned length; /* Read limit in the block. */ - unsigned overread; /* Number of words overread. */ int error; /* < 0 overflow, > 0 disk error. */ - int mode; /* < 0 writing, > 0 reading */ + int mode; /* < 0 writing, > 0 reading. */ int endian; /* Swap endianness. */ -#if IN_LIBGCOV - /* Holds one block plus 4 bytes, thus all coverage reads & writes - fit within this buffer and we always can transfer GCOV_BLOCK_SIZE - to and from the disk. libgcov never backtracks and only writes 4 - or 8 byte objects. */ - gcov_unsigned_t buffer[GCOV_BLOCK_SIZE + 1]; -#else - /* Holds a variable length block, as the compiler can write - strings and needs to backtrack. */ - size_t alloc; - gcov_unsigned_t *buffer; -#endif } gcov_var; /* Save the current position in the gcov file. */ @@ -71,8 +45,7 @@ static inline gcov_position_t gcov_position (void) { - gcov_nonruntime_assert (gcov_var.mode > 0); - return gcov_var.start + gcov_var.offset; + return ftell (gcov_var.file); } /* Return nonzero if the error flag is set. */ @@ -92,20 +65,16 @@ GCOV_LINKAGE inline void gcov_rewrite (void) { gcov_var.mode = -1; - gcov_var.start = 0; - gcov_var.offset = 0; fseek (gcov_var.file, 0L, SEEK_SET); } #endif -static inline gcov_unsigned_t from_file (gcov_unsigned_t value) +static inline gcov_unsigned_t +from_file (gcov_unsigned_t value) { #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) if (gcov_var.endian) - { - value = (value >> 16) | (value << 16); - value = ((value & 0xff00ff) << 8) | ((value >> 8) & 0xff00ff); - } + return __builtin_bswap32 (value); #endif return value; } @@ -142,9 +111,6 @@ gcov_open (const char *name, int mode) #endif gcov_nonruntime_assert (!gcov_var.file); - gcov_var.start = 0; - gcov_var.offset = gcov_var.length = 0; - gcov_var.overread = -1u; gcov_var.error = 0; #if !IN_LIBGCOV || defined (IN_GCOV_TOOL) gcov_var.endian = 0; @@ -222,8 +188,6 @@ gcov_open (const char *name, int mode) gcov_var.mode = mode ? mode : 1; - setbuf (gcov_var.file, (char *)0); - return 1; } @@ -235,19 +199,9 @@ gcov_close (void) { if (gcov_var.file) { -#if !IN_GCOV - if (gcov_var.offset && gcov_var.mode < 0) - gcov_write_block (gcov_var.offset); -#endif fclose (gcov_var.file); gcov_var.file = 0; - gcov_var.length = 0; } -#if !IN_LIBGCOV - free (gcov_var.buffer); - gcov_var.alloc = 0; - gcov_var.buffer = 0; -#endif gcov_var.mode = 0; return gcov_var.error; } @@ -262,9 +216,8 @@ gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected) { if (magic == expected) return 1; - magic = (magic >> 16) | (magic << 16); - magic = ((magic & 0xff00ff) << 8) | ((magic >> 8) & 0xff00ff); - if (magic == expected) + + if (__builtin_bswap32 (magic) == expected) { gcov_var.endian = 1; return -1; @@ -273,71 +226,15 @@ gcov_magic (gcov_unsigned_t magic, gcov_unsigned_t expected) } #endif -#if !IN_LIBGCOV -static void -gcov_allocate (unsigned length) -{ - size_t new_size = gcov_var.alloc; - - if (!new_size) - new_size = GCOV_BLOCK_SIZE; - new_size += length; - new_size *= 2; - - gcov_var.alloc = new_size; - gcov_var.buffer = XRESIZEVAR (gcov_unsigned_t, gcov_var.buffer, new_size << 2); -} -#endif - #if !IN_GCOV -/* Write out the current block, if needs be. */ - -static void -gcov_write_block (unsigned size) -{ - if (fwrite (gcov_var.buffer, size << 2, 1, gcov_var.file) != 1) - gcov_var.error = 1; - gcov_var.start += size; - gcov_var.offset -= size; -} - -/* Allocate space to write BYTES bytes to the gcov file. Return a - pointer to those bytes, or NULL on failure. */ - -static gcov_unsigned_t * -gcov_write_words (unsigned words) -{ - gcov_unsigned_t *result; - - gcov_nonruntime_assert (gcov_var.mode < 0); -#if IN_LIBGCOV - if (gcov_var.offset >= GCOV_BLOCK_SIZE) - { - gcov_write_block (GCOV_BLOCK_SIZE); - if (gcov_var.offset) - { - memcpy (gcov_var.buffer, gcov_var.buffer + GCOV_BLOCK_SIZE, 4); - } - } -#else - if (gcov_var.offset + words > gcov_var.alloc) - gcov_allocate (gcov_var.offset + words); -#endif - result = &gcov_var.buffer[gcov_var.offset]; - gcov_var.offset += words; - - return result; -} - -/* Write unsigned VALUE to coverage file. Sets error flag - appropriately. */ +/* Write unsigned VALUE to coverage file. */ GCOV_LINKAGE void gcov_write_unsigned (gcov_unsigned_t value) { - gcov_unsigned_t *buffer = gcov_write_words (1); - - buffer[0] = value; + gcov_unsigned_t r = fwrite (&value, sizeof (value), 1, gcov_var.file); + if (r != 1) + gcov_var.error = 1; } /* Write counter VALUE to coverage file. Sets error flag @@ -347,13 +244,11 @@ gcov_write_unsigned (gcov_unsigned_t value) GCOV_LINKAGE void gcov_write_counter (gcov_type value) { - gcov_unsigned_t *buffer = gcov_write_words (2); - - buffer[0] = (gcov_unsigned_t) value; + gcov_write_unsigned ((gcov_unsigned_t) value); if (sizeof (value) > sizeof (gcov_unsigned_t)) - buffer[1] = (gcov_unsigned_t) (value >> 32); + gcov_write_unsigned ((gcov_unsigned_t) (value >> 32)); else - buffer[1] = 0; + gcov_write_unsigned (0); } #endif /* IN_LIBGCOV */ @@ -365,23 +260,16 @@ GCOV_LINKAGE void gcov_write_string (const char *string) { unsigned length = 0; - unsigned alloc = 0; - gcov_unsigned_t *buffer; if (string) - { - length = strlen (string); - alloc = (length + 4) >> 2; - } - - buffer = gcov_write_words (1 + alloc); + length = strlen (string) + 1; - buffer[0] = alloc; - - if (alloc > 0) + gcov_write_unsigned (length); + if (length > 0) { - buffer[alloc] = 0; /* place nul terminators. */ - memcpy (&buffer[1], string, length); + gcov_unsigned_t r = fwrite (string, length, 1, gcov_var.file); + if (r != 1) + gcov_var.error = 1; } } #endif @@ -418,6 +306,14 @@ gcov_write_filename (const char *filename) } #endif +/* Move to a given position in a gcov file. */ + +GCOV_LINKAGE void +gcov_seek (gcov_position_t base) +{ + fseek (gcov_var.file, base, SEEK_SET); +} + #if !IN_LIBGCOV /* Write a tag TAG and reserve space for the record length. Return a value to be used for gcov_write_length. */ @@ -425,11 +321,9 @@ gcov_write_filename (const char *filename) GCOV_LINKAGE gcov_position_t gcov_write_tag (gcov_unsigned_t tag) { - gcov_position_t result = gcov_var.start + gcov_var.offset; - gcov_unsigned_t *buffer = gcov_write_words (2); - - buffer[0] = tag; - buffer[1] = 0; + gcov_position_t result = gcov_position (); + gcov_write_unsigned (tag); + gcov_write_unsigned (0); return result; } @@ -442,19 +336,13 @@ gcov_write_tag (gcov_unsigned_t tag) GCOV_LINKAGE void gcov_write_length (gcov_position_t position) { - unsigned offset; - gcov_unsigned_t length; - gcov_unsigned_t *buffer; - + gcov_position_t current_position = gcov_position (); gcov_nonruntime_assert (gcov_var.mode < 0); - gcov_nonruntime_assert (position + 2 <= gcov_var.start + gcov_var.offset); - gcov_nonruntime_assert (position >= gcov_var.start); - offset = position - gcov_var.start; - length = gcov_var.offset - offset - 2; - buffer = (gcov_unsigned_t *) &gcov_var.buffer[offset]; - buffer[1] = length; - if (gcov_var.offset >= GCOV_BLOCK_SIZE) - gcov_write_block (gcov_var.offset); + gcov_nonruntime_assert (current_position >= position + 2 * GCOV_WORD_SIZE); + + gcov_seek (position + GCOV_WORD_SIZE); + gcov_write_unsigned (current_position - position - 2 * GCOV_WORD_SIZE); + gcov_seek (current_position); } #else /* IN_LIBGCOV */ @@ -464,10 +352,8 @@ gcov_write_length (gcov_position_t position) GCOV_LINKAGE void gcov_write_tag_length (gcov_unsigned_t tag, gcov_unsigned_t length) { - gcov_unsigned_t *buffer = gcov_write_words (2); - - buffer[0] = tag; - buffer[1] = length; + gcov_write_unsigned (tag); + gcov_write_unsigned (length); } /* Write a summary structure to the gcov file. Return nonzero on @@ -485,52 +371,28 @@ gcov_write_summary (gcov_unsigned_t tag, const struct gcov_summary *summary) #endif /*!IN_GCOV */ -/* Return a pointer to read BYTES bytes from the gcov file. Returns +/* Return a pointer to read COUNT bytes from the gcov file. Returns NULL on failure (read past EOF). */ -static const gcov_unsigned_t * -gcov_read_words (unsigned words) +static void * +gcov_read_bytes (void *buffer, unsigned count) { - const gcov_unsigned_t *result; - unsigned excess = gcov_var.length - gcov_var.offset; - if (gcov_var.mode <= 0) return NULL; - if (excess < words) - { - gcov_var.start += gcov_var.offset; - if (excess) - { -#if IN_LIBGCOV - memcpy (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, 4); -#else - memmove (gcov_var.buffer, gcov_var.buffer + gcov_var.offset, - excess * 4); -#endif - } - gcov_var.offset = 0; - gcov_var.length = excess; -#if IN_LIBGCOV - excess = GCOV_BLOCK_SIZE; -#else - if (gcov_var.length + words > gcov_var.alloc) - gcov_allocate (gcov_var.length + words); - excess = gcov_var.alloc - gcov_var.length; -#endif - excess = fread (gcov_var.buffer + gcov_var.length, - 1, excess << 2, gcov_var.file) >> 2; - gcov_var.length += excess; - if (gcov_var.length < words) - { - gcov_var.overread += words - gcov_var.length; - gcov_var.length = 0; - return 0; - } - } - result = &gcov_var.buffer[gcov_var.offset]; - gcov_var.offset += words; - return result; + unsigned read = fread (buffer, count, 1, gcov_var.file); + if (read != 1) + return NULL; + + return buffer; +} + +/* Read WORDS gcov_unsigned_t values from gcov file. */ + +static gcov_unsigned_t * +gcov_read_words (void *buffer, unsigned words) +{ + return (gcov_unsigned_t *)gcov_read_bytes (buffer, GCOV_WORD_SIZE * words); } /* Read unsigned value from a coverage file. Sets error flag on file @@ -540,10 +402,12 @@ GCOV_LINKAGE gcov_unsigned_t gcov_read_unsigned (void) { gcov_unsigned_t value; - const gcov_unsigned_t *buffer = gcov_read_words (1); + gcov_unsigned_t allocated_buffer[1]; + gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 1); if (!buffer) return 0; + value = from_file (buffer[0]); return value; } @@ -555,7 +419,8 @@ GCOV_LINKAGE gcov_type gcov_read_counter (void) { gcov_type value; - const gcov_unsigned_t *buffer = gcov_read_words (2); + gcov_unsigned_t allocated_buffer[2]; + gcov_unsigned_t *buffer = gcov_read_words (&allocated_buffer, 2); if (!buffer) return 0; @@ -632,7 +497,8 @@ gcov_read_string (void) if (!length) return 0; - return (const char *) gcov_read_words (length); + void *buffer = XNEWVEC (char *, length); + return (const char *) gcov_read_bytes (buffer, length); } #endif @@ -654,27 +520,7 @@ gcov_sync (gcov_position_t base, gcov_unsigned_t length) { gcov_nonruntime_assert (gcov_var.mode > 0); base += length; - if (base - gcov_var.start <= gcov_var.length) - gcov_var.offset = base - gcov_var.start; - else - { - gcov_var.offset = gcov_var.length = 0; - fseek (gcov_var.file, base << 2, SEEK_SET); - gcov_var.start = ftell (gcov_var.file) >> 2; - } -} -#endif - -#if IN_LIBGCOV -/* Move to a given position in a gcov file. */ - -GCOV_LINKAGE void -gcov_seek (gcov_position_t base) -{ - if (gcov_var.offset) - gcov_write_block (gcov_var.offset); - fseek (gcov_var.file, base << 2, SEEK_SET); - gcov_var.start = ftell (gcov_var.file) >> 2; + fseek (gcov_var.file, base, SEEK_SET); } #endif diff --git a/gcc/gcov-io.h b/gcc/gcov-io.h index c536554..f7584eb 100644 --- a/gcc/gcov-io.h +++ b/gcc/gcov-io.h @@ -243,22 +243,25 @@ typedef uint64_t gcov_type_unsigned; /* The record tags. Values [1..3f] are for tags which may be in either file. Values [41..9f] for those in the note file and [a1..ff] for the data file. The tag value zero is used as an explicit end of - file marker -- it is not required to be present. */ + file marker -- it is not required to be present. + All length values are in bytes. */ + +#define GCOV_WORD_SIZE 4 #define GCOV_TAG_FUNCTION ((gcov_unsigned_t)0x01000000) -#define GCOV_TAG_FUNCTION_LENGTH (3) +#define GCOV_TAG_FUNCTION_LENGTH (3 * GCOV_WORD_SIZE) #define GCOV_TAG_BLOCKS ((gcov_unsigned_t)0x01410000) #define GCOV_TAG_BLOCKS_LENGTH(NUM) (NUM) #define GCOV_TAG_ARCS ((gcov_unsigned_t)0x01430000) -#define GCOV_TAG_ARCS_LENGTH(NUM) (1 + (NUM) * 2) -#define GCOV_TAG_ARCS_NUM(LENGTH) (((LENGTH) - 1) / 2) +#define GCOV_TAG_ARCS_LENGTH(NUM) (1 + (NUM) * 2 * GCOV_WORD_SIZE) +#define GCOV_TAG_ARCS_NUM(LENGTH) (((LENGTH / GCOV_WORD_SIZE) - 1) / 2) #define GCOV_TAG_LINES ((gcov_unsigned_t)0x01450000) #define GCOV_TAG_COUNTER_BASE ((gcov_unsigned_t)0x01a10000) -#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2) -#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH) / 2) +#define GCOV_TAG_COUNTER_LENGTH(NUM) ((NUM) * 2 * GCOV_WORD_SIZE) +#define GCOV_TAG_COUNTER_NUM(LENGTH) ((LENGTH / GCOV_WORD_SIZE) / 2) #define GCOV_TAG_OBJECT_SUMMARY ((gcov_unsigned_t)0xa1000000) #define GCOV_TAG_PROGRAM_SUMMARY ((gcov_unsigned_t)0xa3000000) /* Obsolete */ -#define GCOV_TAG_SUMMARY_LENGTH (2) +#define GCOV_TAG_SUMMARY_LENGTH (2 * GCOV_WORD_SIZE) #define GCOV_TAG_AFDO_FILE_NAMES ((gcov_unsigned_t)0xaa000000) #define GCOV_TAG_AFDO_FUNCTION ((gcov_unsigned_t)0xac000000) #define GCOV_TAG_AFDO_WORKING_SET ((gcov_unsigned_t)0xaf000000) diff --git a/gcc/genoutput.c b/gcc/genoutput.c index 8e911cc..6d4ab81 100644 --- a/gcc/genoutput.c +++ b/gcc/genoutput.c @@ -757,6 +757,7 @@ validate_insn_alternatives (class data *d) int which_alternative = 0; int alternative_count_unsure = 0; bool seen_write = false; + bool alt_mismatch = false; for (p = d->operand[start].constraint; (c = *p); p += len) { @@ -813,8 +814,19 @@ validate_insn_alternatives (class data *d) if (n == 0) n = d->operand[start].n_alternatives; else if (n != d->operand[start].n_alternatives) - error_at (d->loc, "wrong number of alternatives in operand %d", - start); + { + if (!alt_mismatch) + { + alt_mismatch = true; + error_at (d->loc, + "alternative number mismatch: " + "operand %d has %d, operand %d has %d", + 0, n, start, d->operand[start].n_alternatives); + } + else + error_at (d->loc, "operand %d has %d alternatives", + start, d->operand[start].n_alternatives); + } } } diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc index e94bb35..5b288d8 100644 --- a/gcc/gimple-range.cc +++ b/gcc/gimple-range.cc @@ -737,7 +737,7 @@ range_of_builtin_call (range_query &query, irange &r, gcall *call) query.range_of_expr (r, arg, call); // From clz of minimum we can compute result maximum. - if (r.constant_p ()) + if (r.constant_p () && !r.varying_p ()) { int newmaxi = prec - 1 - wi::floor_log2 (r.lower_bound ()); // Argument is unsigned, so do nothing if it is [0, ...] range. @@ -47,14 +47,18 @@ extern const char *fake_ngettext (const char *singular, const char *plural, #endif +/* Used to immediately translate the argument. */ #ifndef _ # define _(msgid) gettext (msgid) #endif +/* Used to mark strings that will be translated later. */ #ifndef N_ # define N_(msgid) msgid #endif +/* Like N_, but for GCC diagnostic format strings. See ABOUT-GCC-NLS for + details. */ #ifndef G_ # define G_(gmsgid) gmsgid #endif diff --git a/gcc/lto-wrapper.c b/gcc/lto-wrapper.c index d6be7f0..1c26439 100644 --- a/gcc/lto-wrapper.c +++ b/gcc/lto-wrapper.c @@ -48,6 +48,7 @@ along with GCC; see the file COPYING3. If not see #include "simple-object.h" #include "lto-section-names.h" #include "collect-utils.h" +#include "opts-diagnostic.h" /* Environment variable, used for passing the names of offload targets from GCC driver to lto-wrapper. */ @@ -139,12 +140,12 @@ maybe_unlink (const char *file) /* Create decoded options from the COLLECT_GCC and COLLECT_GCC_OPTIONS environment. */ -static void +static vec<cl_decoded_option> get_options_from_collect_gcc_options (const char *collect_gcc, - const char *collect_gcc_options, - struct cl_decoded_option **decoded_options, - unsigned int *decoded_options_count) + const char *collect_gcc_options) { + cl_decoded_option *decoded_options; + unsigned int decoded_options_count; struct obstack argv_obstack; const char **argv; int argc; @@ -157,57 +158,80 @@ get_options_from_collect_gcc_options (const char *collect_gcc, argv = XOBFINISH (&argv_obstack, const char **); decode_cmdline_options_to_array (argc, (const char **)argv, CL_DRIVER, - decoded_options, decoded_options_count); + &decoded_options, &decoded_options_count); + vec<cl_decoded_option> decoded; + decoded.create (decoded_options_count); + for (unsigned i = 0; i < decoded_options_count; ++i) + decoded.quick_push (decoded_options[i]); + free (decoded_options); + obstack_free (&argv_obstack, NULL); + + return decoded; } -/* Append OPTION to the options array DECODED_OPTIONS with size - DECODED_OPTIONS_COUNT. */ +/* Find option in OPTIONS based on OPT_INDEX. -1 value is returned + if the option is not present. */ -static void -append_option (struct cl_decoded_option **decoded_options, - unsigned int *decoded_options_count, - struct cl_decoded_option *option) +static int +find_option (vec<cl_decoded_option> &options, size_t opt_index) { - ++*decoded_options_count; - *decoded_options - = (struct cl_decoded_option *) - xrealloc (*decoded_options, - (*decoded_options_count - * sizeof (struct cl_decoded_option))); - memcpy (&(*decoded_options)[*decoded_options_count - 1], option, - sizeof (struct cl_decoded_option)); + for (unsigned i = 0; i < options.length (); ++i) + if (options[i].opt_index == opt_index) + return i; + + return -1; +} + +static int +find_option (vec<cl_decoded_option> &options, cl_decoded_option *option) +{ + return find_option (options, option->opt_index); } -/* Remove option number INDEX from DECODED_OPTIONS, update - DECODED_OPTIONS_COUNT. */ +/* Merge -flto FOPTION into vector of DECODED_OPTIONS. */ static void -remove_option (struct cl_decoded_option **decoded_options, - int index, unsigned int *decoded_options_count) +merge_flto_options (vec<cl_decoded_option> &decoded_options, + cl_decoded_option *foption) { - --*decoded_options_count; - memmove (&(*decoded_options)[index + 1], - &(*decoded_options)[index], - sizeof (struct cl_decoded_option) - * (*decoded_options_count - index)); + int existing_opt = find_option (decoded_options, foption); + if (existing_opt == -1) + decoded_options.safe_push (*foption); + else + { + if (strcmp (foption->arg, decoded_options[existing_opt].arg) != 0) + { + /* -flto=auto is preferred. */ + if (strcmp (decoded_options[existing_opt].arg, "auto") == 0) + ; + else if (strcmp (foption->arg, "auto") == 0 + || strcmp (foption->arg, "jobserver") == 0) + decoded_options[existing_opt].arg = foption->arg; + else if (strcmp (decoded_options[existing_opt].arg, + "jobserver") != 0) + { + int n = atoi (foption->arg); + int original_n = atoi (decoded_options[existing_opt].arg); + if (n > original_n) + decoded_options[existing_opt].arg = foption->arg; + } + } + } } /* Try to merge and complain about options FDECODED_OPTIONS when applied ontop of DECODED_OPTIONS. */ static void -merge_and_complain (struct cl_decoded_option **decoded_options, - unsigned int *decoded_options_count, - struct cl_decoded_option *fdecoded_options, - unsigned int fdecoded_options_count, - struct cl_decoded_option *decoded_cl_options, - unsigned int decoded_cl_options_count) +merge_and_complain (vec<cl_decoded_option> decoded_options, + vec<cl_decoded_option> fdecoded_options, + vec<cl_decoded_option> decoded_cl_options) { unsigned int i, j; - struct cl_decoded_option *pic_option = NULL; - struct cl_decoded_option *pie_option = NULL; - struct cl_decoded_option *cf_protection_option = NULL; + cl_decoded_option *pic_option = NULL; + cl_decoded_option *pie_option = NULL; + cl_decoded_option *cf_protection_option = NULL; /* ??? Merge options from files. Most cases can be handled by either unioning or intersecting @@ -224,9 +248,9 @@ merge_and_complain (struct cl_decoded_option **decoded_options, /* Look for a -fcf-protection option in the link-time options which overrides any -fcf-protection from the lto sections. */ - for (i = 0; i < decoded_cl_options_count; ++i) + for (i = 0; i < decoded_cl_options.length (); ++i) { - struct cl_decoded_option *foption = &decoded_cl_options[i]; + cl_decoded_option *foption = &decoded_cl_options[i]; if (foption->opt_index == OPT_fcf_protection_) { cf_protection_option = foption; @@ -235,9 +259,10 @@ merge_and_complain (struct cl_decoded_option **decoded_options, /* The following does what the old LTO option code did, union all target and a selected set of common options. */ - for (i = 0; i < fdecoded_options_count; ++i) + for (i = 0; i < fdecoded_options.length (); ++i) { - struct cl_decoded_option *foption = &fdecoded_options[i]; + cl_decoded_option *foption = &fdecoded_options[i]; + int existing_opt = find_option (decoded_options, foption); switch (foption->opt_index) { case OPT_SPECIAL_unknown: @@ -265,11 +290,8 @@ merge_and_complain (struct cl_decoded_option **decoded_options, setting per OPT code, we pick the first we encounter. ??? This doesn't make too much sense, but when it doesn't then we should complain. */ - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); + if (existing_opt == -1) + decoded_options.safe_push (*foption); break; /* Figure out what PIC/PIE level wins and merge the results. */ @@ -285,25 +307,19 @@ merge_and_complain (struct cl_decoded_option **decoded_options, case OPT_fopenmp: case OPT_fopenacc: /* For selected options we can merge conservatively. */ - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); + if (existing_opt == -1) + decoded_options.safe_push (*foption); /* -fopenmp > -fno-openmp, -fopenacc > -fno-openacc */ - else if (foption->value > (*decoded_options)[j].value) - (*decoded_options)[j] = *foption; + else if (foption->value > decoded_options[existing_opt].value) + decoded_options[existing_opt] = *foption; break; case OPT_fopenacc_dim_: /* Append or check identical. */ - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - else if (strcmp ((*decoded_options)[j].arg, foption->arg)) + if (existing_opt == -1) + decoded_options.safe_push (*foption); + else if (strcmp (decoded_options[existing_opt].arg, foption->arg)) fatal_error (input_location, "option %s with different values", foption->orig_option_with_args_text); @@ -314,12 +330,9 @@ merge_and_complain (struct cl_decoded_option **decoded_options, if (!cf_protection_option || cf_protection_option->value == CF_CHECK) { - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - else if ((*decoded_options)[j].value != foption->value) + if (existing_opt == -1) + decoded_options.safe_push (*foption); + else if (decoded_options[existing_opt].value != foption->value) { if (cf_protection_option && cf_protection_option->value == CF_CHECK) @@ -327,22 +340,23 @@ merge_and_complain (struct cl_decoded_option **decoded_options, "option %qs with mismatching values" " (%s, %s)", "-fcf-protection", - (*decoded_options)[j].arg, foption->arg); + decoded_options[existing_opt].arg, + foption->arg); else { /* Merge and update the -fcf-protection option. */ - (*decoded_options)[j].value &= (foption->value - & CF_FULL); - switch ((*decoded_options)[j].value) + decoded_options[existing_opt].value + &= (foption->value & CF_FULL); + switch (decoded_options[existing_opt].value) { case CF_NONE: - (*decoded_options)[j].arg = "none"; + decoded_options[existing_opt].arg = "none"; break; case CF_BRANCH: - (*decoded_options)[j].arg = "branch"; + decoded_options[existing_opt].arg = "branch"; break; case CF_RETURN: - (*decoded_options)[j].arg = "return"; + decoded_options[existing_opt].arg = "return"; break; default: gcc_unreachable (); @@ -356,15 +370,19 @@ merge_and_complain (struct cl_decoded_option **decoded_options, case OPT_Ofast: case OPT_Og: case OPT_Os: - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == OPT_O - || (*decoded_options)[j].opt_index == OPT_Ofast - || (*decoded_options)[j].opt_index == OPT_Og - || (*decoded_options)[j].opt_index == OPT_Os) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - else if ((*decoded_options)[j].opt_index == foption->opt_index + existing_opt = -1; + for (j = 0; j < decoded_options.length (); ++j) + if (decoded_options[j].opt_index == OPT_O + || decoded_options[j].opt_index == OPT_Ofast + || decoded_options[j].opt_index == OPT_Og + || decoded_options[j].opt_index == OPT_Os) + { + existing_opt = j; + break; + } + if (existing_opt == -1) + decoded_options.safe_push (*foption); + else if (decoded_options[existing_opt].opt_index == foption->opt_index && foption->opt_index != OPT_O) /* Exact same options get merged. */ ; @@ -394,13 +412,14 @@ merge_and_complain (struct cl_decoded_option **decoded_options, default: gcc_unreachable (); } - switch ((*decoded_options)[j].opt_index) + switch (decoded_options[existing_opt].opt_index) { case OPT_O: - if ((*decoded_options)[j].arg[0] == '\0') + if (decoded_options[existing_opt].arg[0] == '\0') level = MAX (level, 1); else - level = MAX (level, atoi ((*decoded_options)[j].arg)); + level = MAX (level, + atoi (decoded_options[existing_opt].arg)); break; case OPT_Ofast: level = MAX (level, 3); @@ -414,23 +433,20 @@ merge_and_complain (struct cl_decoded_option **decoded_options, default: gcc_unreachable (); } - (*decoded_options)[j].opt_index = OPT_O; + decoded_options[existing_opt].opt_index = OPT_O; char *tem; tem = xasprintf ("-O%d", level); - (*decoded_options)[j].arg = &tem[2]; - (*decoded_options)[j].canonical_option[0] = tem; - (*decoded_options)[j].value = 1; + decoded_options[existing_opt].arg = &tem[2]; + decoded_options[existing_opt].canonical_option[0] = tem; + decoded_options[existing_opt].value = 1; } break; case OPT_foffload_abi_: - for (j = 0; j < *decoded_options_count; ++j) - if ((*decoded_options)[j].opt_index == foption->opt_index) - break; - if (j == *decoded_options_count) - append_option (decoded_options, decoded_options_count, foption); - else if (foption->value != (*decoded_options)[j].value) + if (existing_opt == -1) + decoded_options.safe_push (*foption); + else if (foption->value != decoded_options[existing_opt].value) fatal_error (input_location, "option %s not used consistently in all LTO input" " files", foption->orig_option_with_args_text); @@ -438,7 +454,11 @@ merge_and_complain (struct cl_decoded_option **decoded_options, case OPT_foffload_: - append_option (decoded_options, decoded_options_count, foption); + decoded_options.safe_push (*foption); + break; + + case OPT_flto_: + merge_flto_options (decoded_options, foption); break; } } @@ -458,12 +478,12 @@ merge_and_complain (struct cl_decoded_option **decoded_options, It would be good to warn on mismatches, but it is bit hard to do as we do not know what nothing translates to. */ - for (unsigned int j = 0; j < *decoded_options_count;) - if ((*decoded_options)[j].opt_index == OPT_fPIC - || (*decoded_options)[j].opt_index == OPT_fpic) + for (unsigned int j = 0; j < decoded_options.length ();) + if (decoded_options[j].opt_index == OPT_fPIC + || decoded_options[j].opt_index == OPT_fpic) { /* -fno-pic in one unit implies -fno-pic everywhere. */ - if ((*decoded_options)[j].value == 0) + if (decoded_options[j].value == 0) j++; /* If we have no pic option or merge in -fno-pic, we still may turn existing pic/PIC mode into pie/PIE if -fpie/-fPIE is present. */ @@ -472,41 +492,41 @@ merge_and_complain (struct cl_decoded_option **decoded_options, { if (pie_option) { - bool big = (*decoded_options)[j].opt_index == OPT_fPIC + bool big = decoded_options[j].opt_index == OPT_fPIC && pie_option->opt_index == OPT_fPIE; - (*decoded_options)[j].opt_index = big ? OPT_fPIE : OPT_fpie; + decoded_options[j].opt_index = big ? OPT_fPIE : OPT_fpie; if (pie_option->value) - (*decoded_options)[j].canonical_option[0] + decoded_options[j].canonical_option[0] = big ? "-fPIE" : "-fpie"; else - (*decoded_options)[j].canonical_option[0] = "-fno-pie"; - (*decoded_options)[j].value = pie_option->value; - j++; + decoded_options[j].canonical_option[0] = "-fno-pie"; + decoded_options[j].value = pie_option->value; + j++; } else if (pic_option) { - (*decoded_options)[j] = *pic_option; - j++; + decoded_options[j] = *pic_option; + j++; } /* We do not know if target defaults to pic or not, so just remove option if it is missing in one unit but enabled in other. */ else - remove_option (decoded_options, j, decoded_options_count); + decoded_options.ordered_remove (j); } else if (pic_option->opt_index == OPT_fpic - && (*decoded_options)[j].opt_index == OPT_fPIC) + && decoded_options[j].opt_index == OPT_fPIC) { - (*decoded_options)[j] = *pic_option; + decoded_options[j] = *pic_option; j++; } else j++; } - else if ((*decoded_options)[j].opt_index == OPT_fPIE - || (*decoded_options)[j].opt_index == OPT_fpie) + else if (decoded_options[j].opt_index == OPT_fPIE + || decoded_options[j].opt_index == OPT_fpie) { /* -fno-pie in one unit implies -fno-pie everywhere. */ - if ((*decoded_options)[j].value == 0) + if (decoded_options[j].value == 0) j++; /* If we have no pie option or merge in -fno-pie, we still preserve PIE/pie if pic/PIC is present. */ @@ -517,32 +537,32 @@ merge_and_complain (struct cl_decoded_option **decoded_options, if (pic_option) { if (pic_option->opt_index == OPT_fpic - && (*decoded_options)[j].opt_index == OPT_fPIE) + && decoded_options[j].opt_index == OPT_fPIE) { - (*decoded_options)[j].opt_index = OPT_fpie; - (*decoded_options)[j].canonical_option[0] + decoded_options[j].opt_index = OPT_fpie; + decoded_options[j].canonical_option[0] = pic_option->value ? "-fpie" : "-fno-pie"; } else if (!pic_option->value) - (*decoded_options)[j].canonical_option[0] = "-fno-pie"; - (*decoded_options)[j].value = pic_option->value; + decoded_options[j].canonical_option[0] = "-fno-pie"; + decoded_options[j].value = pic_option->value; j++; } else if (pie_option) { - (*decoded_options)[j] = *pie_option; + decoded_options[j] = *pie_option; j++; } /* Because we always append pic/PIE options this code path should not happen unless the LTO object was built by old lto1 which did not contain that logic yet. */ else - remove_option (decoded_options, j, decoded_options_count); + decoded_options.ordered_remove (j); } else if (pie_option->opt_index == OPT_fpie - && (*decoded_options)[j].opt_index == OPT_fPIE) + && decoded_options[j].opt_index == OPT_fPIE) { - (*decoded_options)[j] = *pie_option; + decoded_options[j] = *pie_option; j++; } else @@ -554,37 +574,41 @@ merge_and_complain (struct cl_decoded_option **decoded_options, if (!xassembler_options_error) for (i = j = 0; ; i++, j++) { - for (; i < *decoded_options_count; i++) - if ((*decoded_options)[i].opt_index == OPT_Xassembler) - break; - - for (; j < fdecoded_options_count; j++) - if (fdecoded_options[j].opt_index == OPT_Xassembler) - break; - - if (i == *decoded_options_count && j == fdecoded_options_count) + int existing_opt_index + = find_option (decoded_options, OPT_Xassembler); + int existing_opt2_index + = find_option (fdecoded_options, OPT_Xassembler); + + cl_decoded_option *existing_opt = NULL; + cl_decoded_option *existing_opt2 = NULL; + if (existing_opt_index != -1) + existing_opt = &decoded_options[existing_opt_index]; + if (existing_opt2_index != -1) + existing_opt2 = &fdecoded_options[existing_opt2_index]; + + if (existing_opt == NULL && existing_opt2 == NULL) break; - else if (i < *decoded_options_count && j == fdecoded_options_count) + else if (existing_opt != NULL && existing_opt2 == NULL) { warning (0, "Extra option to %<-Xassembler%>: %s," " dropping all %<-Xassembler%> and %<-Wa%> options.", - (*decoded_options)[i].arg); + existing_opt->arg); xassembler_options_error = true; break; } - else if (i == *decoded_options_count && j < fdecoded_options_count) + else if (existing_opt == NULL && existing_opt2 != NULL) { warning (0, "Extra option to %<-Xassembler%>: %s," " dropping all %<-Xassembler%> and %<-Wa%> options.", - fdecoded_options[j].arg); + existing_opt2->arg); xassembler_options_error = true; break; } - else if (strcmp ((*decoded_options)[i].arg, fdecoded_options[j].arg)) + else if (strcmp (existing_opt->arg, existing_opt2->arg) != 0) { warning (0, "Options to %<-Xassembler%> do not match: %s, %s," " dropping all %<-Xassembler%> and %<-Wa%> options.", - (*decoded_options)[i].arg, fdecoded_options[j].arg); + existing_opt->arg, existing_opt2->arg); xassembler_options_error = true; break; } @@ -655,13 +679,12 @@ parse_env_var (const char *str, char ***pvalues, const char *append) /* Append options OPTS from lto or offload_lto sections to ARGV_OBSTACK. */ static void -append_compiler_options (obstack *argv_obstack, struct cl_decoded_option *opts, - unsigned int count) +append_compiler_options (obstack *argv_obstack, vec<cl_decoded_option> opts) { /* Append compiler driver arguments as far as they were merged. */ - for (unsigned int j = 1; j < count; ++j) + for (unsigned int j = 1; j < opts.length (); ++j) { - struct cl_decoded_option *option = &opts[j]; + cl_decoded_option *option = &opts[j]; /* File options have been properly filtered by lto-opts.c. */ switch (option->opt_index) @@ -722,16 +745,15 @@ append_compiler_options (obstack *argv_obstack, struct cl_decoded_option *opts, } } -/* Append diag options in OPTS with length COUNT to ARGV_OBSTACK. */ +/* Append diag options in OPTS to ARGV_OBSTACK. */ static void -append_diag_options (obstack *argv_obstack, struct cl_decoded_option *opts, - unsigned int count) +append_diag_options (obstack *argv_obstack, vec<cl_decoded_option> opts) { /* Append compiler driver arguments as far as they were merged. */ - for (unsigned int j = 1; j < count; ++j) + for (unsigned int j = 1; j < opts.length (); ++j) { - struct cl_decoded_option *option = &opts[j]; + cl_decoded_option *option = &opts[j]; switch (option->opt_index) { @@ -758,14 +780,13 @@ append_diag_options (obstack *argv_obstack, struct cl_decoded_option *opts, /* Append linker options OPTS to ARGV_OBSTACK. */ static void -append_linker_options (obstack *argv_obstack, struct cl_decoded_option *opts, - unsigned int count) +append_linker_options (obstack *argv_obstack, vec<cl_decoded_option> opts) { /* Append linker driver arguments. Compiler options from the linker driver arguments will override / merge with those from the compiler. */ - for (unsigned int j = 1; j < count; ++j) + for (unsigned int j = 1; j < opts.length (); ++j) { - struct cl_decoded_option *option = &opts[j]; + cl_decoded_option *option = &opts[j]; /* Do not pass on frontend specific flags not suitable for lto. */ if (!(cl_options[option->opt_index].flags @@ -803,15 +824,14 @@ append_linker_options (obstack *argv_obstack, struct cl_decoded_option *opts, static void append_offload_options (obstack *argv_obstack, const char *target, - struct cl_decoded_option *options, - unsigned int options_count) + vec<cl_decoded_option> options) { - for (unsigned i = 0; i < options_count; i++) + for (unsigned i = 0; i < options.length (); i++) { const char *cur, *next, *opts; char **argv; unsigned argc; - struct cl_decoded_option *option = &options[i]; + cl_decoded_option *option = &options[i]; if (option->opt_index != OPT_foffload_) continue; @@ -883,10 +903,8 @@ access_check (const char *name, int mode) static char * compile_offload_image (const char *target, const char *compiler_path, unsigned in_argc, char *in_argv[], - struct cl_decoded_option *compiler_opts, - unsigned int compiler_opt_count, - struct cl_decoded_option *linker_opts, - unsigned int linker_opt_count) + vec<cl_decoded_option> compiler_opts, + vec<cl_decoded_option> linker_opts) { char *filename = NULL; char *dumpbase; @@ -943,19 +961,16 @@ compile_offload_image (const char *target, const char *compiler_path, obstack_ptr_grow (&argv_obstack, in_argv[i]); /* Append options from offload_lto sections. */ - append_compiler_options (&argv_obstack, compiler_opts, - compiler_opt_count); - append_diag_options (&argv_obstack, linker_opts, linker_opt_count); + append_compiler_options (&argv_obstack, compiler_opts); + append_diag_options (&argv_obstack, linker_opts); obstack_ptr_grow (&argv_obstack, "-dumpbase"); obstack_ptr_grow (&argv_obstack, dumpbase); /* Append options specified by -foffload last. In case of conflicting options we expect offload compiler to choose the latest. */ - append_offload_options (&argv_obstack, target, compiler_opts, - compiler_opt_count); - append_offload_options (&argv_obstack, target, linker_opts, - linker_opt_count); + append_offload_options (&argv_obstack, target, compiler_opts); + append_offload_options (&argv_obstack, target, linker_opts); obstack_ptr_grow (&argv_obstack, NULL); argv = XOBFINISH (&argv_obstack, char **); @@ -974,10 +989,8 @@ compile_offload_image (const char *target, const char *compiler_path, static void compile_images_for_offload_targets (unsigned in_argc, char *in_argv[], - struct cl_decoded_option *compiler_opts, - unsigned int compiler_opt_count, - struct cl_decoded_option *linker_opts, - unsigned int linker_opt_count) + vec<cl_decoded_option> compiler_opts, + vec<cl_decoded_option> linker_opts) { char **names = NULL; const char *target_names = getenv (OFFLOAD_TARGET_NAMES_ENV); @@ -997,8 +1010,7 @@ compile_images_for_offload_targets (unsigned in_argc, char *in_argv[], { offload_names[next_name_entry] = compile_offload_image (names[i], compiler_path, in_argc, in_argv, - compiler_opts, compiler_opt_count, - linker_opts, linker_opt_count); + compiler_opts, linker_opts); if (!offload_names[next_name_entry]) #if OFFLOAD_DEFAULTED continue; @@ -1080,25 +1092,22 @@ find_crtoffloadtable (int save_temps, const char *dumppfx) } /* A subroutine of run_gcc. Examine the open file FD for lto sections with - name prefix PREFIX, at FILE_OFFSET, and store any options we find in OPTS - and OPT_COUNT. Return true if we found a matching section, false + name prefix PREFIX, at FILE_OFFSET, and store any options we find in OPTS. + Return true if we found a matching section, false otherwise. COLLECT_GCC holds the value of the environment variable with the same name. */ static bool find_and_merge_options (int fd, off_t file_offset, const char *prefix, - struct cl_decoded_option *decoded_cl_options, - unsigned int decoded_cl_options_count, - struct cl_decoded_option **opts, - unsigned int *opt_count, const char *collect_gcc) + vec<cl_decoded_option> decoded_cl_options, + vec<cl_decoded_option> *opts, const char *collect_gcc) { off_t offset, length; char *data; char *fopts; const char *errmsg; int err; - struct cl_decoded_option *fdecoded_options = *opts; - unsigned int fdecoded_options_count = *opt_count; + vec<cl_decoded_option> fdecoded_options; simple_object_read *sobj; sobj = simple_object_start_read (fd, file_offset, "__GNU_LTO", @@ -1120,24 +1129,19 @@ find_and_merge_options (int fd, off_t file_offset, const char *prefix, data = (char *)xmalloc (length); read (fd, data, length); fopts = data; + bool first = true; do { - struct cl_decoded_option *f2decoded_options; - unsigned int f2decoded_options_count; - get_options_from_collect_gcc_options (collect_gcc, fopts, - &f2decoded_options, - &f2decoded_options_count); - if (!fdecoded_options) - { - fdecoded_options = f2decoded_options; - fdecoded_options_count = f2decoded_options_count; - } + vec<cl_decoded_option> f2decoded_options + = get_options_from_collect_gcc_options (collect_gcc, fopts); + if (first) + { + fdecoded_options = f2decoded_options; + first = false; + } else - merge_and_complain (&fdecoded_options, - &fdecoded_options_count, - f2decoded_options, f2decoded_options_count, - decoded_cl_options, - decoded_cl_options_count); + merge_and_complain (fdecoded_options, f2decoded_options, + decoded_cl_options); fopts += strlen (fopts) + 1; } @@ -1146,7 +1150,6 @@ find_and_merge_options (int fd, off_t file_offset, const char *prefix, free (data); simple_object_release_read (sobj); *opts = fdecoded_options; - *opt_count = fdecoded_options_count; return true; } @@ -1355,6 +1358,23 @@ jobserver_active_p (void) return JS_PREFIX "cannot access %<" JS_NEEDLE "%> file descriptors"; } +/* Print link to -flto documentation with a hint message. */ + +void +print_lto_docs_link () +{ + const char *url = get_option_url (NULL, OPT_flto); + + pretty_printer pp; + pp.url_format = URL_FORMAT_DEFAULT; + pp_string (&pp, "see the "); + pp_begin_url (&pp, url); + pp_string (&pp, "%<-flto%> option documentation"); + pp_end_url (&pp); + pp_string (&pp, " for more information"); + inform (UNKNOWN_LOCATION, pp_formatted_text (&pp)); +} + /* Test that a make command is present and working, return true if so. */ static bool @@ -1389,14 +1409,13 @@ run_gcc (unsigned argc, char *argv[]) char *collect_gcc_options; int parallel = 0; int jobserver = 0; + bool jobserver_requested = false; int auto_parallel = 0; bool no_partition = false; - struct cl_decoded_option *fdecoded_options = NULL; - struct cl_decoded_option *offload_fdecoded_options = NULL; - unsigned int fdecoded_options_count = 0; - unsigned int offload_fdecoded_options_count = 0; - struct cl_decoded_option *decoded_options; - unsigned int decoded_options_count; + const char *jobserver_error = NULL; + vec<cl_decoded_option> fdecoded_options; + fdecoded_options.create (16); + vec<cl_decoded_option> offload_fdecoded_options = vNULL; struct obstack argv_obstack; int new_head_argc; bool have_lto = false; @@ -1438,9 +1457,8 @@ run_gcc (unsigned argc, char *argv[]) NULL); } - get_options_from_collect_gcc_options (collect_gcc, collect_gcc_options, - &decoded_options, - &decoded_options_count); + vec<cl_decoded_option> decoded_options + = get_options_from_collect_gcc_options (collect_gcc, collect_gcc_options); /* Allocate array for input object files with LTO IL, and for possible preceding arguments. */ @@ -1489,8 +1507,7 @@ run_gcc (unsigned argc, char *argv[]) } if (find_and_merge_options (fd, file_offset, LTO_SECTION_NAME_PREFIX, - decoded_options, decoded_options_count, - &fdecoded_options, &fdecoded_options_count, + decoded_options, &fdecoded_options, collect_gcc)) { have_lto = true; @@ -1505,14 +1522,13 @@ run_gcc (unsigned argc, char *argv[]) obstack_ptr_grow (&argv_obstack, "-xlto"); obstack_ptr_grow (&argv_obstack, "-c"); - append_compiler_options (&argv_obstack, fdecoded_options, - fdecoded_options_count); - append_linker_options (&argv_obstack, decoded_options, decoded_options_count); + append_compiler_options (&argv_obstack, fdecoded_options); + append_linker_options (&argv_obstack, decoded_options); /* Scan linker driver arguments for things that are of relevance to us. */ - for (j = 1; j < decoded_options_count; ++j) + for (j = 1; j < decoded_options.length (); ++j) { - struct cl_decoded_option *option = &decoded_options[j]; + cl_decoded_option *option = &decoded_options[j]; switch (option->opt_index) { case OPT_o: @@ -1537,6 +1553,37 @@ run_gcc (unsigned argc, char *argv[]) break; case OPT_flto_: + /* Merge linker -flto= option with what we have in IL files. */ + merge_flto_options (fdecoded_options, option); + if (strcmp (option->arg, "jobserver") == 0) + jobserver_requested = true; + break; + + case OPT_flinker_output_: + linker_output_rel = !strcmp (option->arg, "rel"); + break; + + case OPT_g: + /* Recognize -g0. */ + skip_debug = option->arg && !strcmp (option->arg, "0"); + break; + + case OPT_dumpdir: + incoming_dumppfx = dumppfx = option->arg; + break; + + default: + break; + } + } + + /* Process LTO-related options on merged options. */ + for (j = 1; j < fdecoded_options.length (); ++j) + { + cl_decoded_option *option = &fdecoded_options[j]; + switch (option->opt_index) + { + case OPT_flto_: if (strcmp (option->arg, "jobserver") == 0) { parallel = 1; @@ -1558,22 +1605,6 @@ run_gcc (unsigned argc, char *argv[]) case OPT_flto: lto_mode = LTO_MODE_WHOPR; break; - - case OPT_flinker_output_: - linker_output_rel = !strcmp (option->arg, "rel"); - break; - - case OPT_g: - /* Recognize -g0. */ - skip_debug = option->arg && !strcmp (option->arg, "0"); - break; - - case OPT_dumpdir: - incoming_dumppfx = dumppfx = option->arg; - break; - - default: - break; } } @@ -1595,15 +1626,15 @@ run_gcc (unsigned argc, char *argv[]) { lto_mode = LTO_MODE_LTO; jobserver = 0; + jobserver_requested = false; auto_parallel = 0; parallel = 0; } else { - const char *jobserver_error = jobserver_active_p (); + jobserver_error = jobserver_active_p (); if (jobserver && jobserver_error != NULL) { - warning (0, jobserver_error); /* Fall back to auto parallelism. */ jobserver = 0; auto_parallel = 1; @@ -1731,9 +1762,7 @@ cont1: fatal_error (input_location, "cannot open %s: %m", filename); if (!find_and_merge_options (fd, file_offset, OFFLOAD_SECTION_NAME_PREFIX, - decoded_options, decoded_options_count, - &offload_fdecoded_options, - &offload_fdecoded_options_count, + decoded_options, &offload_fdecoded_options, collect_gcc)) fatal_error (input_location, "cannot read %s: %m", filename); close (fd); @@ -1742,10 +1771,7 @@ cont1: } compile_images_for_offload_targets (num_offload_files, offload_argv, - offload_fdecoded_options, - offload_fdecoded_options_count, - decoded_options, - decoded_options_count); + offload_fdecoded_options, decoded_options); free_array_of_ptrs ((void **) offload_argv, num_offload_files); @@ -1923,6 +1949,20 @@ cont: maybe_unlink (ltrans_output_file); ltrans_output_file = NULL; + if (nr > 1) + { + if (jobserver_requested && jobserver_error != NULL) + { + warning (0, jobserver_error); + print_lto_docs_link (); + } + else if (parallel == 0) + { + warning (0, "using serial compilation of %d LTRANS jobs", nr); + print_lto_docs_link (); + } + } + if (parallel) { makefile = make_temp_file (".mk"); diff --git a/gcc/match.pd b/gcc/match.pd index 19f4a78..cdb8763 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -4764,6 +4764,18 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (cmp:c (bit_xor:c @0 @1) @0) (cmp @1 { build_zero_cst (TREE_TYPE (@1)); })) +#if GIMPLE + /* (X & Y) == X becomes (X & ~Y) == 0. */ + (simplify + (cmp:c (bit_and:c @0 @1) @0) + (cmp (bit_and @0 (bit_not! @1)) { build_zero_cst (TREE_TYPE (@0)); })) + + /* (X | Y) == Y becomes (X & ~Y) == 0. */ + (simplify + (cmp:c (bit_ior:c @0 @1) @1) + (cmp (bit_and @0 (bit_not! @1)) { build_zero_cst (TREE_TYPE (@0)); })) +#endif + /* (X ^ C1) op C2 can be rewritten as X op (C1 ^ C2). */ (simplify (cmp (convert?@3 (bit_xor @0 INTEGER_CST@1)) INTEGER_CST@2) diff --git a/gcc/omp-low.c b/gcc/omp-low.c index c0ce1a4..cadca7e 100644 --- a/gcc/omp-low.c +++ b/gcc/omp-low.c @@ -2460,7 +2460,7 @@ finish_taskreg_scan (omp_context *ctx) TYPE_FIELDS (ctx->record_type) = field; if (ctx->srecord_type) { - field = lookup_sfield (OMP_CLAUSE_DECL (detach_clause), ctx); + field = lookup_sfield (OMP_CLAUSE_DECL (c), ctx); p = &TYPE_FIELDS (ctx->srecord_type); while (*p) if (*p == field) @@ -3243,9 +3243,13 @@ get_option_html_page (int option_index) const cl_option *cl_opt = &cl_options[option_index]; /* Analyzer options are on their own page. */ - if (strstr(cl_opt->opt_text, "analyzer-")) + if (strstr (cl_opt->opt_text, "analyzer-")) return "gcc/Static-Analyzer-Options.html"; + /* Handle -flto= option. */ + if (strstr (cl_opt->opt_text, "flto")) + return "gcc/Optimize-Options.html"; + #ifdef CL_Fortran if ((cl_opt->flags & CL_Fortran) != 0 /* If it is option common to both C/C++ and Fortran, it is documented diff --git a/gcc/po/ChangeLog b/gcc/po/ChangeLog index 6c78000..cc54ad4 100644 --- a/gcc/po/ChangeLog +++ b/gcc/po/ChangeLog @@ -1,3 +1,7 @@ +2021-05-11 Joseph Myers <joseph@codesourcery.com> + + * ja.po: Update. + 2021-04-29 Joseph Myers <joseph@codesourcery.com> * sv.po: Update. diff --git a/gcc/po/ja.po b/gcc/po/ja.po index 049450e..4199919 100644 --- a/gcc/po/ja.po +++ b/gcc/po/ja.po @@ -19,10 +19,10 @@ # Hiroshi Takekawa <sian@big.or.jp>, <sian.ht@gmail.com>, 2020, 2021 msgid "" msgstr "" -"Project-Id-Version: gcc 11.1-b20210207\n" +"Project-Id-Version: gcc 11.1.0\n" "Report-Msgid-Bugs-To: https://gcc.gnu.org/bugs/\n" "POT-Creation-Date: 2021-04-20 18:19+0000\n" -"PO-Revision-Date: 2021-02-10 19:45+0900\n" +"PO-Revision-Date: 2021-05-11 22:47+0900\n" "Last-Translator: Hiroshi Takekawa <sian@big.or.jp>\n" "Language-Team: Japanese <translation-team-ja@lists.sourceforge.net>\n" "Language: ja\n" @@ -512,10 +512,6 @@ msgid "" " 'none' means revert to the default behavior of\n" " guessing the language based on the file's extension.\n" msgstr "" -" -x <language> これ以降入力するファイルの言語を指定する。\n" -" 指定可能な言語は: c c++ assembler none\n" -" 'none' が指定されるとデフォルトのふるまいである\n" -" 拡張子から言語を推測する。\n" #: gcc.c:3798 #, c-format @@ -908,14 +904,14 @@ msgid " -p, --preserve-paths Preserve all pathname components\n" msgstr " -p, --preserve-paths すべてのパス名要素を保護する\n" #: gcov.c:944 -#, fuzzy, c-format +#, c-format msgid " -q, --use-hotness-colors Emit perf-like colored output for hot lines\n" -msgstr " -n, --no-output 出力ファイルを作成しない\n" +msgstr " -q, --use-hotness-colors perf のように hot line に色をつける\n" #: gcov.c:945 -#, fuzzy, c-format +#, c-format msgid " -r, --relative-only Only show data for relative sources\n" -msgstr " -a, --all-blocks 各基本ブロックに関する情報を表示する\n" +msgstr " -r, --relative-only 相対ソースのデータのみ表示する\n" #: gcov.c:946 #, c-format @@ -1498,9 +1494,8 @@ msgid "function not considered for inlining" msgstr "inline に出来ると見なされていない関数です" #: cif-code.def:43 -#, fuzzy msgid "caller is not optimized" -msgstr "%qE は初期化されていません" +msgstr "caller は最適化されていません" #: cif-code.def:47 msgid "function body not available" @@ -1596,9 +1591,8 @@ msgid "callee refers to comdat-local symbols" msgstr "" #: cif-code.def:134 -#, fuzzy msgid "sanitizer function attribute mismatch" -msgstr "関数属性が合いません" +msgstr "sanitizer関数属性が合いません" #: cif-code.def:139 msgid "function has external linkage when the user requests only inlining static for live patching" @@ -1993,14 +1987,12 @@ msgid "invalid address mode" msgstr "無効なアドレスモードです" #: config/aarch64/aarch64.c:25211 config/arm/arm.c:33660 -#, fuzzy msgid "invalid conversion from type %<bfloat16_t%>" -msgstr "%<__fpreg%> からの無効な変換です" +msgstr "%<bfloat16_t%> からの無効な変換です" #: config/aarch64/aarch64.c:25213 config/arm/arm.c:33662 -#, fuzzy msgid "invalid conversion to type %<bfloat16_t%>" -msgstr "無効なバージョン番号形式" +msgstr "%<bfloat16_t%> への無効な変換です" #: config/aarch64/aarch64.c:25228 config/aarch64/aarch64.c:25244 #: config/arm/arm.c:33677 config/arm/arm.c:33693 @@ -2008,9 +2000,8 @@ msgid "operation not permitted on type %<bfloat16_t%>" msgstr "" #: config/aarch64/aarch64.c:25252 -#, fuzzy msgid "cannot combine GNU and SVE vectors in a binary operation" -msgstr "二進演算内で無効な被演算子です" +msgstr "GNU と SVE ベクタの二項演算はできません" #: config/alpha/alpha.c:5076 config/i386/i386.c:13227 #: config/rs6000/rs6000.c:14147 config/sparc/sparc.c:9323 @@ -2233,9 +2224,8 @@ msgid "operands to %T/%t must be reg + const_int:" msgstr "出力被演算子 %d は %<&%> 制約を使用しなければいけません" #: config/avr/avr.c:2863 config/avr/avr.c:2930 -#, fuzzy msgid "bad address, not an I/O address:" -msgstr "誤ったアドレスです。(reg+disp) ではありません:" +msgstr "誤ったアドレスです。I/O アドレスではありません:" #: config/avr/avr.c:2872 msgid "bad address, not a constant:" @@ -2304,9 +2294,8 @@ msgid "structure field" msgstr "構造体フィールド" #: config/avr/avr.c:10039 -#, fuzzy msgid "return type of function" -msgstr "`%s' の宣言は関数の配列" +msgstr "関数の戻り値の型" #: config/avr/avr.c:10044 msgid "pointer" @@ -2567,9 +2556,9 @@ msgid "operand %%xn code invalid for QImode" msgstr "被演算子は条件コードではありません。無効な被演算子コード 'Y' です" #: config/gcn/gcn.c:6171 -#, fuzzy, c-format +#, c-format msgid "invalid fp constant" -msgstr "無効な命令:" +msgstr "無効な浮動小数点定数です" #: config/h8300/h8300.c:1565 config/h8300/h8300.c:1573 #: config/h8300/h8300.c:1581 config/h8300/h8300.c:1589 @@ -2584,9 +2573,9 @@ msgid "invalid UNSPEC as operand" msgstr "被演算子として無効な UNSPEC です" #: config/i386/i386.c:12591 -#, fuzzy, c-format +#, c-format msgid "invalid use of register '%s'" -msgstr "%<restrict%> の誤った使用法です" +msgstr "'%s' の誤った用法です" #: config/i386/i386.c:12596 #, fuzzy, c-format @@ -2673,9 +2662,9 @@ msgid "unknown insn mode" msgstr "不明な命令モード" #: config/i386/djgpp.h:143 -#, fuzzy, c-format +#, c-format msgid "-f%s ignored (not supported for DJGPP)\n" -msgstr "Unicos/Mk 用としては -f%s は無視されました (サポートされていません)" +msgstr "-f%s は (DJGPP ではサポートされていないので) 無視されました\n" #: config/ia64/ia64.c:5459 #, c-format @@ -2874,14 +2863,14 @@ msgid "invalid zero extract" msgstr "%<restrict%> の誤った使用法です" #: config/or1k/or1k.c:1116 config/or1k/or1k.c:1124 -#, fuzzy, c-format +#, c-format msgid "invalid relocation" -msgstr "無効な %%d 被演算子です" +msgstr "無効な再配置です" #: config/or1k/or1k.c:1218 -#, fuzzy, c-format +#, c-format msgid "invalid %%H value" -msgstr "無効な %%J 値" +msgstr "無効な %%H 値" #: config/or1k/or1k.c:1231 config/xtensa/xtensa.c:2473 #, c-format @@ -2919,14 +2908,14 @@ msgid "unsupported operand %s for code '%c'" msgstr "コード '%c' に対する無効な被演算子です" #: config/pru/pru.c:1870 -#, fuzzy, c-format +#, c-format msgid "unexpected text address:" -msgstr "アドレス内の予期しない副作用" +msgstr "予期しないテキストアドレス:" #: config/pru/pru.c:1889 -#, fuzzy, c-format +#, c-format msgid "unsupported constant address:" -msgstr "サポートされていない組み合わせです: %s" +msgstr "サポートされていない定数アドレス:" #: config/pru/pru.c:1950 #, fuzzy, c-format @@ -2991,7 +2980,7 @@ msgid "Bad 128-bit move" msgstr "誤った 128 ビット move" #: config/rs6000/rs6000.c:13589 -#, fuzzy, c-format +#, c-format msgid "invalid %%A value" msgstr "無効な %%A 値" @@ -3001,7 +2990,7 @@ msgid "invalid %%D value" msgstr "無効な %%D 値です" #: config/rs6000/rs6000.c:13613 -#, fuzzy, c-format +#, c-format msgid "invalid %%e value" msgstr "無効な %%e 値" @@ -3051,7 +3040,7 @@ msgid "invalid %%q value" msgstr "無効な %%q 値です" #: config/rs6000/rs6000.c:13858 -#, fuzzy, c-format +#, c-format msgid "invalid %%t value" msgstr "無効な %%t 値" @@ -3081,9 +3070,9 @@ msgid "invalid %%x value" msgstr "無効な %%x 値です" #: config/rs6000/rs6000.c:14025 -#, fuzzy, c-format +#, c-format msgid "invalid %%z value" -msgstr "無効な %%J 値" +msgstr "無効な %%z 値" #: config/rs6000/rs6000.c:14094 #, c-format @@ -3114,14 +3103,12 @@ msgid "Bad GPR fusion" msgstr "" #: config/rs6000/rs6000.c:27776 -#, fuzzy msgid "invalid conversion from type %<__vector_quad%>" -msgstr "%<__fpreg%> からの無効な変換です" +msgstr "%<__vector_quad%> からの無効な変換です" #: config/rs6000/rs6000.c:27778 -#, fuzzy msgid "invalid conversion to type %<__vector_quad%>" -msgstr "無効なバージョン番号形式" +msgstr "%<__vector_quad%> への無効な変換です" #: config/rs6000/rs6000.c:27780 #, fuzzy @@ -58665,8 +58652,9 @@ msgstr "クラス名が予期されます" #: d/dmd/dsymbolsem.c:1372 #, fuzzy, gcc-internal-format +#| msgid "unexpected text address:" msgid "function name expected for start address" -msgstr "アドレス内の予期しない副作用" +msgstr "予期しないテキストアドレス:" #: d/dmd/dsymbolsem.c:1387 #, gcc-internal-format, gfc-internal-format diff --git a/gcc/regcprop.c b/gcc/regcprop.c index 02753a1..7c271e2 100644 --- a/gcc/regcprop.c +++ b/gcc/regcprop.c @@ -358,34 +358,26 @@ copy_value (rtx dest, rtx src, struct value_data *vd) else if (sn > hard_regno_nregs (sr, vd->e[sr].mode)) return; - /* It is not safe to link DEST into the chain if SRC was defined in some - narrower mode M and if M is also narrower than the mode of the first - register in the chain. For example: - (set (reg:DI r1) (reg:DI r0)) - (set (reg:HI r2) (reg:HI r1)) - (set (reg:SI r3) (reg:SI r2)) //Should be a new chain start at r3 - (set (reg:SI r4) (reg:SI r1)) - (set (reg:SI r5) (reg:SI r4)) - - the upper part of r3 is undefined. If we added it to the chain, - it may be used to replace r5, which has defined upper bits. - See PR98694 for details. - - [A] partial_subreg_p (vd->e[sr].mode, GET_MODE (src)) - [B] partial_subreg_p (vd->e[sr].mode, vd->e[vd->e[sr].oldest_regno].mode) - Condition B is added to to catch optimization opportunities of - - (set (reg:HI R1) (reg:HI R0)) - (set (reg:SI R2) (reg:SI R1)) // [A] - (set (reg:DI R3) (reg:DI R2)) // [A] - (set (reg:SI R4) (reg:SI R[0-3])) - (set (reg:HI R5) (reg:HI R[0-4])) - - in which all registers have only 16 defined bits. */ - else if (partial_subreg_p (vd->e[sr].mode, GET_MODE (src)) - && partial_subreg_p (vd->e[sr].mode, - vd->e[vd->e[sr].oldest_regno].mode)) - return; + /* If a narrower value is copied using wider mode, the upper bits + are undefined (could be e.g. a former paradoxical subreg). Signal + in that case we've only copied value using the narrower mode. + Consider: + (set (reg:DI r14) (mem:DI ...)) + (set (reg:QI si) (reg:QI r14)) + (set (reg:DI bp) (reg:DI r14)) + (set (reg:DI r14) (const_int ...)) + (set (reg:DI dx) (reg:DI si)) + (set (reg:DI si) (const_int ...)) + (set (reg:DI dx) (reg:DI bp)) + The last set is not redundant, while the low 8 bits of dx are already + equal to low 8 bits of bp, the other bits are undefined. */ + else if (partial_subreg_p (vd->e[sr].mode, GET_MODE (src))) + { + if (!REG_CAN_CHANGE_MODE_P (sr, GET_MODE (src), vd->e[sr].mode) + || !REG_CAN_CHANGE_MODE_P (dr, vd->e[sr].mode, GET_MODE (dest))) + return; + set_value_regno (dr, vd->e[sr].mode, vd); + } /* Link DR at the end of the value chain used by SR. */ diff --git a/gcc/reorg.c b/gcc/reorg.c index 4595f9a..7f06a6f 100644 --- a/gcc/reorg.c +++ b/gcc/reorg.c @@ -2375,6 +2375,16 @@ fill_slots_from_thread (rtx_jump_insn *insn, rtx condition, if (! insn_references_resource_p (trial, &set, true) && ! insn_sets_resource_p (trial, filter_flags ? &fset : &set, true) && ! insn_sets_resource_p (trial, &needed, true) + /* If we're handling sets to the flags register specially, we + only allow an insn into a delay-slot, if it either: + - doesn't set the flags register, + - the "set" of the flags register isn't used (clobbered), + - insns between the delay-slot insn and the trial-insn + as accounted in "set", have not affected the flags register. */ + && (! filter_flags + || ! insn_sets_resource_p (trial, &flags_res, true) + || find_regno_note (trial, REG_UNUSED, targetm.flags_regnum) + || ! TEST_HARD_REG_BIT (set.regs, targetm.flags_regnum)) && ! can_throw_internal (trial)) { rtx_insn *prior_insn; @@ -158,10 +158,13 @@ static size_t rtvec_alloc_sizes; Store the length, and initialize all elements to zero. */ rtvec -rtvec_alloc (int n) +rtvec_alloc (size_t n) { rtvec rt; + /* rtvec_def.num_elem is an int. */ + gcc_assert (n < INT_MAX); + rt = ggc_alloc_rtvec_sized (n); /* Clear out the vector. */ memset (&rt->elem[0], 0, n * sizeof (rtx)); @@ -2976,7 +2976,7 @@ extern rtx rtx_alloc_stat_v (RTX_CODE MEM_STAT_DECL, int); (sizeof (struct hwivec_def) \ + ((NWORDS)-1) * sizeof (HOST_WIDE_INT))) \ -extern rtvec rtvec_alloc (int); +extern rtvec rtvec_alloc (size_t); extern rtvec shallow_copy_rtvec (rtvec); extern bool shared_const_p (const_rtx); extern rtx copy_rtx (rtx); diff --git a/gcc/targhooks.c b/gcc/targhooks.c index 2e0fdb7..1947ef2 100644 --- a/gcc/targhooks.c +++ b/gcc/targhooks.c @@ -1001,6 +1001,13 @@ default_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs) { gcc_assert (!hard_reg_set_empty_p (need_zeroed_hardregs)); + HARD_REG_SET failed; + CLEAR_HARD_REG_SET (failed); + bool progress = false; + + /* First, try to zero each register in need_zeroed_hardregs by + loading a zero into it, taking note of any failures in + FAILED. */ for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (TEST_HARD_REG_BIT (need_zeroed_hardregs, regno)) { @@ -1010,16 +1017,88 @@ default_zero_call_used_regs (HARD_REG_SET need_zeroed_hardregs) rtx_insn *insn = emit_move_insn (regno_reg_rtx[regno], zero); if (!valid_insn_p (insn)) { - static bool issued_error; - if (!issued_error) - { - issued_error = true; - sorry ("%qs not supported on this target", - "-fzero-call-used-regs"); - } + SET_HARD_REG_BIT (failed, regno); delete_insns_since (last_insn); } + else + progress = true; } + + /* Now retry with copies from zeroed registers, as long as we've + made some PROGRESS, and registers remain to be zeroed in + FAILED. */ + while (progress && !hard_reg_set_empty_p (failed)) + { + HARD_REG_SET retrying = failed; + + CLEAR_HARD_REG_SET (failed); + progress = false; + + for (unsigned int regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) + if (TEST_HARD_REG_BIT (retrying, regno)) + { + machine_mode mode = GET_MODE (regno_reg_rtx[regno]); + bool success = false; + /* Look for a source. */ + for (unsigned int src = 0; src < FIRST_PSEUDO_REGISTER; src++) + { + /* If SRC hasn't been zeroed (yet?), skip it. */ + if (! TEST_HARD_REG_BIT (need_zeroed_hardregs, src)) + continue; + if (TEST_HARD_REG_BIT (retrying, src)) + continue; + + /* Check that SRC can hold MODE, and that any other + registers needed to hold MODE in SRC have also been + zeroed. */ + if (!targetm.hard_regno_mode_ok (src, mode)) + continue; + unsigned n = targetm.hard_regno_nregs (src, mode); + bool ok = true; + for (unsigned i = 1; ok && i < n; i++) + ok = (TEST_HARD_REG_BIT (need_zeroed_hardregs, src + i) + && !TEST_HARD_REG_BIT (retrying, src + i)); + if (!ok) + continue; + + /* SRC is usable, try to copy from it. */ + rtx_insn *last_insn = get_last_insn (); + rtx zsrc = gen_rtx_REG (mode, src); + rtx_insn *insn = emit_move_insn (regno_reg_rtx[regno], zsrc); + if (!valid_insn_p (insn)) + /* It didn't work, remove any inserts. We'll look + for another SRC. */ + delete_insns_since (last_insn); + else + { + /* We're done for REGNO. */ + success = true; + break; + } + } + + /* If nothing worked for REGNO this round, marked it to be + retried if we get another round. */ + if (!success) + SET_HARD_REG_BIT (failed, regno); + else + /* Take note so as to enable another round if needed. */ + progress = true; + } + } + + /* If any register remained, report it. */ + if (!progress) + { + static bool issued_error; + if (!issued_error) + { + issued_error = true; + sorry ("%qs not supported on this target", + "-fzero-call-used-regs"); + } + } + return need_zeroed_hardregs; } diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 75e235b..936f37a 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,322 @@ +2021-05-16 David Edelsohn <dje.gcc@gmail.com> + + * g++.dg/ext/attrib63.C: Add -Wno-psabi option. + +2021-05-16 David Edelsohn <dje.gcc@gmail.com> + + * g++.dg/warn/uninit-pr93100.C: Require fsantize support. + +2021-05-16 Christophe Lyon <christophe.lyon@linaro.org> + + * gcc.target/arm/unsigned-float.c: Remove arm_fp_ok, adjust + dg-options. + +2021-05-15 Bill Schmidt <wschmidt@linux.ibm.com> + + * gcc.target/powerpc/rop-1.c: New. + * gcc.target/powerpc/rop-2.c: New. + * gcc.target/powerpc/rop-3.c: New. + * gcc.target/powerpc/rop-4.c: New. + * gcc.target/powerpc/rop-5.c: New. + +2021-05-15 Martin Jambor <mjambor@suse.cz> + + Revert: + 2021-05-15 Martin Jambor <mjambor@suse.cz> + + PR tree-optimization/100453 + * gcc.dg/tree-ssa/pr100453.c: New test. + +2021-05-15 Jakub Jelinek <jakub@redhat.com> + + PR rtl-optimization/100342 + * gcc.target/i386/pr100342.c: New test. + +2021-05-14 Tobias Burnus <tobias@codesourcery.com> + + * gfortran.dg/gomp/parallel-master-1.f90: New test. + * gfortran.dg/gomp/parallel-master-2.f90: New test. + +2021-05-14 Tobias Burnus <tobias@codesourcery.com> + + * gfortran.dg/gomp/implicit-save.f90: New test. + +2021-05-14 Jakub Jelinek <jakub@redhat.com> + + PR c++/95226 + * g++.dg/cpp1y/pr95226.C: New test. + +2021-05-14 Jakub Jelinek <jakub@redhat.com> + + PR c++/94616 + * g++.dg/cpp0x/pr94616.C: New test. + +2021-05-14 Jakub Jelinek <jakub@redhat.com> + + PR c++/90019 + * g++.dg/cpp0x/sfinae68.C: New test. + +2021-05-14 Jakub Jelinek <jakub@redhat.com> + + * g++.dg/cpp1y/pr88872.C: New test. + +2021-05-14 Michael de Lang <kingoipo@gmail.com> + + * g++.dg/tsan/pthread_cond_clockwait.C: New test. + +2021-05-14 Marek Polacek <polacek@redhat.com> + Jason Merrill <jason@redhat.com> + + PR c++/99032 + * g++.dg/cpp0x/friend7.C: New test. + * g++.dg/cpp0x/gen-attrs-4.C: Add dg-error. + * g++.dg/cpp0x/gen-attrs-39-1.C: Likewise. + * g++.dg/cpp0x/gen-attrs-74.C: New test. + * g++.dg/ext/attrib63.C: New test. + +2021-05-13 Martin Sebor <msebor@redhat.com> + + PR tree-optimization/93100 + PR middle-end/98583 + * g++.dg/warn/uninit-pr93100.C: New test. + * gcc.dg/uninit-pr93100.c: New test. + * gcc.dg/uninit-pr98583.c: New test. + +2021-05-13 Martin Jambor <mjambor@suse.cz> + + PR tree-optimization/100453 + * gcc.dg/tree-ssa/pr100453.c: New test. + +2021-05-13 Martin Sebor <msebor@redhat.com> + + * g++.dg/pr100574.C: Use size_t as operator new argument type. + +2021-05-13 Martin Liska <mliska@suse.cz> + + PR middle-end/100504 + * gcc.target/i386/pr100504.c: New test. + +2021-05-13 Martin Sebor <msebor@redhat.com> + + PR c/100550 + * gcc.dg/Wvla-parameter-9.c: New test. + +2021-05-13 Jakub Jelinek <jakub@redhat.com> + + PR middle-end/99928 + * c-c++-common/gomp/pr99928-1.c: New test. + * c-c++-common/gomp/pr99928-2.c: New test. + * c-c++-common/gomp/pr99928-3.c: New test. + * c-c++-common/gomp/pr99928-4.c: New test. + * c-c++-common/gomp/pr99928-5.c: New test. + * c-c++-common/gomp/pr99928-6.c: New test. + * c-c++-common/gomp/pr99928-7.c: New test. + * c-c++-common/gomp/pr99928-8.c: New test. + * c-c++-common/gomp/pr99928-9.c: New test. + * c-c++-common/gomp/pr99928-10.c: New test. + * c-c++-common/gomp/pr99928-11.c: New test. + +2021-05-13 Richard Earnshaw <rearnsha@arm.com> + + PR target/100563 + * gcc.dg/pr100563.c (dg-options): Add -wno-pointer-to-int-cast. + +2021-05-13 Richard Earnshaw <rearnsha@arm.com> + + * gcc.dg/pr100563.c: New test. + +2021-05-13 Jakub Jelinek <jakub@redhat.com> + + PR tree-optimization/98856 + * gcc.target/i386/sse2-psraq-1.c: New test. + * gcc.target/i386/sse4_2-psraq-1.c: New test. + * gcc.target/i386/avx-psraq-1.c: New test. + * gcc.target/i386/avx2-psraq-1.c: New test. + * gcc.target/i386/avx-pr82370.c: Adjust expected number of vpsrad + instructions. + * gcc.target/i386/avx2-pr82370.c: Likewise. + * gcc.target/i386/avx512f-pr82370.c: Likewise. + * gcc.target/i386/avx512bw-pr82370.c: Likewise. + * gcc.dg/torture/vshuf-4.inc: Add two further permutations. + * gcc.dg/torture/vshuf-8.inc: Likewise. + +2021-05-13 Uroš Bizjak <ubizjak@gmail.com> + + PR target/100581 + * g++.target/i386/pr100581.C: New test. + +2021-05-13 Eric Botcazou <ebotcazou@adacore.com> + + PR testsuite/100569 + * gnat.dg/lto21.adb: Prune new LTO warning. + +2021-05-13 Martin Liska <mliska@suse.cz> + + PR testsuite/100569 + * gcc.dg/atomic/c11-atomic-exec-2.c: Prune new LTO warning. + * gcc.dg/torture/pr94947-1.c: Likewise. + +2021-05-13 liuhongt <hongtao.liu@intel.com> + + * gcc.target/i386/avx-pr94680.c: Fix typo in testcase. + +2021-05-13 liuhongt <hongtao.liu@intel.com> + + PR target/94680 + * gcc.target/i386/avx-pr94680.c: New test. + * gcc.target/i386/avx512f-pr94680.c: New test. + * gcc.target/i386/sse2-pr94680.c: New test. + +2021-05-12 Martin Sebor <msebor@redhat.com> + + PR middle-end/100571 + * gcc.dg/Wstringop-overflow-67.c: New test. + +2021-05-12 Aldy Hernandez <aldyh@redhat.com> + + PR c/100521 + * gcc.dg/pr100521.c: New file. + +2021-05-12 Marek Polacek <polacek@redhat.com> + + * g++.dg/warn/Wint-in-bool-context-2.C: New test. + +2021-05-12 Marcel Vollweiler <marcel@codesourcery.com> + + * c-c++-common/gomp/map-6.c: New test. + * c-c++-common/gomp/map-7.c: New test. + +2021-05-12 Richard Biener <rguenther@suse.de> + + PR tree-optimization/100566 + * gcc.dg/torture/pr100566.c: New testcase. + +2021-05-12 Jakub Jelinek <jakub@redhat.com> + + PR preprocessor/100392 + * gcc.dg/cpp/pr100392.c: New test. + * gcc.dg/cpp/pr100392.h: New file. + +2021-05-12 Martin Liska <mliska@suse.cz> + + * lib/lto.exp: When running tests without jobserver, one can see + the following warning for tests that use 1to1 partitioning. + +2021-05-12 liuhongt <hongtao.liu@intel.com> + + PR target/99908 + * gcc.target/i386/avx2-pr99908.c: New test. + * gcc.target/i386/sse4_1-pr99908.c: New test. + +2021-05-12 Richard Biener <rguenther@suse.de> + + PR tree-optimization/100519 + * gcc.dg/torture/pr100519.c: New testcase. + +2021-05-12 Bernd Edlinger <bernd.edlinger@hotmail.de> + + PR debug/100515 + * gcc.dg/debug/dwarf2/pr100515.c: New testcase. + +2021-05-12 Jakub Jelinek <jakub@redhat.com> + + PR middle-end/100508 + * gcc.dg/gomp/pr100508.c: New test. + +2021-05-12 Jakub Jelinek <jakub@redhat.com> + Marc Glisse <marc.glisse@inria.fr> + + PR tree-optimization/94589 + * gcc.dg/tree-ssa/pr94589-1.c: New test. + +2021-05-12 Uroš Bizjak <ubizjak@gmail.com> + + PR target/98218 + * g++.target/i386/pr98218-1.C: Ditto. + * gcc.target/i386/pr98218-4.c: New test. + * gcc.target/i386/pr98218-1.c: Correct PR number. + * gcc.target/i386/pr98218-1a.c: Ditto. + * gcc.target/i386/pr98218-2.c: Ditto. + * gcc.target/i386/pr98218-2a.c: Ditto. + * gcc.target/i386/pr98218-3.c: Ditto. + * gcc.target/i386/pr98218-3a.c: Ditto. + +2021-05-11 Joseph Myers <joseph@codesourcery.com> + + * gcc.dg/cpp/c11-elifdef-1.c, gcc.dg/cpp/c2x-elifdef-1.c, + gcc.dg/cpp/c2x-elifdef-2.c: New tests. + +2021-05-11 Joseph Myers <joseph@codesourcery.com> + + * g++.dg/cpp1y/digit-sep-paste.C, gcc.dg/c2x-digit-separators-3.c: + New tests. + +2021-05-11 Stefan Schulze Frielinghaus <stefansf@linux.ibm.com> + + * gcc.dg/guality/pr43077-1.c: Align types of output and input + operands by lifting immediates to type long. + +2021-05-11 Joseph Myers <joseph@codesourcery.com> + + * gcc.dg/c11-digit-separators-1.c, + gcc.dg/c2x-digit-separators-1.c, gcc.dg/c2x-digit-separators-2.c: + New tests. + +2021-05-11 Jason Merrill <jason@redhat.com> + + PR c++/100517 + * g++.dg/ext/vector41.C: New test. + +2021-05-11 Martin Liska <mliska@suse.cz> + + * gfortran.dg/goacc/pr78027.f90: Remove -Wno-hsa option. + * brig.dg/README: Removed. + * brig.dg/dg.exp: Removed. + * brig.dg/test/gimple/alloca.hsail: Removed. + * brig.dg/test/gimple/atomics.hsail: Removed. + * brig.dg/test/gimple/branches.hsail: Removed. + * brig.dg/test/gimple/fbarrier.hsail: Removed. + * brig.dg/test/gimple/function_calls.hsail: Removed. + * brig.dg/test/gimple/internal-casts.hsail: Removed. + * brig.dg/test/gimple/kernarg.hsail: Removed. + * brig.dg/test/gimple/mem.hsail: Removed. + * brig.dg/test/gimple/mulhi.hsail: Removed. + * brig.dg/test/gimple/packed.hsail: Removed. + * brig.dg/test/gimple/priv-array-offset-access.hsail: Removed. + * brig.dg/test/gimple/smoke_test.hsail: Removed. + * brig.dg/test/gimple/variables.hsail: Removed. + * brig.dg/test/gimple/vector.hsail: Removed. + * lib/brig-dg.exp: Removed. + * lib/brig.exp: Removed. + +2021-05-11 Alex Coplan <alex.coplan@arm.com> + + PR target/99725 + * gcc.target/arm/cmse/pr99725.c: New test. + +2021-05-11 Richard Sandiford <richard.sandiford@arm.com> + + * gcc.target/aarch64/fmul_lane_1.c: New test. + +2021-05-11 Richard Biener <rguenther@suse.de> + + PR middle-end/100509 + * gcc.dg/pr100509.c: New testcase. + +2021-05-11 Robin Dapp <rdapp@linux.ibm.com> + + * gcc.target/s390/risbg-ll-3.c: Change match pattern. + +2021-05-11 Patrick Palka <ppalka@redhat.com> + + PR c++/51577 + * g++.dg/lookup/operator-3.C: New test. + +2021-05-11 Patrick Palka <ppalka@redhat.com> + + PR c++/100138 + * g++.dg/cpp2a/concepts-ctad4.C: New test. + 2021-05-10 Eric Botcazou <ebotcazou@adacore.com> * gnat.dg/specs/opt5.ads: New test. diff --git a/gcc/testsuite/c-c++-common/gomp/map-6.c b/gcc/testsuite/c-c++-common/gomp/map-6.c new file mode 100644 index 0000000..6ee5971 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/map-6.c @@ -0,0 +1,135 @@ +/* { dg-do compile } */ +/* { dg-additional-options "-fdump-tree-original" } */ + +void +foo (void) +{ + /* Test to ensure that the close modifier is parsed and ignored in map clauses. */ + int a, b, b1, b2, b3, b4, b5, b6, b7; + + #pragma omp target map (a) + ; + + #pragma omp target map (to:a) + ; + + #pragma omp target map (a to: b) /* { dg-error "'#pragma omp target' with modifier other than 'always' or 'close'" } */ + ; + + #pragma omp target map (close, a to: b) /* { dg-error "'#pragma omp target' with modifier other than 'always' or 'close'" } */ + ; + + #pragma omp target map (close a) /* { dg-error "'close' undeclared" "" { target c } } */ + /* { dg-error "'close' has not been declared" "" { target c++ } .-1 } */ + /* { dg-error "expected '\\)' before 'a'" "" { target *-*-* } .-2 } */ + ; + + #pragma omp target map (always a) /* { dg-error "'always' undeclared" "" { target c } } */ + /* { dg-error "'always' has not been declared" "" { target c++ } .-1 } */ + /* { dg-error "expected '\\)' before 'a'" "" { target *-*-* } .-2 } */ + ; + + #pragma omp target map (close to:a) + ; + + #pragma omp target map (close, to:a) + ; + + #pragma omp target map (close delete:a) /* { dg-error "'#pragma omp target' with map-type other than 'to', 'from', 'tofrom' or 'alloc' on 'map' clause" } */ + ; + + #pragma omp target map (close always to:b1) + ; + + #pragma omp target map (close, always to:b2) + ; + + #pragma omp target map (close, always, to:b3) + ; + + #pragma omp target map (always close to:b4) + ; + + #pragma omp target map (always, close to:b5) + ; + + #pragma omp target map (always, close, to:b6) + ; + + #pragma omp target map (always, always, to:a) /* { dg-error "too many 'always' modifiers" } */ + ; + + #pragma omp target map (always always, to:a) /* { dg-error "too many 'always' modifiers" } */ + ; + + #pragma omp target map (always, always to:a) /* { dg-error "too many 'always' modifiers" } */ + ; + + #pragma omp target map (always always to:a) /* { dg-error "too many 'always' modifiers" } */ + ; + + #pragma omp target map (close, close, to:a) /* { dg-error "too many 'close' modifiers" } */ + ; + + #pragma omp target map (close close, to:a) /* { dg-error "too many 'close' modifiers" } */ + ; + + #pragma omp target map (close, close to:a) /* { dg-error "too many 'close' modifiers" } */ + ; + + #pragma omp target map (close close to:a) /* { dg-error "too many 'close' modifiers" } */ + ; + + #pragma omp target map (always to : a) map (close to : b) + ; + + int close = 0; + #pragma omp target map (close) + ; + + #pragma omp target map (close a) /* { dg-error "expected '\\)' before 'a'" } */ + ; + + int always = 0; + #pragma omp target map (always) + ; + + #pragma omp target map (always a) /* { dg-error "expected '\\)' before 'a'" } */ + ; + + #pragma omp target map (always, close) + ; + + #pragma omp target map (always, always) /* { dg-error "'always' appears more than once in map clauses" } */ + ; + + #pragma omp target map (always, always, close) /* { dg-error "'always' appears more than once in map clauses" } */ + ; + + #pragma omp target map (always, close, to: always, close, b7) + ; + + int to = 0; + #pragma omp target map (always, close, to) + ; + + #pragma omp target map (to, always, close) + { + to = always = close = 1; + } + if (to != 1 || always != 1 || close != 1) + __builtin_abort (); + ; +} + +/* { dg-final { scan-tree-dump-not "map\\(\[^\n\r)]*close\[^\n\r)]*to:" "original" } } */ + +/* { dg-final { scan-tree-dump-times "pragma omp target map\\(always,to:" 7 "original" } } */ + +/* { dg-final { scan-tree-dump "pragma omp target map\\(always,to:b1" "original" } } */ +/* { dg-final { scan-tree-dump "pragma omp target map\\(always,to:b2" "original" } } */ +/* { dg-final { scan-tree-dump "pragma omp target map\\(always,to:b3" "original" } } */ +/* { dg-final { scan-tree-dump "pragma omp target map\\(always,to:b4" "original" } } */ +/* { dg-final { scan-tree-dump "pragma omp target map\\(always,to:b5" "original" } } */ +/* { dg-final { scan-tree-dump "pragma omp target map\\(always,to:b6" "original" } } */ +/* { dg-final { scan-tree-dump "pragma omp target map\\(always,to:b7\\) map\\(always,to:close\\) map\\(always,to:always\\)" "original" } } */ diff --git a/gcc/testsuite/c-c++-common/gomp/map-7.c b/gcc/testsuite/c-c++-common/gomp/map-7.c new file mode 100644 index 0000000..3f1e972 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/map-7.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ + +void +foo (void) +{ + /* Test to ensure that the close modifier is parsed and ignored in map clauses. */ + + #define N 1024 + int always[N]; + int close; + + #pragma omp target map(always[:N]) + ; + + #pragma omp target map(close, always[:N]) + ; + + #pragma omp target map(always[:N], close) + ; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-1.c b/gcc/testsuite/c-c++-common/gomp/pr99928-1.c new file mode 100644 index 0000000..37181fd --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-1.c @@ -0,0 +1,206 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int f00, f01, f02, f03, f04, f05, f06, f07, f08, f09; +int f12, f13, f14, f15, f16, f17, f18, f19; +int f20, f21, f22, f23, f24, f25, f26, f27, f28, f29; + +void +foo (void) +{ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*firstprivate\\(f00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f00\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f00\\)" "gimple" } } *//* FIXME. */ + #pragma omp distribute parallel for firstprivate (f00) + for (int i = 0; i < 64; i++) + f00++; + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*firstprivate\\(f01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f01\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f01\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f01\\)" "gimple" } } */ + #pragma omp distribute parallel for simd firstprivate (f01) + for (int i = 0; i < 64; i++) + f01++; + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*firstprivate\\(f02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f02\\)" "gimple" } } */ + #pragma omp distribute simd firstprivate (f02) + for (int i = 0; i < 64; i++) + f02++; +} + +void +bar (void) +{ + int f10 = 0, f11 = 0; + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*firstprivate\\(f03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f03\\)" "gimple" } } */ + #pragma omp for simd firstprivate (f03) + for (int i = 0; i < 64; i++) + f03++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(f04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(f04\\)" "gimple" } } */ + #pragma omp master taskloop firstprivate (f04) + for (int i = 0; i < 64; i++) + f04++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(f05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(f05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f05\\)" "gimple" } } */ + #pragma omp master taskloop simd firstprivate (f05) + for (int i = 0; i < 64; i++) + f05++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f06\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f06\\)" "gimple" } } *//* FIXME. */ + #pragma omp parallel for firstprivate (f06) + for (int i = 0; i < 64; i++) + f06++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f07\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f07\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f07\\)" "gimple" } } */ + #pragma omp parallel for simd firstprivate (f07) + for (int i = 0; i < 64; i++) + f07++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f08\\)" "gimple" } } */ + #pragma omp parallel loop firstprivate (f08) + for (int i = 0; i < 64; i++) + f08++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(f09\\)" "gimple" } } */ + #pragma omp parallel master firstprivate (f09) + f09++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(f10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(f10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(f10\\)" "gimple" } } */ + #pragma omp parallel master taskloop firstprivate (f10) + for (int i = 0; i < 64; i++) + f10++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(f11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(f11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(f11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f11\\)" "gimple" } } */ + #pragma omp parallel master taskloop simd firstprivate (f11) + for (int i = 0; i < 64; i++) + f11++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f12\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp sections\[^\n\r]*firstprivate\\(f12\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp section \[^\n\r]*firstprivate\\(f12\\)" "gimple" } } */ + #pragma omp parallel sections firstprivate (f12) + { + f12++; + #pragma omp section + f12++; + } + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f13\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f13\\)" "gimple" } } */ + #pragma omp target parallel firstprivate (f13) + f13++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f14\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f14\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f14\\)" "gimple" } } *//* FIXME. */ + #pragma omp target parallel for firstprivate (f14) + for (int i = 0; i < 64; i++) + f14++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f15\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f15\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f15\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f15\\)" "gimple" } } */ + #pragma omp target parallel for simd firstprivate (f15) + for (int i = 0; i < 64; i++) + f15++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f16\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f16\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f16\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f16\\)" "gimple" } } */ + #pragma omp target parallel loop firstprivate (f16) + for (int i = 0; i < 64; i++) + f16++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f17\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f17\\)" "gimple" } } */ + #pragma omp target teams firstprivate (f17) + f17++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f18\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f18\\)" "gimple" } } *//* FIXME: This should be on distribute instead. */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f18\\)" "gimple" } } *//* FIXME. */ + #pragma omp target teams distribute firstprivate (f18) + for (int i = 0; i < 64; i++) + f18++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f19\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f19\\)" "gimple" } } *//* FIXME: This should be on distribute instead. */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f19\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f19\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f19\\)" "gimple" } } *//* FIXME. */ + #pragma omp target teams distribute parallel for firstprivate (f19) + for (int i = 0; i < 64; i++) + f19++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f20\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f20\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f20\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f20\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f20\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f20\\)" "gimple" } } */ + #pragma omp target teams distribute parallel for simd firstprivate (f20) + for (int i = 0; i < 64; i++) + f20++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f21\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f21\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f21\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f21\\)" "gimple" } } */ + #pragma omp target teams distribute simd firstprivate (f21) + for (int i = 0; i < 64; i++) + f21++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(f22\\)" "gimple" } } *//* NOTE: This is an implementation detail. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f22\\)" "gimple" } } */ + #pragma omp target teams loop firstprivate (f22) + for (int i = 0; i < 64; i++) + f22++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*firstprivate\\(f23\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f23\\)" "gimple" } } */ + #pragma omp target simd firstprivate (f23) + for (int i = 0; i < 64; i++) + f23++; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(f24\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f24\\)" "gimple" } } */ + #pragma omp taskloop simd firstprivate (f24) + for (int i = 0; i < 64; i++) + f24++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f25\\)" "gimple" } } *//* FIXME: This should be on distribute instead. */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f25\\)" "gimple" } } *//* FIXME. */ + #pragma omp teams distribute firstprivate (f25) + for (int i = 0; i < 64; i++) + f25++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f26\\)" "gimple" } } *//* FIXME: This should be on distribute instead. */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f26\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f26\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f26\\)" "gimple" } } *//* FIXME. */ + #pragma omp teams distribute parallel for firstprivate (f26) + for (int i = 0; i < 64; i++) + f26++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f27\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f27\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(f27\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f27\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f27\\)" "gimple" } } */ + #pragma omp teams distribute parallel for simd firstprivate (f27) + for (int i = 0; i < 64; i++) + f27++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f28\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f28\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f28\\)" "gimple" } } */ + #pragma omp teams distribute simd firstprivate (f28) + for (int i = 0; i < 64; i++) + f28++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(f29\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*firstprivate\\(f29\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(f29\\)" "gimple" } } *//* NOTE: This is an implementation detail. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(f29\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(f29\\)" "gimple" } } */ + #pragma omp teams loop firstprivate (f29) + for (int i = 0; i < 64; i++) + f29++; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-10.c b/gcc/testsuite/c-c++-common/gomp/pr99928-10.c new file mode 100644 index 0000000..4acff45 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-10.c @@ -0,0 +1,231 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int *r00, *r01, *r02, *r03, *r04, *r05; +int *r13, *r14, *r15, *r16, *r17, *r18, *r19; +int *r20, *r21, *r22, *r23, *r24; +int *baz (void); + +void +foo (void) +{ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r00 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r00 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r00 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp distribute parallel for reduction(+:r00[1:2]) + for (int i = 0; i < 64; i++) + r00[1]++; + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r01 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r01 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r01 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r01 \\+ 4" "gimple" } } */ + #pragma omp distribute parallel for simd reduction(+:r01[1:3]) + for (int i = 0; i < 64; i++) + r01[1]++; + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r02 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r02 \\+ 4" "gimple" } } */ + #pragma omp distribute simd reduction(+:r02[1:4]) + for (int i = 0; i < 64; i++) + r02[1]++; +} + +void +bar (void) +{ + int *r06 = baz (), *r07 = baz (), *r08 = baz (), *r09 = baz (); + int *r10 = baz (), *r11 = baz (), *r12 = baz (); + int *r25 = baz (), *r26 = baz (), *r27 = baz (), *r28 = baz (), *r29 = baz (); + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r03 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r03 \\+ 4" "gimple" } } */ + #pragma omp for simd reduction(+:r03[1:5]) + for (int i = 0; i < 64; i++) + r03[1]++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r04 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r04 \\+ 4" "gimple" } } */ + #pragma omp master taskloop reduction(+:r04[1:6]) + for (int i = 0; i < 64; i++) + r04[1]++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r05 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r05 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r05 \\+ 4" "gimple" } } */ + #pragma omp master taskloop simd reduction(+:r05[1:7]) + for (int i = 0; i < 64; i++) + r05[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r06 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r06 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp parallel for reduction(+:r06[1:8]) + for (int i = 0; i < 64; i++) + r06[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r07 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r07 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r07 \\+ 4" "gimple" } } */ + #pragma omp parallel for simd reduction(+:r07[1:9]) + for (int i = 0; i < 64; i++) + r07[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(r08\\)" "gimple" } } *//* FIXME: Should be shared, but firstprivate is an optimization. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r08 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r08 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp parallel loop reduction(+:r08[1:10]) + for (int i = 0; i < 64; i++) + r08[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r09 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r09 \\+ 4" "gimple" } } */ + #pragma omp parallel master reduction(+:r09[1:11]) + r09[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(r10\\)" "gimple" } } *//* FIXME: Should be shared, but firstprivate is an optimization. */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r10 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r10 \\+ 4" "gimple" } } */ + #pragma omp parallel master taskloop reduction(+:r10[1:12]) + for (int i = 0; i < 64; i++) + r10[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(r11\\)" "gimple" } } *//* FIXME: Should be shared, but firstprivate is an optimization. */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r11 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r11 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r11 \\+ 4" "gimple" } } */ + #pragma omp parallel master taskloop simd reduction(+:r11[1:13]) + for (int i = 0; i < 64; i++) + r11[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r12 \\+ 4" "gimple" } } *//* FIXME: This should be on sections instead. */ + /* { dg-final { scan-tree-dump-not "omp sections\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r12 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp section \[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r12 \\+ 4" "gimple" } } */ + #pragma omp parallel sections reduction(+:r12[1:14]) + { + r12[1]++; + #pragma omp section + r12[1]++; + } + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 60\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r13 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r13\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r13 \\+ 4" "gimple" } } */ + #pragma omp target parallel reduction(+:r13[1:15]) + r13[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 64\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r14 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r14" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r14 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r14 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp target parallel for reduction(+:r14[1:16]) + for (int i = 0; i < 64; i++) + r14[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 68\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r15 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r15\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r15 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r15 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r15 \\+ 4" "gimple" } } */ + #pragma omp target parallel for simd reduction(+:r15[1:17]) + for (int i = 0; i < 64; i++) + r15[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 72\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r16 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r16\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(r16\\)" "gimple" } } *//* FIXME: Should be shared, but firstprivate is an optimization. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r16 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r16 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp target parallel loop reduction(+:r16[1:18]) + for (int i = 0; i < 64; i++) + r16[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 76\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r17 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r17\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r17 \\+ 4" "gimple" } } */ + #pragma omp target teams reduction(+:r17[1:19]) + r17[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 80\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r18 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r18\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r18 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r18 \\+ 4" "gimple" } } */ + #pragma omp target teams distribute reduction(+:r18[1:20]) + for (int i = 0; i < 64; i++) + r18[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 84\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r19 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r19\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r19 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r19 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r19 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r19 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp target teams distribute parallel for reduction(+:r19[1:21]) + for (int i = 0; i < 64; i++) + r19[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 88\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r20 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r20\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r20 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r20 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r20 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r20 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r20 \\+ 4" "gimple" } } */ + #pragma omp target teams distribute parallel for simd reduction(+:r20[1:22]) + for (int i = 0; i < 64; i++) + r20[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 92\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r21 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r21\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r21 \\+ 4" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r21 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r21 \\+ 4" "gimple" } } */ + #pragma omp target teams distribute simd reduction(+:r21[1:23]) + for (int i = 0; i < 64; i++) + r21[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 96\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r22 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r22\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(r22\\)" "gimple" } } *//* FIXME: Should be shared, but firstprivate is an optimization. */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r22 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(r22\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r22 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r22 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp target teams loop reduction(+:r22[1:24]) + for (int i = 0; i < 64; i++) + r22[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:\\\*_\[0-9]* \\\[len: 100\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(firstprivate:r23 \\\[pointer assign, bias: 4\\\]\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r23\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r23 \\+ 4" "gimple" } } */ + #pragma omp target simd reduction(+:r23[1:25]) + for (int i = 0; i < 64; i++) + r23[1]++; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r24 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r24 \\+ 4" "gimple" } } */ + #pragma omp taskloop simd reduction(+:r24[1:26]) + for (int i = 0; i < 64; i++) + r24[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r25 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r25 \\+ 4" "gimple" } } */ + #pragma omp teams distribute reduction(+:r25[1:27]) + for (int i = 0; i < 64; i++) + r25[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r26 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r26 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r26 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r26 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp teams distribute parallel for reduction(+:r26[1:28]) + for (int i = 0; i < 64; i++) + r26[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r27 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r27 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r27 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r27 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r27 \\+ 4" "gimple" } } */ + #pragma omp teams distribute parallel for simd reduction(+:r27[1:29]) + for (int i = 0; i < 64; i++) + r27[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r28 \\+ 4" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r28 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r28 \\+ 4" "gimple" } } */ + #pragma omp teams distribute simd reduction(+:r28[1:30]) + for (int i = 0; i < 64; i++) + r28[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*firstprivate\\(r29\\)" "gimple" } } *//* FIXME: Should be shared, but firstprivate is an optimization. */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r29 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(r29\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r29 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*\\)r29 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp teams loop reduction(+:r29[1:31]) + for (int i = 0; i < 64; i++) + r29[1]++; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-11.c b/gcc/testsuite/c-c++-common/gomp/pr99928-11.c new file mode 100644 index 0000000..66c2feb --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-11.c @@ -0,0 +1,28 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int r00, r01, r02; + +void +bar (void) +{ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*in_reduction\\(\\+:r00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*in_reduction\\(\\+:r00\\)" "gimple" } } */ + #pragma omp master taskloop in_reduction(+:r00) + for (int i = 0; i < 64; i++) + r00++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*in_reduction\\(\\+:r01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*in_reduction\\(\\+:r01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*in_reduction\\(\\+:r01\\)" "gimple" } } */ + #pragma omp master taskloop simd in_reduction(+:r01) + for (int i = 0; i < 64; i++) + r01++; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*in_reduction\\(\\+:r02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*in_reduction\\(\\+:r02\\)" "gimple" } } */ + #pragma omp taskloop simd in_reduction(+:r02) + for (int i = 0; i < 64; i++) + r02++; + /* FIXME: We don't support in_reduction clause on target yet, once we do, should + add testcase coverage for all combined/composite constructs with target as leaf construct. */ +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-2.c b/gcc/testsuite/c-c++-common/gomp/pr99928-2.c new file mode 100644 index 0000000..313374c --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-2.c @@ -0,0 +1,208 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int l00, l01, l02, l03, l04, l05, l06, l07; +int l10, l11, l12, l13, l14, l15, l16, l17, l18; + +void +foo (void) +{ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l00\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l00\\)" "gimple" } } *//* FIXME. */ + #pragma omp distribute parallel for lastprivate (l00) + for (int i = 0; i < 64; i++) + l00 = i; + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l01\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l01\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l01\\)" "gimple" } } */ + #pragma omp distribute parallel for simd lastprivate (l01) + for (int i = 0; i < 64; i++) + l01 = i; + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l02\\)" "gimple" } } */ + #pragma omp distribute simd lastprivate (l02) + for (int i = 0; i < 64; i++) + l02 = i; +} + +void +bar (void) +{ + int j00, j01, j02, j03; + int l08 = 0, l09 = 0, l19 = 0, l20 = 0, l21 = 0, l22 = 0; + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(l03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l03\\)" "gimple" } } */ + #pragma omp for simd lastprivate (l03) + for (int i = 0; i < 64; i++) + l03 = i; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(l04\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l04\\)" "gimple" } } */ + #pragma omp master taskloop lastprivate (l04) + for (int i = 0; i < 64; i++) + l04 = i; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(l05\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l05\\)" "gimple" } } */ + #pragma omp master taskloop simd lastprivate (l05) + for (int i = 0; i < 64; i++) + l05 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l06\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l06\\)" "gimple" } } *//* FIXME. */ + #pragma omp parallel for lastprivate (l06) + for (int i = 0; i < 64; i++) + l06 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l07\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l07\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l07\\)" "gimple" } } */ + #pragma omp parallel for simd lastprivate (l07) + for (int i = 0; i < 64; i++) + l07 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(j00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp parallel loop lastprivate (j00) + for (j00 = 0; j00 < 64; j00++) + ; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(l08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(l08\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l08\\)" "gimple" } } */ + #pragma omp parallel master taskloop lastprivate (l08) + for (int i = 0; i < 64; i++) + l08 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(l09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(l09\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l09\\)" "gimple" } } */ + #pragma omp parallel master taskloop simd lastprivate (l09) + for (int i = 0; i < 64; i++) + l09 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l10\\)" "gimple" } } *//* FIXME: This should be on sections instead. */ + /* { dg-final { scan-tree-dump-not "omp sections\[^\n\r]*lastprivate\\(l10\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp section \[^\n\r]*lastprivate\\(l10\\)" "gimple" } } */ + #pragma omp parallel sections lastprivate (l10) + { + l10 = 1; + #pragma omp section + l10 = 2; + } + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l11" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l11\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l11\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l11\\)" "gimple" } } *//* FIXME. */ + #pragma omp target parallel for lastprivate (l11) + for (int i = 0; i < 64; i++) + l11 = i; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l12" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l12\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l12\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l12\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l12\\)" "gimple" } } */ + #pragma omp target parallel for simd lastprivate (l12) + for (int i = 0; i < 64; i++) + l12 = i; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j01" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j01\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(j01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(j01\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(j01\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp target parallel loop lastprivate (j01) + for (j01 = 0; j01 < 64; j01++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l13" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l13\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(l13\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l13\\)" "gimple" } } */ + #pragma omp target teams distribute lastprivate (l13) + for (int i = 0; i < 64; i++) + l13 = i; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l14" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l14\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(l14\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l14\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l14\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l14\\)" "gimple" } } *//* FIXME. */ + #pragma omp target teams distribute parallel for lastprivate (l14) + for (int i = 0; i < 64; i++) + l14 = i; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l15" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l15\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(l15\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l15\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l15\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l15\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l15\\)" "gimple" } } */ + #pragma omp target teams distribute parallel for simd lastprivate (l15) + for (int i = 0; i < 64; i++) + l15 = i; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l16" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l16\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(l16\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l16\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l16\\)" "gimple" } } */ + #pragma omp target teams distribute simd lastprivate (l16) + for (int i = 0; i < 64; i++) + l16 = i; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j02" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j02\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j02\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(j02\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(j02\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(j02\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp target teams loop lastprivate (j02) + for (j02 = 0; j02 < 64; j02++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l17" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l17\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l17\\)" "gimple" } } */ + #pragma omp target simd lastprivate (l17) + for (int i = 0; i < 64; i++) + l17 = i; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(l18\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l18\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l18\\)" "gimple" } } */ + #pragma omp taskloop simd lastprivate (l18) + for (int i = 0; i < 64; i++) + l18 = i; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(l19\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l19\\)" "gimple" } } */ + #pragma omp teams distribute lastprivate (l19) + for (int i = 0; i < 64; i++) + l19 = i; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(l20\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l20\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l20\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l20\\)" "gimple" } } *//* FIXME. */ + #pragma omp teams distribute parallel for lastprivate (l20) + for (int i = 0; i < 64; i++) + l20 = i; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(l21\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l21\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l21\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l21\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l21\\)" "gimple" } } */ + #pragma omp teams distribute parallel for simd lastprivate (l21) + for (int i = 0; i < 64; i++) + l21 = i; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(l22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(l22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l22\\)" "gimple" } } */ + #pragma omp teams distribute simd lastprivate (l22) + for (int i = 0; i < 64; i++) + l22 = i; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(j03\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp teams loop lastprivate (j03) + for (j03 = 0; j03 < 64; j03++) + ; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-3.c b/gcc/testsuite/c-c++-common/gomp/pr99928-3.c new file mode 100644 index 0000000..67f590b --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-3.c @@ -0,0 +1,119 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int l00, l01, l02, l03, l04, l07, l08, l09; +int l10, l11; + +void +bar (void) +{ + int l05 = 0, l06 = 0; + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*firstprivate\\(l00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(l00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(l00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l00\\)" "gimple" } } */ + #pragma omp for simd firstprivate (l00) lastprivate (l00) + for (int i = 0; i < 64; i++) + l00 = i; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(l01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(l01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l01\\)" "gimple" } } */ + #pragma omp master taskloop firstprivate (l01) lastprivate (l01) + for (int i = 0; i < 64; i++) + l01 = i; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(l02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(l02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(l02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l02\\)" "gimple" } } */ + #pragma omp master taskloop simd firstprivate (l02) lastprivate (l02) + for (int i = 0; i < 64; i++) + l02 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(l03\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l03\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(l03\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l03\\)" "gimple" } } *//* FIXME. */ + #pragma omp parallel for firstprivate (l03) lastprivate (l03) + for (int i = 0; i < 64; i++) + l03 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(l04\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l04\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(l04\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l04\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(l04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l04\\)" "gimple" } } */ + #pragma omp parallel for simd firstprivate (l04) lastprivate (l04) + for (int i = 0; i < 64; i++) + l04 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(l05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(l05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(l05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l05\\)" "gimple" } } */ + #pragma omp parallel master taskloop firstprivate (l05) lastprivate (l05) + for (int i = 0; i < 64; i++) + l05 = i; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(l06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(l06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(l06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(l06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l06\\)" "gimple" } } */ + #pragma omp parallel master taskloop simd firstprivate (l06) lastprivate (l06) + for (int i = 0; i < 64; i++) + l06 = i; + /* FIXME: OpenMP 5.0/5.1 broken here, conceptually it should be shared on parallel and + firstprivate+lastprivate on sections, in GCC implementation we put firstprivate+lastprivate + on parallel for historic reasons, but OpenMP 5.0/5.1 mistakenly say firstprivate + should be on parallel and lastprivate on sections. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(l07\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l07\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp sections\[^\n\r]*firstprivate\\(l07\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp sections\[^\n\r]*lastprivate\\(l07\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp section \[^\n\r]*firstprivate\\(l07\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp section \[^\n\r]*lastprivate\\(l07\\)" "gimple" } } */ + #pragma omp parallel sections firstprivate (l07) lastprivate (l07) + { + l07 = 1; + #pragma omp section + l07 = 2; + } + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l08" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l08\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(l08\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l08\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(l08\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l08\\)" "gimple" } } *//* FIXME. */ + #pragma omp target parallel for firstprivate (l08) lastprivate (l08) + for (int i = 0; i < 64; i++) + l08 = i; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l09" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l09\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(l09\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l09\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(l09\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l09\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(l09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l09\\)" "gimple" } } */ + #pragma omp target parallel for simd firstprivate (l09) lastprivate (l09) + for (int i = 0; i < 64; i++) + l09 = i; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l10" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l10\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(l10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l10\\)" "gimple" } } */ + #pragma omp target simd firstprivate (l10) lastprivate (l10) + for (int i = 0; i < 64; i++) + l10 = i; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(l11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp simd\[^\n\r]*firstprivate\\(l11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*lastprivate\\(l11\\)" "gimple" } } */ + #pragma omp taskloop simd firstprivate (l11) lastprivate (l11) + for (int i = 0; i < 64; i++) + l11 = i; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-4.c b/gcc/testsuite/c-c++-common/gomp/pr99928-4.c new file mode 100644 index 0000000..c03afc4 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-4.c @@ -0,0 +1,76 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int l00, l01, l05, l06, l07, l08; + +void +bar (void) +{ + int l02 = 0, l03 = 0, l04 = 0; + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*firstprivate\\(l00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(l00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(l00:1\\)" "gimple" } } */ + #pragma omp for simd linear (l00) + for (int i = 0; i < 64; i++) + l00++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(l01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(l01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(l01:1\\)" "gimple" } } */ + #pragma omp master taskloop simd linear (l01) + for (int i = 0; i < 64; i++) + l01++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(l02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*linear\\(l02:1\\)" "gimple" } } */ + #pragma omp parallel for linear (l02) + for (int i = 0; i < 64; i++) + l02++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(l03\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l03\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(l03\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l03\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(l03:1\\)" "gimple" } } */ + #pragma omp parallel for simd linear (l03) + for (int i = 0; i < 64; i++) + l03++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(l04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*firstprivate\\(l04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(l04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(l04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(l04:1\\)" "gimple" } } */ + #pragma omp parallel master taskloop simd linear (l04) + for (int i = 0; i < 64; i++) + l04++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l05" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l05\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(l05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*linear\\(l05:1\\)" "gimple" } } */ + #pragma omp target parallel for linear (l05) + for (int i = 0; i < 64; i++) + l05++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l06" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l06\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*firstprivate\\(l06\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(l06\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*firstprivate\\(l06\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(l06\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(l06:1\\)" "gimple" } } */ + #pragma omp target parallel for simd linear (l06) + for (int i = 0; i < 64; i++) + l06++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:l07" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(l07\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(l07:1\\)" "gimple" } } */ + #pragma omp target simd linear (l07) + for (int i = 0; i < 64; i++) + l07++; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*firstprivate\\(l08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(l08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(l08:1\\)" "gimple" } } */ + #pragma omp taskloop simd linear (l08) + for (int i = 0; i < 64; i++) + l08++; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-5.c b/gcc/testsuite/c-c++-common/gomp/pr99928-5.c new file mode 100644 index 0000000..6eca9c8 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-5.c @@ -0,0 +1,107 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int j00, j01, j02, j03, j04, j06, j07, j08, j09; +int j10; + +void +foo (void) +{ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j00:1\\)" "gimple" } } */ + #pragma omp distribute parallel for simd linear (j00) + for (j00 = 0; j00 < 64; j00++) + ; + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j01:1\\)" "gimple" } } */ + #pragma omp distribute simd linear (j01) + for (j01 = 0; j01 < 64; j01++) + ; +} + +void +bar (void) +{ + int j05, j11, j12; + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(j02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j02:1\\)" "gimple" } } */ + #pragma omp for simd linear (j02) + for (j02 = 0; j02 < 64; j02++) + ; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(j03\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j03:1\\)" "gimple" } } */ + #pragma omp master taskloop simd linear (j03) + for (j03 = 0; j03 < 64; j03++) + ; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j04\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j04\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j04:1\\)" "gimple" } } */ + #pragma omp parallel for simd linear (j04) + for (j04 = 0; j04 < 64; j04++) + ; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(j05\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j05:1\\)" "gimple" } } */ + #pragma omp parallel master taskloop simd linear (j05) + for (j05 = 0; j05 < 64; j05++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j06" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j06\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j06\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j06\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j06:1\\)" "gimple" } } */ + #pragma omp target parallel for simd linear (j06) + for (j06 = 0; j06 < 64; j06++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j07" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j07\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j07:1\\)" "gimple" } } */ + #pragma omp target simd linear (j07) + for (j07 = 0; j07 < 64; j07++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j08" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j08\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j08:1\\)" "gimple" } } */ + #pragma omp target teams distribute parallel for simd linear (j08) + for (j08 = 0; j08 < 64; j08++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j09" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j09\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j09:1\\)" "gimple" } } */ + #pragma omp target teams distribute simd linear (j09) + for (j09 = 0; j09 < 64; j09++) + ; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(j10\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(j10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j10:1\\)" "gimple" } } */ + #pragma omp taskloop simd linear (j10) + for (j10 = 0; j10 < 64; j10++) + ; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j11:1\\)" "gimple" } } */ + #pragma omp teams distribute parallel for simd linear (j11) + for (j11 = 0; j11 < 64; j11++) + ; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j12\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j12\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j12:1\\)" "gimple" } } */ + #pragma omp teams distribute simd linear (j12) + for (j12 = 0; j12 < 64; j12++) + ; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-6.c b/gcc/testsuite/c-c++-common/gomp/pr99928-6.c new file mode 100644 index 0000000..9934047 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-6.c @@ -0,0 +1,107 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int j00, j01, j02, j03, j04, j06, j07, j08, j09; +int j10; + +void +foo (void) +{ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j00:1\\)" "gimple" } } */ + #pragma omp distribute parallel for simd + for (j00 = 0; j00 < 64; j00++) + ; + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j01:1\\)" "gimple" } } */ + #pragma omp distribute simd + for (j01 = 0; j01 < 64; j01++) + ; +} + +void +bar (void) +{ + int j05, j11, j12; + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*lastprivate\\(j02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j02:1\\)" "gimple" } } */ + #pragma omp for simd + for (j02 = 0; j02 < 64; j02++) + ; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(j03\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j03:1\\)" "gimple" } } */ + #pragma omp master taskloop simd + for (j03 = 0; j03 < 64; j03++) + ; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j04\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j04\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j04:1\\)" "gimple" } } */ + #pragma omp parallel for simd + for (j04 = 0; j04 < 64; j04++) + ; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(j05\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j05:1\\)" "gimple" } } */ + #pragma omp parallel master taskloop simd + for (j05 = 0; j05 < 64; j05++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j06" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j06\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j06\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j06\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j06:1\\)" "gimple" } } */ + #pragma omp target parallel for simd + for (j06 = 0; j06 < 64; j06++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j07" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j07\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j07:1\\)" "gimple" } } */ + #pragma omp target simd + for (j07 = 0; j07 < 64; j07++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j08" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j08\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j08:1\\)" "gimple" } } */ + #pragma omp target teams distribute parallel for simd + for (j08 = 0; j08 < 64; j08++) + ; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:j09" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j09\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j09:1\\)" "gimple" } } */ + #pragma omp target teams distribute simd + for (j09 = 0; j09 < 64; j09++) + ; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*shared\\(j10\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*lastprivate\\(j10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j10:1\\)" "gimple" } } */ + #pragma omp taskloop simd + for (j10 = 0; j10 < 64; j10++) + ; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j11:1\\)" "gimple" } } */ + #pragma omp teams distribute parallel for simd + for (j11 = 0; j11 < 64; j11++) + ; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(j12\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*lastprivate\\(j12\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j12:1\\)" "gimple" } } */ + #pragma omp teams distribute simd + for (j12 = 0; j12 < 64; j12++) + ; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-7.c b/gcc/testsuite/c-c++-common/gomp/pr99928-7.c new file mode 100644 index 0000000..5db1101 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-7.c @@ -0,0 +1,103 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +void +foo (void) +{ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j00:1\\)" "gimple" } } */ + #pragma omp distribute parallel for simd + for (int j00 = 0; j00 < 64; j00++) + ; + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*lastprivate\\(j01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j01:1\\)" "gimple" } } */ + #pragma omp distribute simd + for (int j01 = 0; j01 < 64; j01++) + ; +} + +void +bar (void) +{ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j02:1\\)" "gimple" } } */ + #pragma omp for simd + for (int j02 = 0; j02 < 64; j02++) + ; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp taskloop\[^\n\r]*lastprivate\\(j03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j03:1\\)" "gimple" } } */ + #pragma omp master taskloop simd + for (int j03 = 0; j03 < 64; j03++) + ; + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*lastprivate\\(j04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j04:1\\)" "gimple" } } */ + #pragma omp parallel for simd + for (int j04 = 0; j04 < 64; j04++) + ; + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*shared\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*lastprivate\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp taskloop\[^\n\r]*lastprivate\\(j05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j05:1\\)" "gimple" } } */ + #pragma omp parallel master taskloop simd + for (int j05 = 0; j05 < 64; j05++) + ; + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*map\\(tofrom:j06" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*shared\\(j06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*lastprivate\\(j06\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j06\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j06:1\\)" "gimple" } } */ + #pragma omp target parallel for simd + for (int j06 = 0; j06 < 64; j06++) + ; + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*map\\(tofrom:j07" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j07\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j07:1\\)" "gimple" } } */ + #pragma omp target simd + for (int j07 = 0; j07 < 64; j07++) + ; + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*map\\(tofrom:j08" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp teams\[^\n\r]*shared\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*shared\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j08:1\\)" "gimple" } } */ + #pragma omp target teams distribute parallel for simd + for (int j08 = 0; j08 < 64; j08++) + ; + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*map\\(tofrom:j09" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(j09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp teams\[^\n\r]*shared\\(j09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*lastprivate\\(j09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j09:1\\)" "gimple" } } */ + #pragma omp target teams distribute simd + for (int j09 = 0; j09 < 64; j09++) + ; + /* { dg-final { scan-tree-dump-not "omp taskloop\[^\n\r]*lastprivate\\(j10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j10:1\\)" "gimple" } } */ + #pragma omp taskloop simd + for (int j10 = 0; j10 < 64; j10++) + ; + /* { dg-final { scan-tree-dump-not "omp teams\[^\n\r]*shared\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*shared\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp parallel\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*lastprivate\\(j11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j11:1\\)" "gimple" } } */ + #pragma omp teams distribute parallel for simd + for (int j11 = 0; j11 < 64; j11++) + ; + /* { dg-final { scan-tree-dump-not "omp teams\[^\n\r]*shared\\(j12\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*lastprivate\\(j12\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*linear\\(j12:1\\)" "gimple" } } */ + #pragma omp teams distribute simd + for (int j12 = 0; j12 < 64; j12++) + ; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-8.c b/gcc/testsuite/c-c++-common/gomp/pr99928-8.c new file mode 100644 index 0000000..755da3b --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-8.c @@ -0,0 +1,219 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int r00, r01, r02, r03, r04, r05; +int r13, r14, r15, r16, r17, r18, r19; +int r20, r21, r22, r23, r24; + +void +foo (void) +{ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r00\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r00\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r00\\)" "gimple" } } *//* FIXME. */ + #pragma omp distribute parallel for reduction(+:r00) + for (int i = 0; i < 64; i++) + r00++; + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r01\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r01\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r01\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r01\\)" "gimple" } } */ + #pragma omp distribute parallel for simd reduction(+:r01) + for (int i = 0; i < 64; i++) + r01++; + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r02\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r02\\)" "gimple" } } */ + #pragma omp distribute simd reduction(+:r02) + for (int i = 0; i < 64; i++) + r02++; +} + +void +bar (void) +{ + int r06 = 0, r07 = 0, r08 = 0, r09 = 0; + int r10 = 0, r11 = 0, r12 = 0; + int r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0; + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:r03\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r03\\)" "gimple" } } */ + #pragma omp for simd reduction(+:r03) + for (int i = 0; i < 64; i++) + r03++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:r04\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:r04\\)" "gimple" } } */ + #pragma omp master taskloop reduction(+:r04) + for (int i = 0; i < 64; i++) + r04++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:r05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:r05\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r05\\)" "gimple" } } */ + #pragma omp master taskloop simd reduction(+:r05) + for (int i = 0; i < 64; i++) + r05++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r06\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r06\\)" "gimple" } } *//* FIXME. */ + #pragma omp parallel for reduction(+:r06) + for (int i = 0; i < 64; i++) + r06++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r07\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r07\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r07\\)" "gimple" } } */ + #pragma omp parallel for simd reduction(+:r07) + for (int i = 0; i < 64; i++) + r07++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:r08\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r08\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp parallel loop reduction(+:r08) + for (int i = 0; i < 64; i++) + r08++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r09\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:r09\\)" "gimple" } } */ + #pragma omp parallel master reduction(+:r09) + r09++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:r10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:r10\\)" "gimple" } } */ + #pragma omp parallel master taskloop reduction(+:r10) + for (int i = 0; i < 64; i++) + r10++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:r11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:r11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r11\\)" "gimple" } } */ + #pragma omp parallel master taskloop simd reduction(+:r11) + for (int i = 0; i < 64; i++) + r11++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r12\\)" "gimple" } } *//* FIXME: This should be on sections instead. */ + /* { dg-final { scan-tree-dump-not "omp sections\[^\n\r]*reduction\\(\\+:r12\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp section \[^\n\r]*reduction\\(\\+:r12\\)" "gimple" } } */ + #pragma omp parallel sections reduction(+:r12) + { + r12++; + #pragma omp section + r12++; + } + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r13" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r13\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r13\\)" "gimple" } } */ + #pragma omp target parallel reduction(+:r13) + r13++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r14" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r14\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r14\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r14\\)" "gimple" } } *//* FIXME. */ + #pragma omp target parallel for reduction(+:r14) + for (int i = 0; i < 64; i++) + r14++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r15" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r15\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r15\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r15\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r15\\)" "gimple" } } */ + #pragma omp target parallel for simd reduction(+:r15) + for (int i = 0; i < 64; i++) + r15++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r16" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r16\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r16\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:r16\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r16\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp target parallel loop reduction(+:r16) + for (int i = 0; i < 64; i++) + r16++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r17" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r17\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r17\\)" "gimple" } } */ + #pragma omp target teams reduction(+:r17) + r17++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r18" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r18\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r18\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r18\\)" "gimple" } } */ + #pragma omp target teams distribute reduction(+:r18) + for (int i = 0; i < 64; i++) + r18++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r19" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r19\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r19\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r19\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r19\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r19\\)" "gimple" } } *//* FIXME. */ + #pragma omp target teams distribute parallel for reduction(+:r19) + for (int i = 0; i < 64; i++) + r19++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r20" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r20\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r20\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r20\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r20\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r20\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r20\\)" "gimple" } } */ + #pragma omp target teams distribute parallel for simd reduction(+:r20) + for (int i = 0; i < 64; i++) + r20++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r21" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r21\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r21\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r21\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r21\\)" "gimple" } } */ + #pragma omp target teams distribute simd reduction(+:r21) + for (int i = 0; i < 64; i++) + r21++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r22" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r22\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(r22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*reduction\\(\\+:r22\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r22\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:r22\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r22\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp target teams loop reduction(+:r22) + for (int i = 0; i < 64; i++) + r22++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r23" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r23\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r23\\)" "gimple" } } */ + #pragma omp target simd reduction(+:r23) + for (int i = 0; i < 64; i++) + r23++; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:r24\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r24\\)" "gimple" } } */ + #pragma omp taskloop simd reduction(+:r24) + for (int i = 0; i < 64; i++) + r24++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r25\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r25\\)" "gimple" } } */ + #pragma omp teams distribute reduction(+:r25) + for (int i = 0; i < 64; i++) + r25++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r26\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r26\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r26\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r26\\)" "gimple" } } *//* FIXME. */ + #pragma omp teams distribute parallel for reduction(+:r26) + for (int i = 0; i < 64; i++) + r26++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r27\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r27\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:r27\\)" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:r27\\)" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r27\\)" "gimple" } } */ + #pragma omp teams distribute parallel for simd reduction(+:r27) + for (int i = 0; i < 64; i++) + r27++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:r28\\)" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:r28\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r28\\)" "gimple" } } */ + #pragma omp teams distribute simd reduction(+:r28) + for (int i = 0; i < 64; i++) + r28++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(r29\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*reduction\\(\\+:r29\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r29\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:r29\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:r29\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp teams loop reduction(+:r29) + for (int i = 0; i < 64; i++) + r29++; +} diff --git a/gcc/testsuite/c-c++-common/gomp/pr99928-9.c b/gcc/testsuite/c-c++-common/gomp/pr99928-9.c new file mode 100644 index 0000000..a766b72 --- /dev/null +++ b/gcc/testsuite/c-c++-common/gomp/pr99928-9.c @@ -0,0 +1,219 @@ +/* PR middle-end/99928 */ +/* { dg-do compile } */ +/* { dg-options "-fopenmp -fdump-tree-gimple" } */ + +int r00[4], r01[4], r02[4], r03[4], r04[4], r05[4]; +int r13[4], r14[4], r15[4], r16[4], r17[4], r18[4], r19[4]; +int r20[4], r21[4], r22[4], r23[4], r24[4]; + +void +foo (void) +{ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r00 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r00 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r00 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp distribute parallel for reduction(+:r00[1:2]) + for (int i = 0; i < 64; i++) + r00[1]++; + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r01 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r01 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r01 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r01 \\+ 4" "gimple" } } */ + #pragma omp distribute parallel for simd reduction(+:r01[1:2]) + for (int i = 0; i < 64; i++) + r01[1]++; + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r02 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r02 \\+ 4" "gimple" } } */ + #pragma omp distribute simd reduction(+:r02[1:2]) + for (int i = 0; i < 64; i++) + r02[1]++; +} + +void +bar (void) +{ + int r06[4] = {}, r07[4] = {}, r08[4] = {}, r09[4] = {}; + int r10[4] = {}, r11[4] = {}, r12[4] = {}; + int r25[4] = {}, r26[4] = {}, r27[4] = {}, r28[4] = {}, r29[4] = {}; + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r03 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r03 \\+ 4" "gimple" } } */ + #pragma omp for simd reduction(+:r03[1:2]) + for (int i = 0; i < 64; i++) + r03[1]++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r04 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r04 \\+ 4" "gimple" } } */ + #pragma omp master taskloop reduction(+:r04[1:2]) + for (int i = 0; i < 64; i++) + r04[1]++; + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r05 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r05 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r05 \\+ 4" "gimple" } } */ + #pragma omp master taskloop simd reduction(+:r05[1:2]) + for (int i = 0; i < 64; i++) + r05[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r06 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r06 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp parallel for reduction(+:r06[1:2]) + for (int i = 0; i < 64; i++) + r06[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r07 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r07 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r07 \\+ 4" "gimple" } } */ + #pragma omp parallel for simd reduction(+:r07[1:2]) + for (int i = 0; i < 64; i++) + r07[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r08\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r08 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r08 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp parallel loop reduction(+:r08[1:2]) + for (int i = 0; i < 64; i++) + r08[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r09 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r09 \\+ 4" "gimple" } } */ + #pragma omp parallel master reduction(+:r09[1:2]) + r09[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r10\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r10 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r10 \\+ 4" "gimple" } } */ + #pragma omp parallel master taskloop reduction(+:r10[1:2]) + for (int i = 0; i < 64; i++) + r10[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r11\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp master\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r11 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r11 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r11 \\+ 4" "gimple" } } */ + #pragma omp parallel master taskloop simd reduction(+:r11[1:2]) + for (int i = 0; i < 64; i++) + r11[1]++; + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r12 \\+ 4" "gimple" } } *//* FIXME: This should be on sections instead. */ + /* { dg-final { scan-tree-dump-not "omp sections\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r12 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump-not "omp section \[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r12 \\+ 4" "gimple" } } */ + #pragma omp parallel sections reduction(+:r12[1:2]) + { + r12[1]++; + #pragma omp section + r12[1]++; + } + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r13\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r13\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r13 \\+ 4" "gimple" } } */ + #pragma omp target parallel reduction(+:r13[1:2]) + r13[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r14\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r14" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r14 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r14 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp target parallel for reduction(+:r14[1:2]) + for (int i = 0; i < 64; i++) + r14[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r15\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r15\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r15 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r15 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r15 \\+ 4" "gimple" } } */ + #pragma omp target parallel for simd reduction(+:r15[1:2]) + for (int i = 0; i < 64; i++) + r15[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r16\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r16\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r16\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r16 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r16 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp target parallel loop reduction(+:r16[1:2]) + for (int i = 0; i < 64; i++) + r16[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r17\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r17\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r17 \\+ 4" "gimple" } } */ + #pragma omp target teams reduction(+:r17[1:2]) + r17[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r18\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r18\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r18 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r18 \\+ 4" "gimple" } } */ + #pragma omp target teams distribute reduction(+:r18[1:2]) + for (int i = 0; i < 64; i++) + r18[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r19\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r19\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r19 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r19 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r19 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r19 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp target teams distribute parallel for reduction(+:r19[1:2]) + for (int i = 0; i < 64; i++) + r19[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r20\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r20\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r20 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r20 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r20 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r20 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r20 \\+ 4" "gimple" } } */ + #pragma omp target teams distribute parallel for simd reduction(+:r20[1:2]) + for (int i = 0; i < 64; i++) + r20[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r21\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r21\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r21 \\+ 4" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r21 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r21 \\+ 4" "gimple" } } */ + #pragma omp target teams distribute simd reduction(+:r21[1:2]) + for (int i = 0; i < 64; i++) + r21[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r22\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(r22\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r22 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r22\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r22 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r22 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp target teams loop reduction(+:r22[1:2]) + for (int i = 0; i < 64; i++) + r22[1]++; + /* { dg-final { scan-tree-dump "omp target\[^\n\r]*map\\(tofrom:r23\\\[1\\\] \\\[len: 8\\\]" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp target\[^\n\r]*firstprivate\\(r23\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r23 \\+ 4" "gimple" } } */ + #pragma omp target simd reduction(+:r23[1:2]) + for (int i = 0; i < 64; i++) + r23[1]++; + /* { dg-final { scan-tree-dump "omp taskloop\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r24 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r24 \\+ 4" "gimple" } } */ + #pragma omp taskloop simd reduction(+:r24[1:2]) + for (int i = 0; i < 64; i++) + r24[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r25 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r25 \\+ 4" "gimple" } } */ + #pragma omp teams distribute reduction(+:r25[1:2]) + for (int i = 0; i < 64; i++) + r25[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r26 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r26 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r26 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r26 \\+ 4" "gimple" } } *//* FIXME. */ + #pragma omp teams distribute parallel for reduction(+:r26[1:2]) + for (int i = 0; i < 64; i++) + r26[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r27 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r27 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r27 \\+ 4" "gimple" } } *//* FIXME: This should be on for instead. */ + /* { dg-final { scan-tree-dump-not "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r27 \\+ 4" "gimple" } } *//* FIXME. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r27 \\+ 4" "gimple" } } */ + #pragma omp teams distribute parallel for simd reduction(+:r27[1:2]) + for (int i = 0; i < 64; i++) + r27[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r28 \\+ 4" "gimple" { xfail *-*-* } } } */ + /* { dg-final { scan-tree-dump-not "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r28 \\+ 4" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r28 \\+ 4" "gimple" } } */ + #pragma omp teams distribute simd reduction(+:r28[1:2]) + for (int i = 0; i < 64; i++) + r28[1]++; + /* { dg-final { scan-tree-dump "omp teams\[^\n\r]*shared\\(r29\\)" "gimple" } } */ + /* { dg-final { scan-tree-dump "omp distribute\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r29 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp parallel\[^\n\r]*shared\\(r29\\)" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp for\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r29 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + /* { dg-final { scan-tree-dump "omp simd\[^\n\r]*reduction\\(\\+:MEM\[^\n\r]*&r29 \\+ 4" "gimple" } } *//* NOTE: This is implementation detail. */ + #pragma omp teams loop reduction(+:r29[1:2]) + for (int i = 0; i < 64; i++) + r29[1]++; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/friend7.C b/gcc/testsuite/g++.dg/cpp0x/friend7.C new file mode 100644 index 0000000..734b367 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/friend7.C @@ -0,0 +1,40 @@ +// PR c++/99032 +// { dg-do compile { target c++11 } } + +class X { }; +template<typename T1, typename T2> +void foo (T1, T2); + +struct S { + [[deprecated]] friend void f(); // { dg-error "attribute appertains" } + [[deprecated]] friend void f2() { } + __attribute__((deprecated)) friend void f3(); // { dg-error "attribute appertains" } + friend void f3 [[deprecated]] (); // { dg-error "attribute appertains" } + friend void f4 [[deprecated]] () { } + [[deprecated]] friend void; // { dg-error "attribute appertains" } + __attribute__((deprecated)) friend int; // { dg-error "attribute appertains" } + friend __attribute__((deprecated)) int; // { dg-error "attribute appertains" } + friend int __attribute__((deprecated)); // { dg-error "attribute appertains" } + [[deprecated]] friend X; // { dg-error "attribute appertains" } + [[deprecated]] friend class N; // { dg-warning "attribute ignored" } + friend class [[deprecated]] N2; // { dg-error "attribute appertains" } + friend class __attribute__((deprecated)) N3; // { dg-error "attribute appertains" } + [[deprecated]] friend void foo<>(int, int); // { dg-error "attribute appertains" } + [[deprecated]] friend void ::foo(int, int); // { dg-error "attribute appertains" } + // { dg-bogus "should have" "PR100339" { xfail *-*-* } .-1 } +}; + +template<typename T> +class node { }; + +template<typename T> +struct A { + [[deprecated]] friend T; // { dg-error "attribute appertains" } + [[deprecated]] friend class node<T>; // { dg-warning "attribute ignored" } + template<typename> + [[deprecated]] friend class A; // { dg-warning "attribute ignored" } + template<typename> + [[deprecated]] friend void bar () { } + template<typename> + [[deprecated]] friend void baz (); // { dg-error "attribute appertains" } +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/gen-attrs-39-1.C b/gcc/testsuite/g++.dg/cpp0x/gen-attrs-39-1.C index 453fc01..4010ba7 100644 --- a/gcc/testsuite/g++.dg/cpp0x/gen-attrs-39-1.C +++ b/gcc/testsuite/g++.dg/cpp0x/gen-attrs-39-1.C @@ -2,7 +2,8 @@ int fragile_block(void) { typedef - [[gnu::aligned (16)]] // { dg-warning "ignored" } + [[gnu::aligned (16)]] // { dg-error "standard attributes in middle of decl-specifiers" } +// { dg-warning "attribute ignored" "" { target *-*-* } .-1 } struct { int i; } XmmUint16; diff --git a/gcc/testsuite/g++.dg/cpp0x/gen-attrs-4.C b/gcc/testsuite/g++.dg/cpp0x/gen-attrs-4.C index b401c69..c120aed 100644 --- a/gcc/testsuite/g++.dg/cpp0x/gen-attrs-4.C +++ b/gcc/testsuite/g++.dg/cpp0x/gen-attrs-4.C @@ -13,7 +13,8 @@ int one_third [[noreturn]] [[gnu::unused]] (void); int [[gnu::unused]] one_half(); // { dg-warning "ignored" } static -[[noreturn]] // { dg-warning "ignored" } +[[noreturn]] // { dg-error "standard attributes in middle of decl-specifiers" } +// { dg-warning "attribute ignored" "" { target *-*-* } .-1 } void two [[gnu::unused]] (void) {} diff --git a/gcc/testsuite/g++.dg/cpp0x/gen-attrs-74.C b/gcc/testsuite/g++.dg/cpp0x/gen-attrs-74.C new file mode 100644 index 0000000..7e17bc8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/gen-attrs-74.C @@ -0,0 +1,10 @@ +// { dg-do compile { target c++11 } } +// A C++11 attribute cannot appear in the middle of the decl-specifier-seq, +// only before it (in which case it appertains to the declaration) or at +// the end (in which case it appertains to the type). + +struct S { + friend [[deprecated]] void; // { dg-error "standard attributes in middle of decl-specifiers" } + friend [[deprecated]] int fn(); // { dg-error "standard attributes in middle of decl-specifiers" } + // { dg-warning "attribute ignored" "" { target *-*-* } .-1 } +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/pr94616.C b/gcc/testsuite/g++.dg/cpp0x/pr94616.C new file mode 100644 index 0000000..a8fe700 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr94616.C @@ -0,0 +1,23 @@ +// PR c++/94616 +// { dg-do compile { target c++11 } } + +struct Bar { + Bar(int n) { if (n > 0) throw 2; } + ~Bar() {} +}; + +struct Foo { + Bar b1 = 0; + Bar b2 = 1; + Foo() {} + ~Foo() {} +}; + +int +main() +{ + try { + Foo f; + } catch(int) { + } +} diff --git a/gcc/testsuite/g++.dg/cpp0x/sfinae68.C b/gcc/testsuite/g++.dg/cpp0x/sfinae68.C new file mode 100644 index 0000000..d292b68 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/sfinae68.C @@ -0,0 +1,23 @@ +// PR c++/90019 +// { dg-do compile { target c++11 } } + +template<bool, typename T = void> +struct enable_if { }; +template<typename T> +struct enable_if<true, T> { typedef T type; }; +template<bool C, typename T = void> +using __enable_if_t = typename enable_if<C, T>::type; +template<bool C, typename T = void> +using enable_if_t = typename enable_if<C, T>::type; + +template <int I, enable_if_t<I == 0, int>...> void foo() {} +template <int I, enable_if_t<I != 0, int>...> void foo() {} +template <int I, int=0, enable_if_t<I == 0, int>...> void bar() {} +template <int I, int=0, enable_if_t<I != 0, int>...> void bar() {} + +void test() +{ + bar<0>(); + bar<0,0>(); + foo<0>(); +} diff --git a/gcc/testsuite/g++.dg/cpp1y/digit-sep-paste.C b/gcc/testsuite/g++.dg/cpp1y/digit-sep-paste.C new file mode 100644 index 0000000..41fb967 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/digit-sep-paste.C @@ -0,0 +1,11 @@ +// Test token pasting with digit separators avoided for preprocessed output. +// { dg-do compile { target c++14 } } +// { dg-options "-save-temps" } + +#define ZERO 0 + +int +f () +{ + return ZERO'0'0; /* { dg-error "expected" } */ +} diff --git a/gcc/testsuite/g++.dg/cpp1y/pr88872.C b/gcc/testsuite/g++.dg/cpp1y/pr88872.C new file mode 100644 index 0000000..3719e39 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/pr88872.C @@ -0,0 +1,24 @@ +// PR c++/88872 +// { dg-do compile { target c++14 } } + +struct a { + template <typename b> constexpr a(b) : c() {} + int c; +}; +void d(); +template <char...> constexpr a operator"" _n() { return d; } +struct e; +struct f { + e operator[](int); +}; +struct g { + void h(); + f i; +}; +template <typename> struct j { + void k() { [](auto) { constexpr auto l = 2_n; }(keywords); } + int keywords; +}; +using m = j<int>; +class e : public m {}; +void g::h() { i[0].k(); } diff --git a/gcc/testsuite/g++.dg/cpp1y/pr95226.C b/gcc/testsuite/g++.dg/cpp1y/pr95226.C new file mode 100644 index 0000000..614c83c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/pr95226.C @@ -0,0 +1,17 @@ +// PR c++/95226 +// { dg-do run { target c++14 } } + +#include <vector> + +struct T { + unsigned a; + float b {8.}; +}; + +int main() +{ + T t = {1}; + std::vector<T> tt = {{1}, {2}}; + if (t.a != 1 || t.b != 8.0f || tt[0].a != 1 || tt[0].b != 8.0f || tt[1].a != 2 || tt[1].b != 8.0f) + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/ext/attrib63.C b/gcc/testsuite/g++.dg/ext/attrib63.C new file mode 100644 index 0000000..93bde1e --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attrib63.C @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-additional-options "-Wno-psabi" } + +#define vector __attribute__((vector_size(16))) +class A { + friend vector float f(); + __attribute__((deprecated)) friend void f2(); // { dg-error "attribute appertains" } + friend __attribute__((deprecated, vector_size(16))) float f3(); // { dg-error "attribute appertains" } + friend __attribute__((vector_size(16), deprecated)) float f4(); // { dg-error "attribute appertains" } +}; + +vector float vf; +vector float +f () +{ + return vf; +} diff --git a/gcc/testsuite/g++.dg/pr100574.C b/gcc/testsuite/g++.dg/pr100574.C new file mode 100644 index 0000000..42ba040 --- /dev/null +++ b/gcc/testsuite/g++.dg/pr100574.C @@ -0,0 +1,64 @@ +/* PR middle-end/100574 - ICE: in size_remaining, at builtins.c:413 with + -O3 -ftracer -fno-tree-dominator-opts -fno-tree-fre + { dg-do compile { target c++11 } } + { dg-options "-O3 -ftracer -fno-tree-dominator-opts -fno-tree-fre" } */ + +void *operator new (__SIZE_TYPE__, void *__p) { return __p; } + +template <typename> struct allocator_traits; +template <typename> class allocator {}; +template <typename _Tp> struct allocator_traits<allocator<_Tp> > +{ + using allocator_type = allocator<_Tp>; + using pointer = _Tp *; + using size_type = long; + template <typename _Up> using rebind_alloc = allocator<_Up>; + static pointer allocate(allocator_type, size_type); + template <typename _Up> static void construct(_Up __p) { new (__p) _Up(); } +}; + +struct __alloc_traits : allocator_traits<allocator<char>> { + struct rebind { + typedef rebind_alloc<char> other; + }; +}; + +struct _Vector_base { + struct : __alloc_traits::rebind::other { + } _M_impl; + long _M_allocate___n; +}; + +template <typename, typename = char> class vector : _Vector_base { + long max_size(); +public: + void push_back() { _M_realloc_insert(); } + template <typename...> void _M_realloc_insert(); +}; + +template <typename _Tp, typename _Alloc> +template <typename...> +void vector<_Tp, _Alloc>::_M_realloc_insert() { + __alloc_traits::pointer __trans_tmp_5; + long __len(__len || max_size()), __elems_before; + __trans_tmp_5 = _M_allocate___n + ? __alloc_traits::allocate(_M_impl, _M_allocate___n) + : __alloc_traits::pointer(); + __alloc_traits::construct(__trans_tmp_5 + __elems_before); +} + +enum { MIDIST_PITCHBEND }; +struct DataBlock { + vector<char> data; +}; + +char ReadTrackChunk_status; + +void ReadTrackChunk() +{ + DataBlock block; + while (!0) + switch (ReadTrackChunk_status) + case MIDIST_PITCHBEND: + block.data.push_back(); +} diff --git a/gcc/testsuite/g++.dg/tsan/pthread_cond_clockwait.C b/gcc/testsuite/g++.dg/tsan/pthread_cond_clockwait.C new file mode 100644 index 0000000..82d6a5c --- /dev/null +++ b/gcc/testsuite/g++.dg/tsan/pthread_cond_clockwait.C @@ -0,0 +1,31 @@ +// Test pthread_cond_clockwait not generating false positives with tsan +// { dg-do run { target { { *-*-linux* *-*-gnu* *-*-uclinux* } && pthread } } } +// { dg-options "-fsanitize=thread -lpthread" } + +#include <pthread.h> + +pthread_cond_t cv; +pthread_mutex_t mtx; + +void *fn(void *vp) { + pthread_mutex_lock(&mtx); + pthread_cond_signal(&cv); + pthread_mutex_unlock(&mtx); + return NULL; +} + +int main() { + pthread_mutex_lock(&mtx); + + pthread_t tid; + pthread_create(&tid, NULL, fn, NULL); + + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + ts.tv_sec += 10; + pthread_cond_clockwait(&cv, &mtx, CLOCK_MONOTONIC, &ts); + pthread_mutex_unlock(&mtx); + + pthread_join(tid, NULL); + return 0; +} diff --git a/gcc/testsuite/g++.dg/warn/Wint-in-bool-context-2.C b/gcc/testsuite/g++.dg/warn/Wint-in-bool-context-2.C new file mode 100644 index 0000000..6cb482d --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wint-in-bool-context-2.C @@ -0,0 +1,16 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-Wint-in-bool-context" } + +unsigned hb(unsigned i) { return ~i; } + +template<typename T> +void f(int i) +{ + auto l = [i]() { return T(2 * hb(i)); }; // { dg-bogus "in boolean context" } + (void) l; +} + +int main() +{ + f<bool>(0); +} diff --git a/gcc/testsuite/g++.dg/warn/uninit-pr93100.C b/gcc/testsuite/g++.dg/warn/uninit-pr93100.C new file mode 100644 index 0000000..56dc894 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/uninit-pr93100.C @@ -0,0 +1,60 @@ +/* PR tree-optimization/98508 - Sanitizer disable -Wall and -Wextra + { dg-do compile } + { dg-require-effective-target no_fsanitize_address } + { dg-options "-O0 -Wall -fsanitize=address" } */ + +struct S +{ + int a; +}; + +void warn_init_self_O0 () +{ + S s = S (s); // { dg-warning "\\\[-Wuninitialized" } + (void)&s; +} + + +void warn_init_self_use_O0 () +{ + S s = S (s); // { dg-warning "\\\[-Wuninitialized" } + + void sink (void*); + sink (&s); +} + + +#pragma GCC optimize ("1") + +void warn_init_self_O1 () +{ + S s = S (s); // { dg-warning "\\\[-Wuninitialized" } + (void)&s; +} + + +void warn_init_self_use_O1 () +{ + S s = S (s); // { dg-warning "\\\[-Wuninitialized" } + + void sink (void*); + sink (&s); +} + + +#pragma GCC optimize ("2") + +void warn_init_self_O2 () +{ + S s = S (s); // { dg-warning "\\\[-Wuninitialized" } + (void)&s; +} + + +void warn_init_self_use_O2 () +{ + S s = S (s); // { dg-warning "\\\[-Wuninitialized" } + + void sink (void*); + sink (&s); +} diff --git a/gcc/testsuite/g++.target/i386/pr100581.C b/gcc/testsuite/g++.target/i386/pr100581.C new file mode 100644 index 0000000..37cc9f1 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr100581.C @@ -0,0 +1,9 @@ +/* PR target/100581 */ +/* { dg-do compile { target { ! ia32 } } } */ +/* { dg-options "-O2 -mxop" } */ + +typedef float __attribute__((__vector_size__(8))) v64f32; + +v64f32 af, bf, ff_a, ff_b; + +v64f32 f() { return ff_a > ff_b ? af : bf; } diff --git a/gcc/testsuite/g++.target/i386/pr98218-1.C b/gcc/testsuite/g++.target/i386/pr98218-1.C new file mode 100644 index 0000000..61ea4bf --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr98218-1.C @@ -0,0 +1,20 @@ +/* PR target/98218 */ +/* { dg-do compile { target { ! ia32 } } } */ +/* { dg-options "-O2 -msse2" } */ + +typedef unsigned int __attribute__((__vector_size__ (8))) v64u32; +typedef int __attribute__((__vector_size__ (8))) v64s32; +typedef float __attribute__((__vector_size__ (8))) v64f32; + +v64u32 au, bu; +v64s32 as, bs; +v64f32 af, bf; + +v64u32 tu (v64f32 a, v64f32 b) { return (a > b) ? au : bu; } +v64s32 ts (v64f32 a, v64f32 b) { return (a > b) ? as : bs; } +v64f32 fu (v64u32 a, v64u32 b) { return (a > b) ? af : bf; } +v64f32 fs (v64s32 a, v64s32 b) { return (a > b) ? af : bf; } +v64f32 ff (v64f32 a, v64f32 b) { return (a > b) ? af : bf; } + +/* { dg-final { scan-assembler-times "cmpltps" 3 } } */ +/* { dg-final { scan-assembler-times "pcmpgtd" 2 } } */ diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-67.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-67.c new file mode 100644 index 0000000..7b8f3f0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-67.c @@ -0,0 +1,92 @@ +/* PR middle-end/100571 - bogus -Wstringop-overflow with VLA of elements + larger than byte + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +__attribute__ ((access (read_only, 1, 2))) void fro (int *, int); +__attribute__ ((access (write_only, 1, 2))) void fwo (int *, int); +__attribute__ ((access (read_write, 1, 2))) void frw (int *, int); + +extern __SIZE_TYPE__ n; + +void alloca_ro (void) +{ + int *a = __builtin_alloca (n * sizeof *a); + a[0] = 0; + fro (a, n); +} + +void alloca_wo (void) +{ + int *a = __builtin_alloca (n * sizeof *a); + fwo (a, n); +} + +void alloca_rw (void) +{ + int *a = __builtin_alloca (n * sizeof *a); + a[0] = 0; + frw (a, n); +} + + +void calloc_ro (void) +{ + int *a = __builtin_calloc (n, sizeof *a); + fro (a, n); +} + +void calloc_wo (void) +{ + int *a = __builtin_calloc (n, sizeof *a); + fwo (a, n); +} + +void calloc_rw (void) +{ + int *a = __builtin_calloc (n, sizeof *a); + a[0] = 0; + frw (a, n); +} + + +void malloc_ro (void) +{ + int *a = __builtin_malloc (n * sizeof *a); + a[0] = 0; + fro (a, n); +} + +void malloc_wo (void) +{ + int *a = __builtin_malloc (n * sizeof *a); + fwo (a, n); +} + +void malloc_rw (void) +{ + int *a = __builtin_malloc (n * sizeof *a); + a[0] = 0; + frw (a, n); +} + + +void vla_ro (void) +{ + int a[n]; + a[0] = 0; + fro (a, n); +} + +void vla_wo (void) +{ + int a[n]; + fwo (a, n); +} + +void vla_rw (void) +{ + int a[n]; + a[0] = 0; + frw (a, n); +} diff --git a/gcc/testsuite/gcc.dg/Wvla-parameter-9.c b/gcc/testsuite/gcc.dg/Wvla-parameter-9.c new file mode 100644 index 0000000..6c8987a --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wvla-parameter-9.c @@ -0,0 +1,30 @@ +/* PR c/100550 - ICE: in fold_convert_loc with function call VLA argument + { dg-do compile } + { dg-options "-Wall" } */ + +struct S { int i; }; + +extern void v; +extern void *pv; +extern struct S s; +void vf (void); + +/* Verify that a function redeclaration with an invalid VLA doesn't ICE. */ + +void f0 (int[]); +void f0 (int[undeclared]); // { dg-error "undeclared" } + +void f1 (int[]); +void f1 (int[-1]); // { dg-error "size" } + +void f2 (int[]); +void f2 (int[v]); // { dg-error "size" } + +void f3 (int[]); +void f3 (int b[s]); // { dg-error "size" } + +void f4 (int[]); +void f4 (int c[pv]); // { dg-error "size" } + +void f5 (int[]); +void f5 (int d[vf ()]); // { dg-error "size" } diff --git a/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c index 9ee56b6..3e75096 100644 --- a/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c +++ b/gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-2.c @@ -2,6 +2,7 @@ assignment. */ /* { dg-do run } */ /* { dg-options "-std=c11 -pedantic-errors" } */ +/* { dg-prune-output "warning: using serial compilation" } */ extern void abort (void); extern void exit (int); diff --git a/gcc/testsuite/gcc.dg/c2x-digit-separators-3.c b/gcc/testsuite/gcc.dg/c2x-digit-separators-3.c new file mode 100644 index 0000000..cddb88f --- /dev/null +++ b/gcc/testsuite/gcc.dg/c2x-digit-separators-3.c @@ -0,0 +1,12 @@ +/* Test C2x digit separators. Test token pasting avoided for preprocessed + output. */ +/* { dg-do compile } */ +/* { dg-options "-std=c2x -save-temps" } */ + +#define ZERO 0 + +int +f (void) +{ + return ZERO'0'0; /* { dg-error "expected" } */ +} diff --git a/gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c b/gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c new file mode 100644 index 0000000..2d5809a --- /dev/null +++ b/gcc/testsuite/gcc.dg/cpp/c11-elifdef-1.c @@ -0,0 +1,16 @@ +/* Test #elifdef and #elifndef not in C11. */ +/* { dg-do preprocess } */ +/* { dg-options "-std=c11 -pedantic-errors" } */ + +#define A +#undef B + +#if 0 +#elifdef A +#error "#elifdef A applied" +#endif + +#if 0 +#elifndef B +#error "#elifndef B applied" +#endif diff --git a/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c new file mode 100644 index 0000000..b23e311 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-1.c @@ -0,0 +1,57 @@ +/* Test #elifdef and #elifndef in C2x. */ +/* { dg-do preprocess } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +#define A +#undef B + +#if 0 +#elifdef A +#define M1 1 +#endif + +#if M1 != 1 +#error "#elifdef A did not apply" +#endif + +#if 0 +#elifdef B +#error "#elifdef B applied" +#endif + +#if 0 +#elifndef A +#error "#elifndef A applied" +#endif + +#if 0 +#elifndef B +#define M2 2 +#endif + +#if M2 != 2 +#error "#elifndef B did not apply" +#endif + +#if 0 +#elifdef A +#else +#error "#elifdef A did not apply" +#endif + +#if 0 +#elifndef B +#else +#error "#elifndef B did not apply" +#endif + +/* As with #elif, the syntax of the new directives is relaxed after a + non-skipped group. */ + +#if 1 +#elifdef x * y +#endif + +#if 1 +#elifndef ! +#endif diff --git a/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c new file mode 100644 index 0000000..9132832 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cpp/c2x-elifdef-2.c @@ -0,0 +1,63 @@ +/* Test #elifdef and #elifndef in C2x: erroneous usages. */ +/* { dg-do preprocess } */ +/* { dg-options "-std=c2x -pedantic-errors" } */ + +#define A +#undef B + +#elifdef A /* { dg-error "#elifdef without #if" } */ +#elifdef B /* { dg-error "#elifdef without #if" } */ +#elifndef A /* { dg-error "#elifndef without #if" } */ +#elifndef B /* { dg-error "#elifndef without #if" } */ + +#if 1 /* { dg-error "-:began here" } */ +#else +#elifdef A /* { dg-error "#elifdef after #else" } */ +#endif + +#if 1 /* { dg-error "-:began here" } */ +#else +#elifdef B /* { dg-error "#elifdef after #else" } */ +#endif + +#if 1 /* { dg-error "-:began here" } */ +#else +#elifndef A /* { dg-error "#elifndef after #else" } */ +#endif + +#if 1 /* { dg-error "-:began here" } */ +#else +#elifndef B /* { dg-error "#elifndef after #else" } */ +#endif + +#if 0 +#elifdef A = /* { dg-error "extra tokens at end of #elifdef directive" } */ +#endif + +#if 0 +#elifdef B = /* { dg-error "extra tokens at end of #elifdef directive" } */ +#endif + +#if 0 +#elifndef A = /* { dg-error "extra tokens at end of #elifndef directive" } */ +#endif + +#if 0 +#elifndef B = /* { dg-error "extra tokens at end of #elifndef directive" } */ +#endif + +#if 0 +#elifdef /* { dg-error "no macro name given in #elifdef directive" } */ +#endif + +#if 0 +#elifndef /* { dg-error "no macro name given in #elifndef directive" } */ +#endif + +#if 0 +#elifdef , /* { dg-error "macro names must be identifiers" } */ +#endif + +#if 0 +#elifndef , /* { dg-error "macro names must be identifiers" } */ +#endif diff --git a/gcc/testsuite/gcc.dg/cpp/pr100392.c b/gcc/testsuite/gcc.dg/cpp/pr100392.c new file mode 100644 index 0000000..670ad2b --- /dev/null +++ b/gcc/testsuite/gcc.dg/cpp/pr100392.c @@ -0,0 +1,5 @@ +/* PR preprocessor/100392 */ +/* { dg-do compile } */ +/* { dg-options "-save-temps -fdirectives-only" } */ + +#include "pr100392.h" diff --git a/gcc/testsuite/gcc.dg/cpp/pr100392.h b/gcc/testsuite/gcc.dg/cpp/pr100392.h new file mode 100644 index 0000000..340bc92 --- /dev/null +++ b/gcc/testsuite/gcc.dg/cpp/pr100392.h @@ -0,0 +1,4 @@ +/* PR preprocessor/100392 */ + +/* No newline after ; below. */ +int i = 1;
\ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/pr100515.c b/gcc/testsuite/gcc.dg/debug/dwarf2/pr100515.c new file mode 100644 index 0000000..17f6463 --- /dev/null +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/pr100515.c @@ -0,0 +1,20 @@ +/* PR debug/100515 */ +/* { dg-do compile } */ +/* { dg-require-effective-target fopenmp } */ +/* { dg-options "-g -O2 -fopenmp" } */ + +void +foo (int x) +{ +#pragma omp taskloop + for (int i = 0; i < x; i++) + ; +} + +void +bar (int x) +{ +#pragma omp taskloop + for (int i = 0; i < x; i++) + ; +} diff --git a/gcc/testsuite/gcc.dg/gomp/pr100508.c b/gcc/testsuite/gcc.dg/gomp/pr100508.c new file mode 100644 index 0000000..c3fa2fc --- /dev/null +++ b/gcc/testsuite/gcc.dg/gomp/pr100508.c @@ -0,0 +1,14 @@ +/* PR middle-end/100508 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -g -fopenmp-simd" } */ + +typedef int __attribute__((__vector_size__(32))) V; +V j; + +#pragma omp declare simd +int +foo (void) +{ + V m = j; + return 0; +} diff --git a/gcc/testsuite/gcc.dg/guality/pr43077-1.c b/gcc/testsuite/gcc.dg/guality/pr43077-1.c index 39bd26a..2d93762 100644 --- a/gcc/testsuite/gcc.dg/guality/pr43077-1.c +++ b/gcc/testsuite/gcc.dg/guality/pr43077-1.c @@ -24,7 +24,7 @@ int __attribute__((noinline)) foo (unsigned long *p, unsigned long *q) { int ret; - asm volatile ("" : "=r" (ret), "=r" (*p), "=r" (*q) : "0" (1), "1" (2), "2" (3)); + asm volatile ("" : "=r" (ret), "=r" (*p), "=r" (*q) : "0" (1), "1" (2l), "2" (3l)); return ret; } diff --git a/gcc/testsuite/gcc.dg/pr100521.c b/gcc/testsuite/gcc.dg/pr100521.c new file mode 100644 index 0000000..fd9f0db --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr100521.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +int +__builtin_clz (int a) +{ + return __builtin_clz(a); +} diff --git a/gcc/testsuite/gcc.dg/pr100563.c b/gcc/testsuite/gcc.dg/pr100563.c new file mode 100644 index 0000000..f6a5fcd --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr100563.c @@ -0,0 +1,9 @@ +/* { dg-do compile } */ +/* { dg-options "-Og -Wno-pointer-to-int-cast" } */ +unsigned long long e(void); +void f(int); +void a() { + short b = -1, c = (int)&b; + unsigned long long d = e(); + f(b >= d); +} diff --git a/gcc/testsuite/gcc.dg/torture/pr100519.c b/gcc/testsuite/gcc.dg/torture/pr100519.c new file mode 100644 index 0000000..faf6e24 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr100519.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-additional-options "--param tree-reassoc-width=2" } */ + +unsigned int foo_a1, foo_a2; + +unsigned int foo() +{ + unsigned int v0, x; + asm goto("" : "=r"(x) : : : lab); +lab: + v0 += x + x; + return v0 + x + foo_a1 + foo_a2; +} diff --git a/gcc/testsuite/gcc.dg/torture/pr100566.c b/gcc/testsuite/gcc.dg/torture/pr100566.c new file mode 100644 index 0000000..ed85691 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr100566.c @@ -0,0 +1,36 @@ +/* { dg-do run } */ + +volatile int s, c; + +__attribute__((noipa)) void +foo (void) +{ + if (c++ > 1) + __builtin_abort (); +} + +__attribute__((noipa)) int +bar (void) +{ + int i = 0, j = s; + if (j == 0) + goto lab; + for (i = 0; i < j; i++) + { + lab: + foo (); + if (!j) + goto lab; + } + return 0; +} + +int +main () +{ + s = 1; + bar (); + if (c != 1) + __builtin_abort (); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/pr94947-1.c b/gcc/testsuite/gcc.dg/torture/pr94947-1.c index ab8b488..832e40d 100644 --- a/gcc/testsuite/gcc.dg/torture/pr94947-1.c +++ b/gcc/testsuite/gcc.dg/torture/pr94947-1.c @@ -1,6 +1,7 @@ /* { dg-do run } */ /* { dg-additional-sources "pr94947-2.c" } */ /* { dg-additional-options "-fipa-pta -flto-partition=1to1" } */ +/* { dg-prune-output "warning: using serial compilation" } */ extern void abort (); extern void baz (); diff --git a/gcc/testsuite/gcc.dg/torture/vshuf-4.inc b/gcc/testsuite/gcc.dg/torture/vshuf-4.inc index d041b33..fb35df8 100644 --- a/gcc/testsuite/gcc.dg/torture/vshuf-4.inc +++ b/gcc/testsuite/gcc.dg/torture/vshuf-4.inc @@ -25,7 +25,9 @@ T (21, 2, 6, 3, 7) \ T (22, 1, 2, 3, 0) \ T (23, 2, 1, 0, 3) \ T (24, 2, 5, 6, 3) \ -T (25, 0, 1, 4, 5) +T (25, 0, 1, 4, 5) \ +T (26, 1, 5, 3, 7) \ +T (27, 0, 5, 2, 7) #define EXPTESTS \ T (116, 1, 2, 4, 3) \ T (117, 7, 3, 3, 0) \ diff --git a/gcc/testsuite/gcc.dg/torture/vshuf-8.inc b/gcc/testsuite/gcc.dg/torture/vshuf-8.inc index de358f3..d628039 100644 --- a/gcc/testsuite/gcc.dg/torture/vshuf-8.inc +++ b/gcc/testsuite/gcc.dg/torture/vshuf-8.inc @@ -27,7 +27,9 @@ T (23, 6, 5, 4, 3, 2, 1, 0, 7) \ T (24, 0, 1, 2, 3, 8, 9, 10, 11) \ T (25, 0, 1, 2, 3, 12, 13, 14, 15) \ T (26, 0, 1, 8, 9, 10, 11, 12, 13) \ -T (27, 0, 8, 9, 10, 11, 12, 13, 14) +T (27, 0, 8, 9, 10, 11, 12, 13, 14) \ +T (28, 1, 9, 3, 11, 5, 13, 7, 15) \ +T (29, 0, 9, 2, 11, 4, 13, 6, 15) #define EXPTESTS \ T (116, 9, 3, 9, 4, 7, 0, 0, 6) \ T (117, 4, 14, 12, 8, 9, 6, 0, 10) \ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr94589-1.c b/gcc/testsuite/gcc.dg/tree-ssa/pr94589-1.c new file mode 100644 index 0000000..7e1aaaa --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr94589-1.c @@ -0,0 +1,21 @@ +/* PR tree-optimization/94589 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +int +foo (int x) +{ + return (x & 23) == x; +/* { dg-final { scan-tree-dump " & -24;" "optimized" } } */ +/* { dg-final { scan-tree-dump-not " & 23;" "optimized" } } */ +/* { dg-final { scan-tree-dump " == 0" "optimized" } } */ +} + +int +bar (int x) +{ + return (x | 137) != 137; +/* { dg-final { scan-tree-dump " & -138;" "optimized" } } */ +/* { dg-final { scan-tree-dump-not " \\| 137;" "optimized" } } */ +/* { dg-final { scan-tree-dump " != 0" "optimized" } } */ +} diff --git a/gcc/testsuite/gcc.dg/uninit-pr93100.c b/gcc/testsuite/gcc.dg/uninit-pr93100.c new file mode 100644 index 0000000..61b7e43 --- /dev/null +++ b/gcc/testsuite/gcc.dg/uninit-pr93100.c @@ -0,0 +1,74 @@ +/* PR tree-optimization/93100 - gcc -fsanitize=address inhibits -Wuninitialized + { dg-do compile } + { dg-options "-Wall -fsanitize=address" } */ + +struct A +{ + _Bool b; + int i; +}; + +void warn_A_b_O0 (void) +{ + struct A a; + + if (a.b) // { dg-warning "\\\[-Wuninitialized" } + { + (void)&a; + } +} + +void warn_A_i_O0 (void) +{ + struct A a; + + if (a.i) // { dg-warning "\\\[-Wuninitialized" } + { + (void)&a; + } +} + +#pragma GCC optimize ("1") + +void warn_A_b_O1 (void) +{ + struct A a; + + if (a.b) // { dg-warning "\\\[-Wuninitialized" } + { + (void)&a; + } +} + +void warn_A_i_O1 (void) +{ + struct A a; + + if (a.i) // { dg-warning "\\\[-Wuninitialized" } + { + (void)&a; + } +} + + +#pragma GCC optimize ("2") + +void warn_A_b_O2 (void) +{ + struct A a; + + if (a.b) // { dg-warning "\\\[-Wuninitialized" } + { + (void)&a; + } +} + +void warn_A_i_O2 (void) +{ + struct A a; + + if (a.i) // { dg-warning "\\\[-Wuninitialized" } + { + (void)&a; + } +} diff --git a/gcc/testsuite/gcc.dg/uninit-pr98583.c b/gcc/testsuite/gcc.dg/uninit-pr98583.c new file mode 100644 index 0000000..638b029 --- /dev/null +++ b/gcc/testsuite/gcc.dg/uninit-pr98583.c @@ -0,0 +1,31 @@ +/* PR middle-end/98583 - missing -Wuninitialized reading from a second VLA + in its own block + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +void f (int*); +void g (int); + +void h1 (int n) +{ + int a[n]; + f (a); + + int b[n]; + g (b[1]); // { dg-warning "\\\[-Wuninitialized" } +} + +void h2 (int n, int i, int j) +{ + if (i) + { + int a[n]; + f (a); + } + + if (j) + { + int b[n]; + g (b[1]); // { dg-warning "\\\[-Wmaybe-uninitialized" } + } +} diff --git a/gcc/testsuite/gcc.target/arm/acle/saturation.c b/gcc/testsuite/gcc.target/arm/acle/saturation.c index 0b3fe51..a9f99e5 100644 --- a/gcc/testsuite/gcc.target/arm/acle/saturation.c +++ b/gcc/testsuite/gcc.target/arm/acle/saturation.c @@ -1,6 +1,6 @@ /* { dg-do compile } */ -/* { dg-require-effective-target arm_qbit_ok } */ -/* { dg-add-options arm_qbit } */ +/* { dg-require-effective-target arm_sat_ok } */ +/* { dg-add-options arm_sat } */ #include <arm_acle.h> diff --git a/gcc/testsuite/gcc.target/arm/armv8_2-fp16-arith-1.c b/gcc/testsuite/gcc.target/arm/armv8_2-fp16-arith-1.c index 921d26e..52b8737 100644 --- a/gcc/testsuite/gcc.target/arm/armv8_2-fp16-arith-1.c +++ b/gcc/testsuite/gcc.target/arm/armv8_2-fp16-arith-1.c @@ -104,8 +104,20 @@ TEST_CMP (greaterthanqual, >=, int16x8_t, float16x8_t) /* { dg-final { scan-assembler-times {vmul\.f16\tq[0-9]+, q[0-9]+, q[0-9]+} 1 } } */ /* { dg-final { scan-assembler-times {vdiv\.f16\ts[0-9]+, s[0-9]+, s[0-9]+} 13 } } */ -/* { dg-final { scan-assembler-times {vcmp\.f32\ts[0-9]+, s[0-9]+} 26 } } */ -/* { dg-final { scan-assembler-times {vcmpe\.f32\ts[0-9]+, s[0-9]+} 52 } } */ + +/* For float16_t. */ +/* { dg-final { scan-assembler-times {vcmp\.f32\ts[0-9]+, s[0-9]+} 2 } } */ +/* { dg-final { scan-assembler-times {vcmpe\.f32\ts[0-9]+, s[0-9]+} 4 } } */ + +/* For float16x4_t. */ +/* { dg-final { scan-assembler-times {vceq\.f16\td[0-9]+, d[0-9]+} 2 } } */ +/* { dg-final { scan-assembler-times {vcge\.f16\td[0-9]+, d[0-9]+} 2 } } */ +/* { dg-final { scan-assembler-times {vcgt\.f16\td[0-9]+, d[0-9]+} 2 } } */ + +/* For float16x8_t. */ +/* { dg-final { scan-assembler-times {vceq\.f16\tq[0-9]+, q[0-9]+} 2 } } */ +/* { dg-final { scan-assembler-times {vcge\.f16\tq[0-9]+, q[0-9]+} 2 } } */ +/* { dg-final { scan-assembler-times {vcgt\.f16\tq[0-9]+, q[0-9]+} 2 } } */ /* { dg-final { scan-assembler-not {vadd\.f32} } } */ /* { dg-final { scan-assembler-not {vsub\.f32} } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-compare-1.c b/gcc/testsuite/gcc.target/arm/simd/mve-compare-1.c new file mode 100644 index 0000000..029c931 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-compare-1.c @@ -0,0 +1,80 @@ +/* { dg-do assemble } */ +/* { dg-require-effective-target arm_v8_1m_mve_ok } */ +/* { dg-add-options arm_v8_1m_mve } */ +/* { dg-additional-options "-O3" } */ + +/* Integer tests. */ + +#define COMPARE_REG(NAME, OP, TYPE) \ + TYPE \ + cmp_##NAME##_##TYPE##_reg (TYPE a, TYPE b) \ + { \ + return a OP b; \ + } + +#define COMPARE_REG_AND_ZERO(NAME, OP, TYPE) \ + COMPARE_REG (NAME, OP, TYPE) \ + \ + TYPE \ + cmp_##NAME##_##TYPE##_zero (TYPE a) \ + { \ + return a OP (TYPE) {}; \ + } + +#define COMPARE_TYPE(TYPE, COMPARE_ORDERED) \ + COMPARE_REG_AND_ZERO (eq, ==, TYPE) \ + COMPARE_REG_AND_ZERO (ne, !=, TYPE) \ + COMPARE_ORDERED (lt, <, TYPE) \ + COMPARE_ORDERED (le, <=, TYPE) \ + COMPARE_ORDERED (gt, >, TYPE) \ + COMPARE_ORDERED (ge, >=, TYPE) + +#define TEST_TYPE(NAME, ELEM, COMPARE_ORDERED, SIZE) \ + typedef ELEM NAME##SIZE __attribute__((vector_size(SIZE))); \ + COMPARE_TYPE (NAME##SIZE, COMPARE_ORDERED) + +/* 64-bits vectors, not vectorized. */ +TEST_TYPE (vs8, __INT8_TYPE__, COMPARE_REG_AND_ZERO, 8) +TEST_TYPE (vu8, __UINT8_TYPE__, COMPARE_REG, 8) +TEST_TYPE (vs16, __INT16_TYPE__, COMPARE_REG_AND_ZERO, 8) +TEST_TYPE (vu16, __UINT16_TYPE__, COMPARE_REG, 8) +TEST_TYPE (vs32, __INT32_TYPE__, COMPARE_REG_AND_ZERO, 8) +TEST_TYPE (vu32, __UINT32_TYPE__, COMPARE_REG, 8) + +/* 128-bits vectors. */ +TEST_TYPE (vs8, __INT8_TYPE__, COMPARE_REG_AND_ZERO, 16) +TEST_TYPE (vu8, __UINT8_TYPE__, COMPARE_REG, 16) +TEST_TYPE (vs16, __INT16_TYPE__, COMPARE_REG_AND_ZERO, 16) +TEST_TYPE (vu16, __UINT16_TYPE__, COMPARE_REG, 16) +TEST_TYPE (vs32, __INT32_TYPE__, COMPARE_REG_AND_ZERO, 16) +TEST_TYPE (vu32, __UINT32_TYPE__, COMPARE_REG, 16) + +/* { 8 bits } x { eq, ne, lt, le, gt, ge, hi, cs }. +/* { dg-final { scan-assembler-times {\tvcmp.i8 eq, q[0-9]+, q[0-9]+\n} 4 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.i8 ne, q[0-9]+, q[0-9]+\n} 4 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s8 lt, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s8 le, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s8 gt, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s8 ge, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u8 hi, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u8 cs, q[0-9]+, q[0-9]+\n} 2 } } */ + +/* { 16 bits } x { eq, ne, lt, le, gt, ge, hi, cs }. +/* { dg-final { scan-assembler-times {\tvcmp.i16 eq, q[0-9]+, q[0-9]+\n} 4 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.i16 ne, q[0-9]+, q[0-9]+\n} 4 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s16 lt, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s16 le, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s16 gt, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s16 ge, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u16 hi, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u16 cs, q[0-9]+, q[0-9]+\n} 2 } } */ + +/* { 32 bits } x { eq, ne, lt, le, gt, ge, hi, cs }. +/* { dg-final { scan-assembler-times {\tvcmp.i32 eq, q[0-9]+, q[0-9]+\n} 4 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.i32 ne, q[0-9]+, q[0-9]+\n} 4 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s32 lt, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s32 le, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s32 gt, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s32 ge, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u32 hi, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u32 cs, q[0-9]+, q[0-9]+\n} 2 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-compare-2.c b/gcc/testsuite/gcc.target/arm/simd/mve-compare-2.c new file mode 100644 index 0000000..8515195 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-compare-2.c @@ -0,0 +1,38 @@ +/* { dg-do assemble } */ +/* { dg-require-effective-target arm_v8_1m_mve_fp_ok } */ +/* { dg-add-options arm_v8_1m_mve_fp } */ +/* { dg-additional-options "-O3 -funsafe-math-optimizations" } */ + +/* float 32 tests. */ + +#ifndef ELEM_TYPE +#define ELEM_TYPE float +#endif +#ifndef INT_ELEM_TYPE +#define INT_ELEM_TYPE __INT32_TYPE__ +#endif + +#define COMPARE(NAME, OP) \ + int_vec \ + cmp_##NAME##_reg (vec a, vec b) \ + { \ + return a OP b; \ + } + +typedef INT_ELEM_TYPE int_vec __attribute__((vector_size(16))); +typedef ELEM_TYPE vec __attribute__((vector_size(16))); + +COMPARE (eq, ==) +COMPARE (ne, !=) +COMPARE (lt, <) +COMPARE (le, <=) +COMPARE (gt, >) +COMPARE (ge, >=) + +/* eq, ne, lt, le, gt, ge. +/* { dg-final { scan-assembler-times {\tvcmp.f32\teq, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tne, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tlt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tle, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tgt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tge, q[0-9]+, q[0-9]+\n} 1 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-compare-3.c b/gcc/testsuite/gcc.target/arm/simd/mve-compare-3.c new file mode 100644 index 0000000..76f81e8 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-compare-3.c @@ -0,0 +1,38 @@ +/* { dg-do assemble } */ +/* { dg-require-effective-target arm_v8_1m_mve_fp_ok } */ +/* { dg-add-options arm_v8_1m_mve_fp } */ +/* { dg-additional-options "-O3 -funsafe-math-optimizations" } */ + +/* float 16 tests. */ + +#ifndef ELEM_TYPE +#define ELEM_TYPE __fp16 +#endif +#ifndef INT_ELEM_TYPE +#define INT_ELEM_TYPE __INT16_TYPE__ +#endif + +#define COMPARE(NAME, OP) \ + int_vec \ + cmp_##NAME##_reg (vec a, vec b) \ + { \ + return a OP b; \ + } + +typedef INT_ELEM_TYPE int_vec __attribute__((vector_size(16))); +typedef ELEM_TYPE vec __attribute__((vector_size(16))); + +COMPARE (eq, ==) +COMPARE (ne, !=) +COMPARE (lt, <) +COMPARE (le, <=) +COMPARE (gt, >) +COMPARE (ge, >=) + +/* eq, ne, lt, le, gt, ge. +/* { dg-final { scan-assembler-times {\tvcmp.f16\teq, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tne, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tlt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tle, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tgt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tge, q[0-9]+, q[0-9]+\n} 1 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-compare-scalar-1.c b/gcc/testsuite/gcc.target/arm/simd/mve-compare-scalar-1.c new file mode 100644 index 0000000..7774972 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-compare-scalar-1.c @@ -0,0 +1,69 @@ +/* { dg-do assemble } */ +/* { dg-require-effective-target arm_v8_1m_mve_ok } */ +/* { dg-add-options arm_v8_1m_mve } */ +/* { dg-additional-options "-O3" } */ + +#define COMPARE_REG(NAME, OP, TYPE, SCALAR) \ + TYPE \ + cmp_##NAME##_##TYPE##_scalar (TYPE a, SCALAR b) \ + { \ + return a OP b; \ + } + +#define COMPARE_TYPE(SCALAR, TYPE) \ + COMPARE_REG (eq, ==, TYPE, SCALAR) \ + COMPARE_REG (ne, !=, TYPE, SCALAR) \ + COMPARE_REG (lt, <, TYPE, SCALAR) \ + COMPARE_REG (le, <=, TYPE, SCALAR) \ + COMPARE_REG (gt, >, TYPE, SCALAR) \ + COMPARE_REG (ge, >=, TYPE, SCALAR) + +#define TEST_TYPE(NAME, ELEM, SIZE) \ + typedef ELEM NAME##SIZE __attribute__((vector_size(SIZE))); \ + COMPARE_TYPE (ELEM, NAME##SIZE) + +/* 64-bits vectors, not vectorized. */ +TEST_TYPE (vs8, __INT8_TYPE__, 8) +TEST_TYPE (vu8, __UINT8_TYPE__, 8) +TEST_TYPE (vs16, __INT16_TYPE__, 8) +TEST_TYPE (vu16, __UINT16_TYPE__, 8) +TEST_TYPE (vs32, __INT32_TYPE__, 8) +TEST_TYPE (vu32, __UINT32_TYPE__, 8) + +/* 128-bits vectors. */ +TEST_TYPE (vs8, __INT8_TYPE__, 16) +TEST_TYPE (vu8, __UINT8_TYPE__, 16) +TEST_TYPE (vs16, __INT16_TYPE__, 16) +TEST_TYPE (vu16, __UINT16_TYPE__, 16) +TEST_TYPE (vs32, __INT32_TYPE__, 16) +TEST_TYPE (vu32, __UINT32_TYPE__, 16) + +/* { 8 bits } x { eq, ne, lt, le, gt, ge, hi, cs }. +/* { dg-final { scan-assembler-times {\tvcmp.i8 eq, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.i8 ne, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s8 lt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s8 le, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s8 gt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s8 ge, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u8 hi, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u8 cs, q[0-9]+, q[0-9]+\n} 2 } } */ + +/* { 16 bits } x { eq, ne, lt, le, gt, ge, hi, cs }. +/* { dg-final { scan-assembler-times {\tvcmp.i16 eq, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.i16 ne, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s16 lt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s16 le, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s16 gt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s16 ge, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u16 hi, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u16 cs, q[0-9]+, q[0-9]+\n} 2 } } */ + +/* { 32 bits } x { eq, ne, lt, le, gt, ge, hi, cs }. +/* { dg-final { scan-assembler-times {\tvcmp.i32 eq, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.i32 ne, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s32 lt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s32 le, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s32 gt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s32 ge, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u32 hi, q[0-9]+, q[0-9]+\n} 2 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u32 cs, q[0-9]+, q[0-9]+\n} 2 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-vadd-1.c b/gcc/testsuite/gcc.target/arm/simd/mve-vadd-1.c new file mode 100644 index 0000000..15a9daa --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-vadd-1.c @@ -0,0 +1,43 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_v8_1m_mve_fp_ok } */ +/* { dg-add-options arm_v8_1m_mve_fp } */ +/* { dg-additional-options "-O3" } */ + +#include <stdint.h> + +#define FUNC(SIGN, TYPE, BITS, NB, OP, NAME) \ + void test_ ## NAME ##_ ## SIGN ## BITS ## x ## NB (TYPE##BITS##_t * __restrict__ dest, \ + TYPE##BITS##_t *a, TYPE##BITS##_t *b) { \ + int i; \ + for (i=0; i<NB; i++) { \ + dest[i] = a[i] OP b[i]; \ + } \ +} + +/* 128-bit vectors. */ +FUNC(s, int, 32, 4, +, vadd) +FUNC(u, uint, 32, 4, +, vadd) +FUNC(s, int, 16, 8, +, vadd) +FUNC(u, uint, 16, 8, +, vadd) +FUNC(s, int, 8, 16, +, vadd) +FUNC(u, uint, 8, 16, +, vadd) + +/* { dg-final { scan-assembler-times {vadd\.i32 q[0-9]+, q[0-9]+, q[0-9]+} 2 } } */ +/* { dg-final { scan-assembler-times {vadd\.i16 q[0-9]+, q[0-9]+, q[0-9]+} 2 } } */ +/* { dg-final { scan-assembler-times {vadd\.i8 q[0-9]+, q[0-9]+, q[0-9]+} 2 } } */ + +void test_vadd_f32 (float * dest, float * a, float * b) { + int i; + for (i=0; i<4; i++) { + dest[i] = a[i] + b[i]; + } +} +/* { dg-final { scan-assembler-times {vadd\.f32 q[0-9]+, q[0-9]+, q[0-9]+} 1 } } */ + +void test_vadd_f16 (__fp16 * dest, __fp16 * a, __fp16 * b) { + int i; + for (i=0; i<8; i++) { + dest[i] = a[i] + b[i]; + } +} +/* { dg-final { scan-assembler-times {vadd\.f16 q[0-9]+, q[0-9]+, q[0-9]+} 1 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-vadd-scalar-1.c b/gcc/testsuite/gcc.target/arm/simd/mve-vadd-scalar-1.c new file mode 100644 index 0000000..bbf70e1 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-vadd-scalar-1.c @@ -0,0 +1,47 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_v8_1m_mve_fp_ok } */ +/* { dg-add-options arm_v8_1m_mve_fp } */ +/* { dg-additional-options "-O3" } */ + +#include <stdint.h> + +#define FUNC_IMM(SIGN, TYPE, BITS, NB, OP, NAME) \ + void test_ ## NAME ##_ ## SIGN ## BITS ## x ## NB (TYPE##BITS##_t * __restrict__ dest, \ + TYPE##BITS##_t *a) { \ + int i; \ + for (i=0; i<NB; i++) { \ + dest[i] = a[i] OP 1; \ + } \ +} + +/* 128-bit vectors. */ +FUNC_IMM(s, int, 32, 4, +, vaddimm) +FUNC_IMM(u, uint, 32, 4, +, vaddimm) +FUNC_IMM(s, int, 16, 8, +, vaddimm) +FUNC_IMM(u, uint, 16, 8, +, vaddimm) +FUNC_IMM(s, int, 8, 16, +, vaddimm) +FUNC_IMM(u, uint, 8, 16, +, vaddimm) + +/* For the moment we do not select the T2 vadd variant operating on a scalar + final argument. */ +/* { dg-final { scan-assembler-times {vadd\.i32 q[0-9]+, q[0-9]+, r[0-9]+} 2 { xfail *-*-* } } } */ +/* { dg-final { scan-assembler-times {vadd\.i16 q[0-9]+, q[0-9]+, r[0-9]+} 2 { xfail *-*-* } } } */ +/* { dg-final { scan-assembler-times {vadd\.i8 q[0-9]+, q[0-9]+, r[0-9]+} 2 { xfail *-*-* } } } */ + +void test_vaddimm_f32 (float * dest, float * a) { + int i; + for (i=0; i<4; i++) { + dest[i] = a[i] + 5.0; + } +} +/* { dg-final { scan-assembler-times {vadd\.f32 q[0-9]+, q[0-9]+, r[0-9]+} 1 { xfail *-*-* } } } */ + +/* Note that dest[i] = a[i] + 5.0f16 is not vectorized. */ +void test_vaddimm_f16 (__fp16 * dest, __fp16 * a) { + int i; + __fp16 b = 5.0f16; + for (i=0; i<8; i++) { + dest[i] = a[i] + b; + } +} +/* { dg-final { scan-assembler-times {vadd\.f16 q[0-9]+, q[0-9]+, r[0-9]+} 1 { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-vcmp-f16.c b/gcc/testsuite/gcc.target/arm/simd/mve-vcmp-f16.c new file mode 100644 index 0000000..dbae2d1 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-vcmp-f16.c @@ -0,0 +1,30 @@ +/* { dg-do assemble } */ +/* { dg-require-effective-target arm_v8_1m_mve_fp_ok } */ +/* { dg-add-options arm_v8_1m_mve_fp } */ +/* { dg-additional-options "-O3 -funsafe-math-optimizations" } */ + +#include <stdint.h> + +#define NB 8 + +#define FUNC(OP, NAME) \ + void test_ ## NAME ##_f (__fp16 * __restrict__ dest, __fp16 *a, __fp16 *b) { \ + int i; \ + for (i=0; i<NB; i++) { \ + dest[i] = a[i] OP b[i]; \ + } \ + } + +FUNC(==, vcmpeq) +FUNC(!=, vcmpne) +FUNC(<, vcmplt) +FUNC(<=, vcmple) +FUNC(>, vcmpgt) +FUNC(>=, vcmpge) + +/* { dg-final { scan-assembler-times {\tvcmp.f16\teq, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tne, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tlt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tle, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tgt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f16\tge, q[0-9]+, q[0-9]+\n} 1 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-vcmp-f32.c b/gcc/testsuite/gcc.target/arm/simd/mve-vcmp-f32.c new file mode 100644 index 0000000..4ed449e --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-vcmp-f32.c @@ -0,0 +1,30 @@ +/* { dg-do assemble } */ +/* { dg-require-effective-target arm_v8_1m_mve_fp_ok } */ +/* { dg-add-options arm_v8_1m_mve_fp } */ +/* { dg-additional-options "-O3 -funsafe-math-optimizations" } */ + +#include <stdint.h> + +#define NB 4 + +#define FUNC(OP, NAME) \ + void test_ ## NAME ##_f (float * __restrict__ dest, float *a, float *b) { \ + int i; \ + for (i=0; i<NB; i++) { \ + dest[i] = a[i] OP b[i]; \ + } \ + } + +FUNC(==, vcmpeq) +FUNC(!=, vcmpne) +FUNC(<, vcmplt) +FUNC(<=, vcmple) +FUNC(>, vcmpgt) +FUNC(>=, vcmpge) + +/* { dg-final { scan-assembler-times {\tvcmp.f32\teq, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tne, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tlt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tle, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tgt, q[0-9]+, q[0-9]+\n} 1 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.f32\tge, q[0-9]+, q[0-9]+\n} 1 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-vcmp.c b/gcc/testsuite/gcc.target/arm/simd/mve-vcmp.c new file mode 100644 index 0000000..8da15e7 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/simd/mve-vcmp.c @@ -0,0 +1,50 @@ +/* { dg-do assemble } */ +/* { dg-require-effective-target arm_v8_1m_mve_ok } */ +/* { dg-add-options arm_v8_1m_mve } */ +/* { dg-additional-options "-O3" } */ + +#include <stdint.h> + +#define FUNC(SIGN, TYPE, BITS, NB, OP, NAME) \ + void test_ ## NAME ##_ ## SIGN ## BITS ## x ## NB (TYPE##BITS##_t * __restrict__ dest, TYPE##BITS##_t *a, TYPE##BITS##_t *b) { \ + int i; \ + for (i=0; i<NB; i++) { \ + dest[i] = a[i] OP b[i]; \ + } \ +} + +#define ALL_FUNCS(OP, NAME) \ + FUNC(s, int, 32, 2, OP, NAME) \ + FUNC(u, uint, 32, 2, OP, NAME) \ + FUNC(s, int, 16, 4, OP, NAME) \ + FUNC(u, uint, 16, 4, OP, NAME) \ + FUNC(s, int, 8, 8, OP, NAME) \ + FUNC(u, uint, 8, 8, OP, NAME) \ + FUNC(s, int, 32, 4, OP, NAME) \ + FUNC(u, uint, 32, 4, OP, NAME) \ + FUNC(s, int, 16, 8, OP, NAME) \ + FUNC(u, uint, 16, 8, OP, NAME) \ + FUNC(s, int, 8, 16, OP, NAME) \ + FUNC(u, uint, 8, 16, OP, NAME) + +ALL_FUNCS(==, vcmpeq) +ALL_FUNCS(!=, vcmpne) +ALL_FUNCS(<, vcmplt) +ALL_FUNCS(<=, vcmple) +ALL_FUNCS(>, vcmpgt) +ALL_FUNCS(>=, vcmpge) + +/* MVE has only 128-bit vectors, so we can vectorize only half of the + functions above. */ +/* { dg-final { scan-assembler-times {\tvcmp.i[0-9]+ eq, q[0-9]+, q[0-9]+\n} 6 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.i[0-9]+ ne, q[0-9]+, q[0-9]+\n} 6 } } */ + +/* lt, le, gt, ge apply to signed types, cs and hi to unsigned types. */ +/* lt and le with unsigned types are replaced with the opposite condition, hence + the double number of matches for cs and hi. */ +/* { dg-final { scan-assembler-times {\tvcmp.s[0-9]+ lt, q[0-9]+, q[0-9]+\n} 3 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s[0-9]+ le, q[0-9]+, q[0-9]+\n} 3 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s[0-9]+ gt, q[0-9]+, q[0-9]+\n} 3 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.s[0-9]+ ge, q[0-9]+, q[0-9]+\n} 3 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u[0-9]+ cs, q[0-9]+, q[0-9]+\n} 6 } } */ +/* { dg-final { scan-assembler-times {\tvcmp.u[0-9]+ hi, q[0-9]+, q[0-9]+\n} 6 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-vshr.c b/gcc/testsuite/gcc.target/arm/simd/mve-vshr.c index d4e658c..d4258e9 100644 --- a/gcc/testsuite/gcc.target/arm/simd/mve-vshr.c +++ b/gcc/testsuite/gcc.target/arm/simd/mve-vshr.c @@ -55,5 +55,12 @@ FUNC_IMM(u, uint, 8, 16, >>, vshrimm) /* MVE has only 128-bit vectors, so we can vectorize only half of the functions above. */ +/* Vector right shifts use vneg and left shifts. */ +/* { dg-final { scan-assembler-times {vshl.s[0-9]+\tq[0-9]+, q[0-9]+} 3 } } */ +/* { dg-final { scan-assembler-times {vshl.u[0-9]+\tq[0-9]+, q[0-9]+} 3 } } */ +/* { dg-final { scan-assembler-times {vneg.s[0-9]+ q[0-9]+, q[0-9]+} 6 } } */ + + +/* Shift by immediate. */ /* { dg-final { scan-assembler-times {vshr.s[0-9]+\tq[0-9]+, q[0-9]+} 3 } } */ /* { dg-final { scan-assembler-times {vshr.u[0-9]+\tq[0-9]+, q[0-9]+} 3 } } */ diff --git a/gcc/testsuite/gcc.target/arm/simd/mve-vsub_1.c b/gcc/testsuite/gcc.target/arm/simd/mve-vsub_1.c index 842e5c6..5a6c345 100644 --- a/gcc/testsuite/gcc.target/arm/simd/mve-vsub_1.c +++ b/gcc/testsuite/gcc.target/arm/simd/mve-vsub_1.c @@ -5,60 +5,42 @@ #include <stdint.h> -void test_vsub_i32 (int32_t * dest, int32_t * a, int32_t * b) { - int i; - for (i=0; i<4; i++) { - dest[i] = a[i] - b[i]; - } +#define FUNC(SIGN, TYPE, BITS, NB, OP, NAME) \ + void test_ ## NAME ##_ ## SIGN ## BITS ## x ## NB (TYPE##BITS##_t * __restrict__ dest, \ + TYPE##BITS##_t *a, TYPE##BITS##_t *b) { \ + int i; \ + for (i=0; i<NB; i++) { \ + dest[i] = a[i] OP b[i]; \ + } \ } -void test_vsub_i32_u (uint32_t * dest, uint32_t * a, uint32_t * b) { - int i; - for (i=0; i<4; i++) { - dest[i] = a[i] - b[i]; - } -} +/* 128-bit vectors. */ +FUNC(s, int, 32, 4, -, vsub) +FUNC(u, uint, 32, 4, -, vsub) +FUNC(s, int, 16, 8, -, vsub) +FUNC(u, uint, 16, 8, -, vsub) +FUNC(s, int, 8, 16, -, vsub) +FUNC(u, uint, 8, 16, -, vsub) /* { dg-final { scan-assembler-times {vsub\.i32\tq[0-9]+, q[0-9]+, q[0-9]+} 2 } } */ - -void test_vsub_i16 (int16_t * dest, int16_t * a, int16_t * b) { - int i; - for (i=0; i<8; i++) { - dest[i] = a[i] - b[i]; - } -} - -void test_vsub_i16_u (uint16_t * dest, uint16_t * a, uint16_t * b) { - int i; - for (i=0; i<8; i++) { - dest[i] = a[i] - b[i]; - } -} - /* { dg-final { scan-assembler-times {vsub\.i16\tq[0-9]+, q[0-9]+, q[0-9]+} 2 } } */ +/* { dg-final { scan-assembler-times {vsub\.i8\tq[0-9]+, q[0-9]+, q[0-9]+} 2 } } */ -void test_vsub_i8 (int8_t * dest, int8_t * a, int8_t * b) { - int i; - for (i=0; i<16; i++) { - dest[i] = a[i] - b[i]; - } -} - -void test_vsub_i8_u (uint8_t * dest, uint8_t * a, uint8_t * b) { +void test_vsub_f32 (float * dest, float * a, float * b) { int i; - for (i=0; i<16; i++) { + for (i=0; i<4; i++) { dest[i] = a[i] - b[i]; } } +/* { dg-final { scan-assembler-times {vsub\.f32\tq[0-9]+, q[0-9]+, q[0-9]+} 1 } } */ -/* { dg-final { scan-assembler-times {vsub\.i8\tq[0-9]+, q[0-9]+, q[0-9]+} 2 } } */ -void test_vsub_f32 (float * dest, float * a, float * b) { +void test_vsub_f16 (__fp16 * dest, __fp16 * a, __fp16 * b) { int i; - for (i=0; i<4; i++) { + for (i=0; i<8; i++) { dest[i] = a[i] - b[i]; } } -/* { dg-final { scan-assembler-times {vsub\.f32\tq[0-9]+, q[0-9]+, q[0-9]+} 1 } } */ +/* { dg-final { scan-assembler-times {vsub\.f16\tq[0-9]+, q[0-9]+, q[0-9]+} 1 } } */ diff --git a/gcc/testsuite/gcc.target/arm/unsigned-float.c b/gcc/testsuite/gcc.target/arm/unsigned-float.c index ad589d9..ea3abc7 100644 --- a/gcc/testsuite/gcc.target/arm/unsigned-float.c +++ b/gcc/testsuite/gcc.target/arm/unsigned-float.c @@ -1,8 +1,8 @@ /* { dg-do compile } */ -/* { dg-require-effective-target arm_fp_ok } */ -/* { dg-skip-if "need fp instructions" { *-*-* } { "-mfloat-abi=soft" } { "" } } */ /* { dg-skip-if "-mpure-code supports M-profile only" { *-*-* } { "-mpure-code" } } */ -/* { dg-options "-march=armv7-a -O1" } */ +/* { dg-options "-march=armv7-a+fp -mfpu=auto -O1" } */ +/* Do not require arm_ok effective target to avoid skipping on arm-eabi with + default configure options. */ /* { dg-add-options arm_fp } */ diff --git a/gcc/testsuite/gcc.target/i386/avx-pr82370.c b/gcc/testsuite/gcc.target/i386/avx-pr82370.c index 4dc8a5b..dc12dbf 100644 --- a/gcc/testsuite/gcc.target/i386/avx-pr82370.c +++ b/gcc/testsuite/gcc.target/i386/avx-pr82370.c @@ -4,7 +4,7 @@ /* { dg-final { scan-assembler-times "vpslld\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 3 } } */ /* { dg-final { scan-assembler-times "vpsllq\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 3 } } */ /* { dg-final { scan-assembler-times "vpsllw\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 3 } } */ -/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 3 } } */ +/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 6 } } */ /* { dg-final { scan-assembler-times "vpsraq\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 0 } } */ /* { dg-final { scan-assembler-times "vpsraw\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 3 } } */ /* { dg-final { scan-assembler-times "vpsrld\[ \t]\+\\\$5, %xmm\[0-9]\+, %xmm\[0-9]\+" 3 } } */ diff --git a/gcc/testsuite/gcc.target/i386/avx-pr94680.c b/gcc/testsuite/gcc.target/i386/avx-pr94680.c new file mode 100644 index 0000000..cb5041b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/avx-pr94680.c @@ -0,0 +1,107 @@ +/* { dg-do compile } */ +/* { dg-options "-mavx -mno-avx512f -O2" } */ +/* { dg-final { scan-assembler-times {(?n)vmov[a-z0-9]*[ \t]*%xmm[0-9]} 12 } } */ +/* { dg-final { scan-assembler-not "pxor" } } */ + +typedef float v8sf __attribute__((vector_size(32))); +typedef double v4df __attribute__ ((vector_size (32))); +typedef long long v4di __attribute__((vector_size(32))); +typedef int v8si __attribute__((vector_size(32))); +typedef short v16hi __attribute__ ((vector_size (32))); +typedef char v32qi __attribute__ ((vector_size (32))); + +v4df +foo_v4df (v4df x) +{ + return __builtin_shuffle (x, (v4df) { 0, 0, 0, 0 }, (v4di) { 0, 1, 4, 5 }); +} + +v4df +foo_v4df_l (v4df x) +{ + return __builtin_shuffle ((v4df) { 0, 0, 0, 0 }, x, (v4di) { 4, 5, 1, 2 }); +} + +v4di +foo_v4di (v4di x) +{ + return __builtin_shuffle (x, (v4di) { 0, 0, 0, 0 }, (v4di) { 0, 1, 4, 7 }); +} + +v4di +foo_v4di_l (v4di x) +{ + return __builtin_shuffle ((v4di) { 0, 0, 0, 0 }, x, (v4di) { 4, 5, 3, 1 }); +} + +v8sf +foo_v8sf (v8sf x) +{ + return __builtin_shuffle ((v8sf) { 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v8si) { 8, 9, 10, 11, 0, 1, 2, 3 }); +} + +v8sf +foo_v8sf_l (v8sf x) +{ + return __builtin_shuffle (x, (v8sf) { 0, 0, 0, 0, 0, 0, 0, 0 }, + (v8si) { 0, 1, 2, 3, 8, 9, 10, 11 }); +} + +v8si +foo_v8si (v8si x) +{ + return __builtin_shuffle (x, (v8si) { 0, 0, 0, 0, 0, 0, 0, 0 }, + (v8si) { 0, 1, 2, 3, 13, 12, 11, 15 }); +} + +v8si +foo_v8si_l (v8si x) +{ + return __builtin_shuffle ((v8si) { 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v8si) { 8, 9, 10, 11, 7, 6, 5, 4 }); +} + +v16hi +foo_v16hi (v16hi x) +{ + return __builtin_shuffle (x, (v16hi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, + (v16hi) { 0, 1, 2, 3, 4, 5, 6, 7, + 24, 17, 26, 19, 28, 21, 30, 23 }); +} + +v16hi +foo_v16hi_l (v16hi x) +{ + return __builtin_shuffle ((v16hi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v16hi) { 16, 17, 18, 19, 20, 21, 22, 23, + 15, 0, 13, 2, 11, 4, 9, 6 }); +} + +v32qi +foo_v32qi (v32qi x) +{ + return __builtin_shuffle (x, (v32qi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, + (v32qi) { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 32, 49, 34, 58, 36, 53, 38, 39, + 40, 60, 42, 43, 63, 45, 46, 47 }); +} + +v32qi +foo_v32qi_l (v32qi x) +{ + return __builtin_shuffle ((v32qi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v32qi) { 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 31, 0, 29, 2, 27, 4, 25, 6, + 23, 8, 21, 10, 19, 12, 17, 14 }); +} diff --git a/gcc/testsuite/gcc.target/i386/avx-psraq-1.c b/gcc/testsuite/gcc.target/i386/avx-psraq-1.c new file mode 100644 index 0000000..2722088 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/avx-psraq-1.c @@ -0,0 +1,13 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -mavx -mno-avx2" } */ +/* { dg-require-effective-target avx } */ + +#ifndef CHECK_H +#define CHECK_H "avx-check.h" +#endif + +#ifndef TEST +#define TEST avx_test +#endif + +#include "sse2-psraq-1.c" diff --git a/gcc/testsuite/gcc.target/i386/avx2-pr82370.c b/gcc/testsuite/gcc.target/i386/avx2-pr82370.c index 6609ebb..df3dfd8 100644 --- a/gcc/testsuite/gcc.target/i386/avx2-pr82370.c +++ b/gcc/testsuite/gcc.target/i386/avx2-pr82370.c @@ -4,7 +4,7 @@ /* { dg-final { scan-assembler-times "vpslld\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllq\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllw\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ -/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ +/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 2 } } */ /* { dg-final { scan-assembler-times "vpsraq\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 0 } } */ /* { dg-final { scan-assembler-times "vpsraw\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsrld\[ \t]\+\\\$5, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ @@ -13,7 +13,7 @@ /* { dg-final { scan-assembler-times "vpslld\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllq\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllw\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ -/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ +/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 2 } } */ /* { dg-final { scan-assembler-times "vpsraq\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 0 } } */ /* { dg-final { scan-assembler-times "vpsraw\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsrld\[ \t]\+\\\$5, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/avx2-pr99908.c b/gcc/testsuite/gcc.target/i386/avx2-pr99908.c new file mode 100644 index 0000000..2775f3b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/avx2-pr99908.c @@ -0,0 +1,25 @@ +/* PR target/99908 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -mavx2 -masm=att" } */ +/* { dg-final { scan-assembler-times "\tvpblendvb\t" 2 } } */ +/* { dg-final { scan-assembler-not "\tvpcmpeq" } } */ +/* { dg-final { scan-assembler-not "\tvpandn" } } */ + +#include <x86intrin.h> + +__m256i +f1 (__m256i a, __m256i b, __m256i mask) +{ + return _mm256_blendv_epi8(a, b, + _mm256_andnot_si256(mask, _mm256_set1_epi8(255))); +} + +__m256i +f2 (__v32qi x, __v32qi a, __v32qi b) +{ + x ^= (__v32qi) { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; + return _mm256_blendv_epi8 ((__m256i) a, (__m256i) b, (__m256i) x); +} diff --git a/gcc/testsuite/gcc.target/i386/avx2-psraq-1.c b/gcc/testsuite/gcc.target/i386/avx2-psraq-1.c new file mode 100644 index 0000000..e9051bf --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/avx2-psraq-1.c @@ -0,0 +1,51 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -mavx2 -mno-avx512f" } */ +/* { dg-require-effective-target avx2 } */ + +#ifndef CHECK_H +#define CHECK_H "avx2-check.h" +#endif + +#ifndef TEST +#define TEST avx2_test +#endif + +#include CHECK_H + +typedef long long V __attribute__((vector_size (32))); + +#define TESTN(N) \ +static V \ +__attribute__((noipa)) \ +test##N (V x) \ +{ \ + return x >> N; \ +} + +#define TESTS TESTN (63) TESTN (49) TESTN (32) TESTN (31) TESTN (18) +TESTS + +struct +{ + int n; + V (*fn) (V); +} tests[] = { +#undef TESTN +#define TESTN(N) { N, test##N }, + TESTS +}; + +static void +TEST (void) +{ + V a = (V) { 0xdeadbeefcafebabeULL, 0x123456789abcdef0ULL, + 0x173a74be8a95134cULL, 0x817bae35ac0ebf12ULL }; + int i; + for (i = 0; tests[i].n; i++) + { + V c = tests[i].fn (a); + if (c[0] != a[0] >> tests[i].n || c[1] != a[1] >> tests[i].n + || c[2] != a[2] >> tests[i].n || c[3] != a[3] >> tests[i].n) + abort (); + } +} diff --git a/gcc/testsuite/gcc.target/i386/avx512bw-pr82370.c b/gcc/testsuite/gcc.target/i386/avx512bw-pr82370.c index 174f499..12c3b27 100644 --- a/gcc/testsuite/gcc.target/i386/avx512bw-pr82370.c +++ b/gcc/testsuite/gcc.target/i386/avx512bw-pr82370.c @@ -4,7 +4,7 @@ /* { dg-final { scan-assembler-times "vpslld\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllq\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllw\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ -/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ +/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 2 } } */ /* { dg-final { scan-assembler-times "vpsraq\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 0 } } */ /* { dg-final { scan-assembler-times "vpsraw\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsrld\[ \t]\+\\\$5, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ @@ -13,7 +13,7 @@ /* { dg-final { scan-assembler-times "vpslld\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllq\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllw\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ -/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ +/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 2 } } */ /* { dg-final { scan-assembler-times "vpsraq\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 0 } } */ /* { dg-final { scan-assembler-times "vpsraw\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsrld\[ \t]\+\\\$5, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/avx512f-pr82370.c b/gcc/testsuite/gcc.target/i386/avx512f-pr82370.c index 20ad8dc..b179f9b 100644 --- a/gcc/testsuite/gcc.target/i386/avx512f-pr82370.c +++ b/gcc/testsuite/gcc.target/i386/avx512f-pr82370.c @@ -4,7 +4,7 @@ /* { dg-final { scan-assembler-times "vpslld\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllq\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllw\[ \t]\+\\\$7, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ -/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ +/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 2 } } */ /* { dg-final { scan-assembler-times "vpsraq\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 0 } } */ /* { dg-final { scan-assembler-times "vpsraw\[ \t]\+\\\$3, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsrld\[ \t]\+\\\$5, %xmm\[0-9]\+, %xmm\[0-9]\+" 1 } } */ @@ -13,7 +13,7 @@ /* { dg-final { scan-assembler-times "vpslld\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllq\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ /* { dg-final { scan-assembler-times "vpsllw\[ \t]\+\\\$7, %ymm\[0-9]\+, %ymm\[0-9]\+" 3 } } */ -/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ +/* { dg-final { scan-assembler-times "vpsrad\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 2 } } */ /* { dg-final { scan-assembler-times "vpsraq\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 0 } } */ /* { dg-final { scan-assembler-times "vpsraw\[ \t]\+\\\$3, %ymm\[0-9]\+, %ymm\[0-9]\+" 3 } } */ /* { dg-final { scan-assembler-times "vpsrld\[ \t]\+\\\$5, %ymm\[0-9]\+, %ymm\[0-9]\+" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/avx512f-pr94680.c b/gcc/testsuite/gcc.target/i386/avx512f-pr94680.c new file mode 100644 index 0000000..c27431a --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/avx512f-pr94680.c @@ -0,0 +1,144 @@ +/* { dg-do compile } */ +/* { dg-options "-mavx512bw -mavx512vbmi -O2" } */ +/* { dg-final { scan-assembler-times {(?n)vmov[a-z0-9]*[ \t]*%ymm[0-9]} 12} } */ +/* { dg-final { scan-assembler-not "pxor" } } */ + + +typedef float v16sf __attribute__((vector_size(64))); +typedef double v8df __attribute__ ((vector_size (64))); +typedef long long v8di __attribute__((vector_size(64))); +typedef int v16si __attribute__((vector_size(64))); +typedef short v32hi __attribute__ ((vector_size (64))); +typedef char v64qi __attribute__ ((vector_size (64))); + +v8df +foo_v8df (v8df x) +{ + return __builtin_shuffle (x, (v8df) { 0, 0, 0, 0, 0, 0, 0, 0 }, + (v8di) { 0, 1, 2, 3, 15, 14, 10, 11 }); +} + +v8df +foo_v8df_l (v8df x) +{ + return __builtin_shuffle ((v8df) { 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v8di) { 8, 9, 10, 11, 0, 1, 2, 3 }); +} + +v8di +foo_v8di (v8di x) +{ + return __builtin_shuffle (x, (v8di) { 0, 0, 0, 0, 0, 0, 0, 0 }, + (v8di) { 0, 1, 2, 3, 8, 9, 10, 11 }); +} + +v8di +foo_v8di_l (v8di x) +{ + return __builtin_shuffle ((v8di) { 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v8di) { 8, 9, 10, 11, 7, 6, 5, 4 }); +} + +v16sf +foo_v16sf (v16sf x) +{ + return __builtin_shuffle (x, (v16sf) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, + (v16si) { 0, 1, 2, 3, 4, 5, 6, 7, + 16, 17, 18, 19, 20, 21, 22, 23 }); +} + +v16sf +foo_v16sf_l (v16sf x) +{ + return __builtin_shuffle ((v16sf) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v16si) { 16, 17, 18, 19, 20, 21, 22, 23, + 0, 15, 2, 13, 4, 11, 6, 9 }); +} + +v16si +foo_v16si (v16si x) +{ + return __builtin_shuffle (x, (v16si) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, + (v16si) { 0, 1, 2, 3, 4, 5, 6, 7, + 31, 30, 29, 28, 20, 21, 22, 23 }); +} + +v16si +foo_v16si_l (v16si x) +{ + return __builtin_shuffle ((v16si) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v16si) { 16, 17, 18, 19, 20, 21, 22, 23, + 15, 0, 13, 2, 11, 4, 9, 6 }); +} + +v32hi +foo_v32hi (v32hi x) +{ + return __builtin_shuffle (x, (v32hi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, + (v32hi) { 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 63, 33, 61, 35, 59, 37, 57, 39, + 55, 41, 53, 43, 51, 45, 49, 47 }); +} + +v32hi +foo_v32hi_l (v32hi x) +{ + return __builtin_shuffle ((v32hi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v32hi) { 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, + 31, 0, 29, 2, 27, 4, 25, 6, + 23, 8, 21, 10, 19, 12, 17, 14 }); +} + +v64qi +foo_v64qi (v64qi x) +{ + return __builtin_shuffle (x, (v64qi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, + (v64qi) {0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, + 64, 127, 66, 125, 68, 123, 70, 121, + 72, 119, 74, 117, 76, 115, 78, 113, + 80, 111, 82, 109, 84, 107, 86, 105, + 88, 103, 90, 101, 92, 99, 94, 97 }); +} + +v64qi +foo_v64qi_l (v64qi x) +{ + return __builtin_shuffle ((v64qi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v64qi) { 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 0, 63, 2, 61, 4, 59, 6, 57, + 8, 55, 10, 53, 12, 51, 14, 49, + 16, 47, 18, 45, 20, 43, 22, 41, + 24, 39, 26, 37, 28, 35, 30, 33 }); +} diff --git a/gcc/testsuite/gcc.target/i386/pr100342.c b/gcc/testsuite/gcc.target/i386/pr100342.c new file mode 100644 index 0000000..8e2ec82 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr100342.c @@ -0,0 +1,70 @@ +/* PR rtl-optimization/100342 */ +/* { dg-do run { target int128 } } */ +/* { dg-options "-O2 -fno-dse -fno-forward-propagate -Wno-psabi -mno-sse2" } */ + +#define SHL(x, y) ((x) << ((y) & (sizeof(x) * 8 - 1))) +#define SHR(x, y) ((x) >> ((y) & (sizeof(x) * 8 - 1))) +#define ROR(x, y) (SHR(x, y)) | (SHL(x, (sizeof(x) * 8 - (y)))) +#define SHLV(x, y) ((x) << ((y) & (sizeof((x)[0]) * 8 - 1))) +#define SHLSV(x, y) ((x) << ((y) & (sizeof((y)[0]) * 8 - 1))) +typedef unsigned char A; +typedef unsigned char __attribute__((__vector_size__ (8))) B; +typedef unsigned char __attribute__((__vector_size__ (16))) C; +typedef unsigned char __attribute__((__vector_size__ (32))) D; +typedef unsigned char __attribute__((__vector_size__ (64))) E; +typedef unsigned short F; +typedef unsigned short __attribute__((__vector_size__ (16))) G; +typedef unsigned int H; +typedef unsigned int __attribute__((__vector_size__ (32))) I; +typedef unsigned long long J; +typedef unsigned long long __attribute__((__vector_size__ (8))) K; +typedef unsigned long long __attribute__((__vector_size__ (32))) L; +typedef unsigned long long __attribute__((__vector_size__ (64))) M; +typedef unsigned __int128 N; +typedef unsigned __int128 __attribute__((__vector_size__ (16))) O; +typedef unsigned __int128 __attribute__((__vector_size__ (32))) P; +typedef unsigned __int128 __attribute__((__vector_size__ (64))) Q; +B v1; +D v2; +L v3; +K v4; +I v5; +O v6; + +B +foo (A a, C b, E c, F d, G e, H f, J g, M h, N i, P j, Q k) +{ + b &= (A) f; + k += a; + G l = e; + D m = v2 >= (A) (J) v1; + J r = a + g; + L n = v3 <= f; + k -= i / f; + l -= (A) g; + c |= (A) d; + b -= (A) i; + J o = ROR (__builtin_clz (r), a); + K p = v4 | f, q = v4 <= f; + P s = SHLV (SHLSV (__builtin_bswap64 (i), (P) (0 < j)) <= 0, j); + n += a <= r; + M t = (M) (a / SHLV (c, 0)) != __builtin_bswap64 (i); + I u = f - v5; + E v = (E) h + (E) t + (E) k; + D w = (union { D b[2]; }) { }.b[0] + ((union { E b; }) v).b[1] + m + (D) u + (D) n + (D) s; + C x = ((union { D b; }) w).b[1] + b + (C) l + (C) v6; + B y = ((union { C a; B b; }) x).b + ((union { C a; B b[2]; }) x).b[1] + (B) p + (B) q; + J z = i + o; + F z2 = z; + A z3 = z2; + return y + z3; +} + +int +main () +{ + B x = foo (0, (C) { }, (E) { }, 10, (G) { }, 4, 2, (M) { }, 123842323652213865LL, (P) { 1 }, (Q) { }); + if ((J) x != 0x2e2c2e2c2e2c2e30ULL) + __builtin_abort(); + return 0; +} diff --git a/gcc/testsuite/gcc.target/i386/pr100504.c b/gcc/testsuite/gcc.target/i386/pr100504.c new file mode 100644 index 0000000..2910dfb --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr100504.c @@ -0,0 +1,7 @@ +/* PR middle-end/100504 */ +/* { dg-do compile } */ + +__attribute__((target_clones(0))) +foo() +{ /* { dg-error ".target_clones. attribute argument not a string constant" } */ +} diff --git a/gcc/testsuite/gcc.target/i386/pr100549.c b/gcc/testsuite/gcc.target/i386/pr100549.c new file mode 100644 index 0000000..83bba3c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr100549.c @@ -0,0 +1,108 @@ +/* PR target/100549 */ +/* { dg-do compile } */ +/* { dg-options "-O -mavx2" } */ + +typedef char v16qi __attribute__ ((vector_size (16))); +typedef char v32qi __attribute__ ((vector_size (32))); +typedef short v8hi __attribute__ ((vector_size (16))); +typedef short v16hi __attribute__ ((vector_size (32))); +typedef int v4si __attribute__ ((vector_size (16))); +typedef int v8si __attribute__ ((vector_size (32))); +typedef long long v2di __attribute__ ((vector_size (16))); +typedef long long v4di __attribute__ ((vector_size (32))); + +v16qi +f1 (v16qi a) +{ + return __builtin_ia32_pcmpeqb128 (a, a); +} + +v8hi +f2 (v8hi a) +{ + return __builtin_ia32_pcmpeqw128 (a, a); +} + +v4si +f3 (v4si a) +{ + return __builtin_ia32_pcmpeqd128 (a, a); +} + +v2di +f4 (v2di a) +{ + return __builtin_ia32_pcmpeqq (a, a); +} + +v16qi +f5 (v16qi a) +{ + return __builtin_ia32_pcmpgtb128 (a, a); +} + +v8hi +f6 (v8hi a) +{ + return __builtin_ia32_pcmpgtw128 (a, a); +} + +v4si +f7 (v4si a) +{ + return __builtin_ia32_pcmpgtd128 (a, a); +} + +v2di +f8 (v2di a) +{ + return __builtin_ia32_pcmpgtq (a, a); +} + +v32qi +f9 (v32qi a) +{ + return __builtin_ia32_pcmpeqb256 (a, a); +} + +v16hi +f10 (v16hi a) +{ + return __builtin_ia32_pcmpeqw256 (a, a); +} + +v8si +f11 (v8si a) +{ + return __builtin_ia32_pcmpeqd256 (a, a); +} + +v4di +f12 (v4di a) +{ + return __builtin_ia32_pcmpeqq256 (a, a); +} + +v32qi +f13 (v32qi a) +{ + return __builtin_ia32_pcmpgtb256 (a, a); +} + +v16hi +f14 (v16hi a) +{ + return __builtin_ia32_pcmpgtw256 (a, a); +} + +v8si +f15 (v8si a) +{ + return __builtin_ia32_pcmpgtd256 (a, a); +} + +v4di +f16 (v4di a) +{ + return __builtin_ia32_pcmpgtq256 (a, a); +} diff --git a/gcc/testsuite/gcc.target/i386/pr100582.c b/gcc/testsuite/gcc.target/i386/pr100582.c new file mode 100644 index 0000000..9520fe7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr100582.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-O3 -mavx2" } */ + +typedef unsigned char v32qi __attribute__((vector_size(32))); + +v32qi +f2 (v32qi x, v32qi a, v32qi b) +{ + v32qi e; + for (int i = 0; i != 32; i++) + e[i] = x[i] ? a[i] : b[i]; + + return e; +} + +/* { dg-final { scan-assembler-times "pblendvb" 1 } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr98218-1.c b/gcc/testsuite/gcc.target/i386/pr98218-1.c index 48407da..9d6602c 100644 --- a/gcc/testsuite/gcc.target/i386/pr98218-1.c +++ b/gcc/testsuite/gcc.target/i386/pr98218-1.c @@ -1,4 +1,4 @@ -/* PR target/98522 */ +/* PR target/98218 */ /* { dg-do compile { target { ! ia32 } } } */ /* { dg-options "-O2 -msse2" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr98218-1a.c b/gcc/testsuite/gcc.target/i386/pr98218-1a.c index 3470c87..2610438 100644 --- a/gcc/testsuite/gcc.target/i386/pr98218-1a.c +++ b/gcc/testsuite/gcc.target/i386/pr98218-1a.c @@ -1,4 +1,4 @@ -/* PR target/98522 */ +/* PR target/98218 */ /* { dg-do compile { target { ! ia32 } } } */ /* { dg-options "-O2 -ftree-vectorize -msse2" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr98218-2.c b/gcc/testsuite/gcc.target/i386/pr98218-2.c index 0b71612..948bf4f 100644 --- a/gcc/testsuite/gcc.target/i386/pr98218-2.c +++ b/gcc/testsuite/gcc.target/i386/pr98218-2.c @@ -1,4 +1,4 @@ -/* PR target/98522 */ +/* PR target/98218 */ /* { dg-do compile { target { ! ia32 } } } */ /* { dg-options "-O2 -msse2" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr98218-2a.c b/gcc/testsuite/gcc.target/i386/pr98218-2a.c index 6afd0a4..73c7226 100644 --- a/gcc/testsuite/gcc.target/i386/pr98218-2a.c +++ b/gcc/testsuite/gcc.target/i386/pr98218-2a.c @@ -1,4 +1,4 @@ -/* PR target/98522 */ +/* PR target/98218 */ /* { dg-do compile { target { ! ia32 } } } */ /* { dg-options "-O2 -ftree-vectorize -msse2" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr98218-3.c b/gcc/testsuite/gcc.target/i386/pr98218-3.c index 83a8c29..1b40d0ce 100644 --- a/gcc/testsuite/gcc.target/i386/pr98218-3.c +++ b/gcc/testsuite/gcc.target/i386/pr98218-3.c @@ -1,4 +1,4 @@ -/* PR target/98522 */ +/* PR target/98218 */ /* { dg-do compile { target { ! ia32 } } } */ /* { dg-options "-O2 -msse2" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr98218-3a.c b/gcc/testsuite/gcc.target/i386/pr98218-3a.c index 272d54e..cf1d497 100644 --- a/gcc/testsuite/gcc.target/i386/pr98218-3a.c +++ b/gcc/testsuite/gcc.target/i386/pr98218-3a.c @@ -1,4 +1,4 @@ -/* PR target/98522 */ +/* PR target/98218 */ /* { dg-do compile { target { ! ia32 } } } */ /* { dg-options "-O2 -ftree-vectorize -msse2" } */ diff --git a/gcc/testsuite/gcc.target/i386/pr98218-4.c b/gcc/testsuite/gcc.target/i386/pr98218-4.c new file mode 100644 index 0000000..647bdb1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr98218-4.c @@ -0,0 +1,16 @@ +/* PR target/98218 */ +/* { dg-do compile { target { ! ia32 } } } */ +/* { dg-options "-O2 -msse2" } */ + +typedef unsigned int __attribute__((__vector_size__ (8))) v64u32; +typedef int __attribute__((__vector_size__ (8))) v64s32; +typedef float __attribute__((__vector_size__ (8))) v64f32; + +v64u32 tu (v64f32 a, v64f32 b) { return a > b; } +v64s32 ts (v64f32 a, v64f32 b) { return a > b; } +v64f32 fu (v64u32 a, v64u32 b) { return a > b; } +v64f32 fs (v64s32 a, v64s32 b) { return a > b; } +v64f32 ff (v64f32 a, v64f32 b) { return a > b; } + +/* { dg-final { scan-assembler-times "cmpltps" 3 } } */ +/* { dg-final { scan-assembler-times "pcmpgtd" 2 } } */ diff --git a/gcc/testsuite/gcc.target/i386/sse2-pr94680.c b/gcc/testsuite/gcc.target/i386/sse2-pr94680.c new file mode 100644 index 0000000..7e0ff9f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse2-pr94680.c @@ -0,0 +1,91 @@ +/* { dg-do compile } */ +/* { dg-options "-msse2 -mno-sse4.1 -O2" } */ +/* { dg-final { scan-assembler-times {(?n)(?:mov|psrldq).*%xmm[0-9]} 12 } } */ +/* { dg-final { scan-assembler-not "pxor" } } */ + +typedef float v4sf __attribute__((vector_size(16))); +typedef double v2df __attribute__ ((vector_size (16))); +typedef long long v2di __attribute__((vector_size(16))); +typedef int v4si __attribute__((vector_size(16))); +typedef short v8hi __attribute__ ((vector_size (16))); +typedef char v16qi __attribute__ ((vector_size (16))); + +v2df +foo_v2df (v2df x) +{ + return __builtin_shuffle (x, (v2df) { 0, 0 }, (v2di) {0, 2}); +} + +v2df +foo_v2df_l (v2df x) +{ + return __builtin_shuffle ((v2df) { 0, 0 }, x, (v2di) {3, 1}); +} + +v2di +foo_v2di (v2di x) +{ + return __builtin_shuffle (x, (v2di) { 0, 0 }, (v2di) {0, 3}); +} + +v2di +foo_v2di_l (v2di x) +{ + return __builtin_shuffle ((v2di) { 0, 0 }, x, (v2di) {3, 0}); +} + +v4sf +foo_v4sf (v4sf x) +{ + return __builtin_shuffle (x, (v4sf) { 0, 0, 0, 0 }, (v4si) {0, 1, 4, 5}); +} + +v4sf +foo_v4sf_l (v4sf x) +{ + return __builtin_shuffle ((v4sf) { 0, 0, 0, 0 }, x, (v4si) {4, 5, 3, 1}); +} + +v4si +foo_v4si (v4si x) +{ + return __builtin_shuffle (x, (v4si) { 0, 0, 0, 0 }, (v4si) {0, 1, 6, 7}); +} + +v4si +foo_v4si_l (v4si x) +{ + return __builtin_shuffle ((v4si) { 0, 0, 0, 0 }, x, (v4si) {4, 5, 1, 2}); +} + +v8hi +foo_v8hi (v8hi x) +{ + return __builtin_shuffle (x, (v8hi) { 0, 0, 0, 0, 0, 0, 0, 0 }, + (v8hi) { 0, 1, 2, 3, 8, 12, 10, 13 }); +} + +v8hi +foo_v8hi_l (v8hi x) +{ + return __builtin_shuffle ((v8hi) { 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v8hi) { 8, 9, 10, 11, 7, 6, 5, 4 }); +} + +v16qi +foo_v16qi (v16qi x) +{ + return __builtin_shuffle (x, (v16qi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, + (v16qi) {0, 1, 2, 3, 4, 5, 6, 7, + 16, 24, 18, 26, 20, 28, 22, 30 }); +} + +v16qi +foo_v16qi_l (v16qi x) +{ + return __builtin_shuffle ((v16qi) { 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 }, x, + (v16qi) { 16, 17, 18, 19, 20, 21, 22, 23, + 15, 0, 13, 2, 11, 4, 9, 6 }); +} diff --git a/gcc/testsuite/gcc.target/i386/sse2-psraq-1.c b/gcc/testsuite/gcc.target/i386/sse2-psraq-1.c new file mode 100644 index 0000000..9a08ee4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse2-psraq-1.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -msse2 -mno-sse3" } */ +/* { dg-require-effective-target sse2 } */ + +#ifndef CHECK_H +#define CHECK_H "sse2-check.h" +#endif + +#ifndef TEST +#define TEST sse2_test +#endif + +#include CHECK_H + +typedef long long V __attribute__((vector_size (16))); + +#define TESTN(N) \ +static V \ +__attribute__((noipa)) \ +test##N (V x) \ +{ \ + return x >> N; \ +} + +#define TESTS TESTN (63) TESTN (49) TESTN (32) TESTN (31) TESTN (18) +TESTS + +struct +{ + int n; + V (*fn) (V); +} tests[] = { +#undef TESTN +#define TESTN(N) { N, test##N }, + TESTS +}; + +static void +TEST (void) +{ + V a = (V) { 0xdeadbeefcafebabeULL, 0x123456789abcdef0ULL }; + V b = (V) { 0x173a74be8a95134cULL, 0x817bae35ac0ebf12ULL }; + int i; + for (i = 0; tests[i].n; i++) + { + V c = tests[i].fn (a); + if (c[0] != a[0] >> tests[i].n || c[1] != a[1] >> tests[i].n) + abort (); + c = tests[i].fn (b); + if (c[0] != b[0] >> tests[i].n || c[1] != b[1] >> tests[i].n) + abort (); + } +} diff --git a/gcc/testsuite/gcc.target/i386/sse4_1-pr99908.c b/gcc/testsuite/gcc.target/i386/sse4_1-pr99908.c new file mode 100644 index 0000000..c13e730 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse4_1-pr99908.c @@ -0,0 +1,23 @@ +/* PR target/99908 */ +/* { dg-do compile } */ +/* { dg-options "-O2 -msse4.1 -mno-avx -masm=att" } */ +/* { dg-final { scan-assembler-times "\tpblendvb\t" 2 } } */ +/* { dg-final { scan-assembler-not "\tpcmpeq" } } */ +/* { dg-final { scan-assembler-not "\tpandn" } } */ + +#include <x86intrin.h> + +__m128i +f1 (__m128i a, __m128i b, __m128i mask) +{ + return _mm_blendv_epi8(a, b, + _mm_andnot_si128(mask, _mm_set1_epi8(255))); +} + +__m128i +f2 (__v16qi x, __v16qi a, __v16qi b) +{ + x ^= (__v16qi) { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; + return _mm_blendv_epi8 ((__m128i) a, (__m128i) b, (__m128i) x); +} diff --git a/gcc/testsuite/gcc.target/i386/sse4_2-psraq-1.c b/gcc/testsuite/gcc.target/i386/sse4_2-psraq-1.c new file mode 100644 index 0000000..947b623 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/sse4_2-psraq-1.c @@ -0,0 +1,13 @@ +/* { dg-do run } */ +/* { dg-options "-O2 -msse4.2 -mno-avx" } */ +/* { dg-require-effective-target sse4 } */ + +#ifndef CHECK_H +#define CHECK_H "sse4_2-check.h" +#endif + +#ifndef TEST +#define TEST sse4_2_test +#endif + +#include "sse2-psraq-1.c" diff --git a/gcc/testsuite/gcc.target/powerpc/rop-1.c b/gcc/testsuite/gcc.target/powerpc/rop-1.c new file mode 100644 index 0000000..8cedcb6 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/rop-1.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mdejagnu-cpu=power10 -mrop-protect" } */ +/* { dg-require-effective-target powerpc_elfv2 } */ + +/* Verify that ROP-protect instructions are inserted when a + call is present. */ + +extern void foo (void); + +int bar () +{ + foo (); + return 5; +} + +/* { dg-final { scan-assembler-times {\mhashst\M} 1 } } */ +/* { dg-final { scan-assembler-times {\mhashchk\M} 1 } } */ diff --git a/gcc/testsuite/gcc.target/powerpc/rop-2.c b/gcc/testsuite/gcc.target/powerpc/rop-2.c new file mode 100644 index 0000000..c556952 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/rop-2.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mdejagnu-cpu=power10 -mrop-protect -mprivileged" } */ +/* { dg-require-effective-target powerpc_elfv2 } */ + +/* Verify that privileged ROP-protect instructions are inserted when a + call is present. */ + +extern void foo (void); + +int bar () +{ + foo (); + return 5; +} + +/* { dg-final { scan-assembler-times {\mhashstp\M} 1 } } */ +/* { dg-final { scan-assembler-times {\mhashchkp\M} 1 } } */ diff --git a/gcc/testsuite/gcc.target/powerpc/rop-3.c b/gcc/testsuite/gcc.target/powerpc/rop-3.c new file mode 100644 index 0000000..8d03792 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/rop-3.c @@ -0,0 +1,18 @@ +/* { dg-do run { target { power10_hw } } } */ +/* { dg-require-effective-target powerpc_elfv2 } */ +/* { dg-options "-O2 -mdejagnu-cpu=power10 -mrop-protect" } */ + +/* Verify that ROP-protect instructions execute correctly when a + call is present. */ + +void __attribute__((noipa)) foo () +{ + asm (""); +} + +int main () +{ + foo (); + return 0; +} + diff --git a/gcc/testsuite/gcc.target/powerpc/rop-4.c b/gcc/testsuite/gcc.target/powerpc/rop-4.c new file mode 100644 index 0000000..dcf47c6 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/rop-4.c @@ -0,0 +1,15 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mdejagnu-cpu=power10 -mrop-protect" } */ +/* { dg-require-effective-target powerpc_elfv2 } */ + +/* Verify that no ROP-protect instructions are inserted when no + call is present. */ + + +int bar () +{ + return 5; +} + +/* { dg-final { scan-assembler-not {\mhashst\M} } } */ +/* { dg-final { scan-assembler-not {\mhashchk\M} } } */ diff --git a/gcc/testsuite/gcc.target/powerpc/rop-5.c b/gcc/testsuite/gcc.target/powerpc/rop-5.c new file mode 100644 index 0000000..cf04ea9 --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/rop-5.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -mdejagnu-cpu=power10 -mrop-protect" } */ + +/* Verify that __ROP_PROTECT__ is predefined for -mrop-protect. */ + +int foo () +{ +#ifndef __ROP_PROTECT__ + __ROP_PROTECT__ macro is not defined when it should be +#endif + return 0; +} + diff --git a/gcc/testsuite/gfortran.dg/gomp/implicit-save.f90 b/gcc/testsuite/gfortran.dg/gomp/implicit-save.f90 new file mode 100644 index 0000000..2af9647 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/implicit-save.f90 @@ -0,0 +1,11 @@ +subroutine foo + integer :: n = 5, m = 7 + !$omp declare target to(n) + !$omp threadprivate (m) +end + +program main + integer :: i, j + !$omp declare target to(i) + !$omp threadprivate (j) +end diff --git a/gcc/testsuite/gfortran.dg/gomp/parallel-master-1.f90 b/gcc/testsuite/gfortran.dg/gomp/parallel-master-1.f90 new file mode 100644 index 0000000..2ccc18f --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/parallel-master-1.f90 @@ -0,0 +1,23 @@ +! { dg-additional-options "-fdump-tree-original" } + implicit none + integer :: k, p, s, r, nth, t, f + logical(kind=2) l2 + !$omp threadprivate (t) + + external bar + !$omp parallel master default(none) private (k) + call bar (k) + !$omp end parallel master + + !$omp parallel master private (p) firstprivate (f) if (parallel: l2) default(shared) & + !$omp& shared(s) reduction(+:r) num_threads (nth) proc_bind(spread) copyin(t) + ! + !$omp end parallel master +end + +! { dg-final { scan-tree-dump "omp parallel private\\(k\\) default\\(none\\)" "original" } } +! { dg-final { scan-tree-dump-times "#pragma omp master" 1 "original" } } +! { dg-final { scan-tree-dump "D.\[0-9\]+ = l2;" "original" } } +! { dg-final { scan-tree-dump "D.\[0-9\]+ = nth;" "original" } } +! { dg-final { scan-tree-dump "#pragma omp parallel private\\(p\\) firstprivate\\(f\\) shared\\(s\\) copyin\\(t\\) reduction\\(\\+:r\\) if\\(parallel:D.\[0-9\]+\\) num_threads\\(D.\[0-9\]+\\) default\\(shared\\) proc_bind\\(spread\\)" "original" } } + diff --git a/gcc/testsuite/gfortran.dg/gomp/parallel-master-2.f90 b/gcc/testsuite/gfortran.dg/gomp/parallel-master-2.f90 new file mode 100644 index 0000000..2e12de6 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/parallel-master-2.f90 @@ -0,0 +1,9 @@ + use iso_c_binding, only: c_intptr_t + implicit none (external, type) + integer, parameter :: omp_event_handle_kind = c_intptr_t + integer (kind=omp_event_handle_kind) :: x + !$omp parallel master default (none) ! { dg-message "enclosing 'parallel'" } + !$omp task detach (x) ! { dg-error "'x' not specified in enclosing 'parallel'" } + !$omp end task + !$omp end parallel master +end diff --git a/gcc/testsuite/gfortran.dg/gomp/workshare-59.f90 b/gcc/testsuite/gfortran.dg/gomp/workshare-59.f90 new file mode 100644 index 0000000..65d04c2 --- /dev/null +++ b/gcc/testsuite/gfortran.dg/gomp/workshare-59.f90 @@ -0,0 +1,26 @@ +! PR fortran/100633 + +module defined_assign + interface assignment(=) + module procedure work_assign + end interface + + contains + subroutine work_assign(a,b) + integer, intent(out) :: a + logical, intent(in) :: b(:) + end subroutine work_assign +end module defined_assign + +program omp_workshare + use defined_assign + + integer :: a + logical :: l(10) + l = .TRUE. + + !$omp workshare + a = l ! { dg-error "Expected intrinsic assignment in OMP WORKSHARE" } + !$omp end workshare + +end program omp_workshare diff --git a/gcc/testsuite/gnat.dg/lto21.adb b/gcc/testsuite/gnat.dg/lto21.adb index fe6fb273..f626697 100644 --- a/gcc/testsuite/gnat.dg/lto21.adb +++ b/gcc/testsuite/gnat.dg/lto21.adb @@ -1,5 +1,6 @@ -- { dg-do run } -- { dg-options "-O3 -flto" { target lto } } +-- { dg-prune-output "warning: using serial compilation" } with Lto21_Pkg1; with Lto21_Pkg2; use Lto21_Pkg2; diff --git a/gcc/testsuite/lib/lto.exp b/gcc/testsuite/lib/lto.exp index 94a81ff..77919e8 100644 --- a/gcc/testsuite/lib/lto.exp +++ b/gcc/testsuite/lib/lto.exp @@ -159,6 +159,9 @@ proc lto_prune_warns { text } { regsub -all "(^|\n)\[ \t\]*\[\(\]file \[^\n\]* value=\[^\n\]*; file \[^\n\]* value=\[^\n\]*\[)\];" $text "" text regsub -all "(^|\n)\[ \t\]*\[^\n\]* definition taken" $text "" text + # Ignore missing jobserver for tests that do more than 1 LTRANS unit + regsub -all "(^|\n)\[^\n\]*: warning: using serial compilation of \[^\n\]*" $text "" text + # Ignore informational notes. regsub -all "(^|\n)\[^\n\]*: note: \[^\n\]*" $text "" text diff --git a/gcc/testsuite/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp index 5700c23..8192da5 100644 --- a/gcc/testsuite/lib/target-supports.exp +++ b/gcc/testsuite/lib/target-supports.exp @@ -4180,24 +4180,24 @@ proc add_options_for_arm_simd32 { flags } { return "$flags $et_arm_simd32_flags" } -# Return 1 if this is an ARM target supporting the saturation intrinsics -# from arm_acle.h. Some multilibs may be incompatible with these options. -# Also set et_arm_qbit_flags to the best options to add. -# arm_acle.h includes stdint.h which can cause trouble with incompatible -# -mfloat-abi= options. - -proc check_effective_target_arm_qbit_ok_nocache { } { - global et_arm_qbit_flags - set et_arm_qbit_flags "" - foreach flags {"" "-march=armv5te" "-march=armv5te -mfloat-abi=softfp" "-march=armv5te -mfloat-abi=hard"} { - if { [check_no_compiler_messages_nocache et_arm_qbit_flags object { +# Return 1 if this is an ARM target supporting the __ssat and __usat +# saturation intrinsics from arm_acle.h. Some multilibs may be +# incompatible with these options. Also set et_arm_sat_flags to the +# best options to add. arm_acle.h includes stdint.h which can cause +# trouble with incompatible -mfloat-abi= options. + +proc check_effective_target_arm_sat_ok_nocache { } { + global et_arm_sat_flags + set et_arm_sat_flags "" + foreach flags {"" "-march=armv6" "-march=armv6 -mfloat-abi=softfp" "-march=armv6 -mfloat-abi=hard -mfpu=vfp"} { + if { [check_no_compiler_messages_nocache et_arm_sat_flags object { #include <arm_acle.h> int dummy; - #ifndef __ARM_FEATURE_QBIT - #error not QBIT + #ifndef __ARM_FEATURE_SAT + #error not SAT #endif } "$flags"] } { - set et_arm_qbit_flags $flags + set et_arm_sat_flags $flags return 1 } } @@ -4205,17 +4205,17 @@ proc check_effective_target_arm_qbit_ok_nocache { } { return 0 } -proc check_effective_target_arm_qbit_ok { } { - return [check_cached_effective_target et_arm_qbit_flags \ - check_effective_target_arm_qbit_ok_nocache] +proc check_effective_target_arm_sat_ok { } { + return [check_cached_effective_target et_arm_sat_flags \ + check_effective_target_arm_sat_ok_nocache] } -proc add_options_for_arm_qbit { flags } { - if { ! [check_effective_target_arm_qbit_ok] } { +proc add_options_for_arm_sat { flags } { + if { ! [check_effective_target_arm_sat_ok] } { return "$flags" } - global et_arm_qbit_flags - return "$flags $et_arm_qbit_flags" + global et_arm_sat_flags + return "$flags $et_arm_sat_flags" } # Return 1 if this is an ARM target supporting the DSP intrinsics from diff --git a/gcc/tree-ssa-dom.c b/gcc/tree-ssa-dom.c index 11b86b2..075b1cc 100644 --- a/gcc/tree-ssa-dom.c +++ b/gcc/tree-ssa-dom.c @@ -649,7 +649,6 @@ private: void test_for_singularity (gimple *, avail_exprs_stack *); - dom_jump_threader_simplifier *m_simplifier; jump_threader *m_threader; evrp_range_analyzer *m_evrp_range_analyzer; }; diff --git a/gcc/tree-ssa-reassoc.c b/gcc/tree-ssa-reassoc.c index 359367c..32e1632 100644 --- a/gcc/tree-ssa-reassoc.c +++ b/gcc/tree-ssa-reassoc.c @@ -593,6 +593,36 @@ add_repeat_to_ops_vec (vec<operand_entry *> *ops, tree op, reassociate_stats.pows_encountered++; } +/* Returns true if we can associate the SSA def OP. */ + +static bool +can_reassociate_op_p (tree op) +{ + if (TREE_CODE (op) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op)) + return false; + /* Make sure asm goto outputs do not participate in reassociation since + we have no way to find an insertion place after asm goto. */ + if (TREE_CODE (op) == SSA_NAME + && gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_ASM + && gimple_asm_nlabels (as_a <gasm *> (SSA_NAME_DEF_STMT (op))) != 0) + return false; + return true; +} + +/* Returns true if we can reassociate operations of TYPE. + That is for integral or non-saturating fixed-point types, and for + floating point type when associative-math is enabled. */ + +static bool +can_reassociate_type_p (tree type) +{ + if ((ANY_INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type)) + || NON_SAT_FIXED_POINT_TYPE_P (type) + || (flag_associative_math && FLOAT_TYPE_P (type))) + return true; + return false; +} + /* Return true if STMT is reassociable operation containing a binary operation with tree code CODE, and is inside LOOP. */ @@ -613,12 +643,8 @@ is_reassociable_op (gimple *stmt, enum tree_code code, class loop *loop) { tree rhs1 = gimple_assign_rhs1 (stmt); tree rhs2 = gimple_assign_rhs2 (stmt); - if (TREE_CODE (rhs1) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs1)) - return false; - if (rhs2 - && TREE_CODE (rhs2) == SSA_NAME - && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs2)) + if (!can_reassociate_op_p (rhs1) + || (rhs2 && !can_reassociate_op_p (rhs2))) return false; return true; } @@ -5887,29 +5913,6 @@ repropagate_negates (void) } } -/* Returns true if OP is of a type for which we can do reassociation. - That is for integral or non-saturating fixed-point types, and for - floating point type when associative-math is enabled. */ - -static bool -can_reassociate_p (tree op) -{ - tree type = TREE_TYPE (op); - if (TREE_CODE (op) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (op)) - return false; - /* Make sure asm goto outputs do not participate in reassociation since - we have no way to find an insertion place after asm goto. */ - if (TREE_CODE (op) == SSA_NAME - && gimple_code (SSA_NAME_DEF_STMT (op)) == GIMPLE_ASM - && gimple_asm_nlabels (as_a <gasm *> (SSA_NAME_DEF_STMT (op))) != 0) - return false; - if ((ANY_INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type)) - || NON_SAT_FIXED_POINT_TYPE_P (type) - || (flag_associative_math && FLOAT_TYPE_P (type))) - return true; - return false; -} - /* Break up subtract operations in block BB. We do this top down because we don't know whether the subtract is @@ -5942,14 +5945,15 @@ break_up_subtract_bb (basic_block bb) gimple_set_uid (stmt, uid++); if (!is_gimple_assign (stmt) - || !can_reassociate_p (gimple_assign_lhs (stmt))) + || !can_reassociate_type_p (TREE_TYPE (gimple_assign_lhs (stmt))) + || !can_reassociate_op_p (gimple_assign_lhs (stmt))) continue; /* Look for simple gimple subtract operations. */ if (gimple_assign_rhs_code (stmt) == MINUS_EXPR) { - if (!can_reassociate_p (gimple_assign_rhs1 (stmt)) - || !can_reassociate_p (gimple_assign_rhs2 (stmt))) + if (!can_reassociate_op_p (gimple_assign_rhs1 (stmt)) + || !can_reassociate_op_p (gimple_assign_rhs2 (stmt))) continue; /* Check for a subtract used only in an addition. If this @@ -5960,7 +5964,7 @@ break_up_subtract_bb (basic_block bb) break_up_subtract (stmt, &gsi); } else if (gimple_assign_rhs_code (stmt) == NEGATE_EXPR - && can_reassociate_p (gimple_assign_rhs1 (stmt))) + && can_reassociate_op_p (gimple_assign_rhs1 (stmt))) plus_negates.safe_push (gimple_assign_lhs (stmt)); } for (son = first_dom_son (CDI_DOMINATORS, bb); @@ -6553,14 +6557,14 @@ reassociate_bb (basic_block bb) /* For non-bit or min/max operations we can't associate all types. Verify that here. */ - if (rhs_code != BIT_IOR_EXPR - && rhs_code != BIT_AND_EXPR - && rhs_code != BIT_XOR_EXPR - && rhs_code != MIN_EXPR - && rhs_code != MAX_EXPR - && (!can_reassociate_p (lhs) - || !can_reassociate_p (rhs1) - || !can_reassociate_p (rhs2))) + if ((rhs_code != BIT_IOR_EXPR + && rhs_code != BIT_AND_EXPR + && rhs_code != BIT_XOR_EXPR + && rhs_code != MIN_EXPR + && rhs_code != MAX_EXPR + && !can_reassociate_type_p (TREE_TYPE (lhs))) + || !can_reassociate_op_p (rhs1) + || !can_reassociate_op_p (rhs2)) continue; if (associative_tree_code (rhs_code)) diff --git a/gcc/tree-ssa-sccvn.c b/gcc/tree-ssa-sccvn.c index e54a0c9..e876121 100644 --- a/gcc/tree-ssa-sccvn.c +++ b/gcc/tree-ssa-sccvn.c @@ -4529,7 +4529,8 @@ dominated_by_p_w_unex (basic_block bb1, basic_block bb2, bool allow_back) /* Iterate to the single executable bb2 successor. */ edge succe = NULL; FOR_EACH_EDGE (e, ei, bb2->succs) - if (e->flags & EDGE_EXECUTABLE) + if ((e->flags & EDGE_EXECUTABLE) + || (!allow_back && (e->flags & EDGE_DFS_BACK))) { if (succe) { @@ -4547,7 +4548,8 @@ dominated_by_p_w_unex (basic_block bb1, basic_block bb2, bool allow_back) { FOR_EACH_EDGE (e, ei, succe->dest->preds) if (e != succe - && (e->flags & EDGE_EXECUTABLE)) + && ((e->flags & EDGE_EXECUTABLE) + || (!allow_back && (e->flags & EDGE_DFS_BACK)))) { succe = NULL; break; diff --git a/gcc/tree-ssa-uninit.c b/gcc/tree-ssa-uninit.c index 0800f59..f55ce19 100644 --- a/gcc/tree-ssa-uninit.c +++ b/gcc/tree-ssa-uninit.c @@ -209,6 +209,16 @@ check_defs (ao_ref *ref, tree vdef, void *data_) { check_defs_data *data = (check_defs_data *)data_; gimple *def_stmt = SSA_NAME_DEF_STMT (vdef); + + /* The ASAN_MARK intrinsic doesn't modify the variable. */ + if (is_gimple_call (def_stmt) + && gimple_call_internal_p (def_stmt, IFN_ASAN_MARK)) + return false; + + /* End of VLA scope is not a kill. */ + if (gimple_call_builtin_p (def_stmt, BUILT_IN_STACK_RESTORE)) + return false; + /* If this is a clobber then if it is not a kill walk past it. */ if (gimple_clobber_p (def_stmt)) { @@ -216,6 +226,7 @@ check_defs (ao_ref *ref, tree vdef, void *data_) return true; return false; } + /* Found a may-def on this path. */ data->found_may_defs = true; return true; @@ -12550,13 +12550,11 @@ array_at_struct_end_p (tree ref) || ! TYPE_MAX_VALUE (TYPE_DOMAIN (atype))) return true; - if (TREE_CODE (ref) == MEM_REF - && TREE_CODE (TREE_OPERAND (ref, 0)) == ADDR_EXPR) - ref = TREE_OPERAND (TREE_OPERAND (ref, 0), 0); - /* If the reference is based on a declared entity, the size of the array is constrained by its given domain. (Do not trust commons PR/69368). */ - if (DECL_P (ref) + ref = get_base_address (ref); + if (ref + && DECL_P (ref) && !(flag_unconstrained_commons && VAR_P (ref) && DECL_COMMON (ref)) && DECL_SIZE_UNIT (ref) diff --git a/libcpp/ChangeLog b/libcpp/ChangeLog index e1275e6..0d2c86f 100644 --- a/libcpp/ChangeLog +++ b/libcpp/ChangeLog @@ -1,3 +1,34 @@ +2021-05-12 Jakub Jelinek <jakub@redhat.com> + + PR preprocessor/100392 + * lex.c (cpp_directive_only_process): If buffer doesn't end with '\n', + add buffer->rlimit[0] character to the printed range and + CPP_INCREMENT_LINE and increment line_count. + +2021-05-11 Joseph Myers <joseph@codesourcery.com> + + * include/cpplib.h (struct cpp_options): Add elifdef. + * init.c (struct lang_flags): Add elifdef. + (lang_defaults): Update to include elifdef initializers. + (cpp_set_lang): Set elifdef for pfile based on language. + * directives.c (STDC2X, ELIFDEF): New macros. + (EXTENSION): Increase value to 3. + (DIRECTIVE_TABLE): Add #elifdef and #elifndef. + (_cpp_handle_directive): Do not treat ELIFDEF directives as + directives for language versions without the #elifdef feature. + (do_elif): Handle #elifdef and #elifndef. + (do_elifdef, do_elifndef): New functions. + +2021-05-11 Joseph Myers <joseph@codesourcery.com> + + * lex.c (cpp_avoid_paste): Do not allow pasting CPP_NUMBER with + CPP_CHAR. + +2021-05-11 Joseph Myers <joseph@codesourcery.com> + + * init.c (lang_defaults): Enable digit separators for GNUC2X and + STDC2X. + 2021-05-07 Jakub Jelinek <jakub@redhat.com> PR c/100450 diff --git a/libcpp/directives.c b/libcpp/directives.c index 795f93e..261a584 100644 --- a/libcpp/directives.c +++ b/libcpp/directives.c @@ -56,10 +56,12 @@ struct pragma_entry /* Values for the origin field of struct directive. KANDR directives come from traditional (K&R) C. STDC89 directives come from the - 1989 C standard. EXTENSION directives are extensions. */ + 1989 C standard. STDC2X directives come from the C2X standard. EXTENSION + directives are extensions. */ #define KANDR 0 #define STDC89 1 -#define EXTENSION 2 +#define STDC2X 2 +#define EXTENSION 3 /* Values for the flags field of struct directive. COND indicates a conditional; IF_COND an opening conditional. INCL means to treat @@ -67,13 +69,17 @@ struct pragma_entry means this directive should be handled even if -fpreprocessed is in effect (these are the directives with callback hooks). - EXPAND is set on directives that are always macro-expanded. */ + EXPAND is set on directives that are always macro-expanded. + + ELIFDEF is set on directives that are only handled for standards with the + #elifdef / #elifndef feature. */ #define COND (1 << 0) #define IF_COND (1 << 1) #define INCL (1 << 2) #define IN_I (1 << 3) #define EXPAND (1 << 4) #define DEPRECATED (1 << 5) +#define ELIFDEF (1 << 6) /* Defines one #-directive, including how to handle it. */ typedef void (*directive_handler) (cpp_reader *); @@ -148,6 +154,8 @@ static void cpp_pop_definition (cpp_reader *, struct def_pragma_macro *); D(undef, T_UNDEF, KANDR, IN_I) \ D(line, T_LINE, KANDR, EXPAND) \ D(elif, T_ELIF, STDC89, COND | EXPAND) \ + D(elifdef, T_ELIFDEF, STDC2X, COND | ELIFDEF) \ + D(elifndef, T_ELIFNDEF, STDC2X, COND | ELIFDEF) \ D(error, T_ERROR, STDC89, 0) \ D(pragma, T_PRAGMA, STDC89, IN_I) \ D(warning, T_WARNING, EXTENSION, 0) \ @@ -437,7 +445,11 @@ _cpp_handle_directive (cpp_reader *pfile, bool indented) if (dname->type == CPP_NAME) { if (dname->val.node.node->is_directive) - dir = &dtable[dname->val.node.node->directive_index]; + { + dir = &dtable[dname->val.node.node->directive_index]; + if ((dir->flags & ELIFDEF) && !CPP_OPTION (pfile, elifdef)) + dir = 0; + } } /* We do not recognize the # followed by a number extension in assembler code. */ @@ -2079,8 +2091,8 @@ do_else (cpp_reader *pfile) } } -/* Handle a #elif directive by not changing if_stack either. See the - comment above do_else. */ +/* Handle a #elif, #elifdef or #elifndef directive by not changing if_stack + either. See the comment above do_else. */ static void do_elif (cpp_reader *pfile) { @@ -2088,12 +2100,13 @@ do_elif (cpp_reader *pfile) struct if_stack *ifs = buffer->if_stack; if (ifs == NULL) - cpp_error (pfile, CPP_DL_ERROR, "#elif without #if"); + cpp_error (pfile, CPP_DL_ERROR, "#%s without #if", pfile->directive->name); else { if (ifs->type == T_ELSE) { - cpp_error (pfile, CPP_DL_ERROR, "#elif after #else"); + cpp_error (pfile, CPP_DL_ERROR, "#%s after #else", + pfile->directive->name); cpp_error_with_line (pfile, CPP_DL_ERROR, ifs->line, 0, "the conditional began here"); } @@ -2107,8 +2120,29 @@ do_elif (cpp_reader *pfile) pfile->state.skipping = 1; else { - pfile->state.skipping = ! _cpp_parse_expr (pfile, false); - ifs->skip_elses = ! pfile->state.skipping; + if (pfile->directive == &dtable[T_ELIF]) + pfile->state.skipping = !_cpp_parse_expr (pfile, false); + else + { + cpp_hashnode *node = lex_macro_node (pfile, false); + + if (node) + { + bool macro_defined = _cpp_defined_macro_p (node); + if (!_cpp_maybe_notify_macro_use (pfile, node, + pfile->directive_line)) + /* It wasn't a macro after all. */ + macro_defined = false; + bool skip = (pfile->directive == &dtable[T_ELIFDEF] + ? !macro_defined + : macro_defined); + if (pfile->cb.used) + pfile->cb.used (pfile, pfile->directive_line, node); + check_eol (pfile, false); + pfile->state.skipping = skip; + } + } + ifs->skip_elses = !pfile->state.skipping; } /* Invalidate any controlling macro. */ @@ -2116,6 +2150,20 @@ do_elif (cpp_reader *pfile) } } +/* Handle a #elifdef directive. */ +static void +do_elifdef (cpp_reader *pfile) +{ + do_elif (pfile); +} + +/* Handle a #elifndef directive. */ +static void +do_elifndef (cpp_reader *pfile) +{ + do_elif (pfile); +} + /* #endif pops the if stack and resets pfile->state.skipping. */ static void do_endif (cpp_reader *pfile) diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index 41d75d9..2cdaf19 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -497,6 +497,9 @@ struct cpp_options /* Nonzero for the '::' token. */ unsigned char scope; + /* Nonzero for the '#elifdef' and '#elifndef' directives. */ + unsigned char elifdef; + /* Nonzero means tokenize C++20 module directives. */ unsigned char module_directives; diff --git a/libcpp/init.c b/libcpp/init.c index 18a2341..d5d4e99 100644 --- a/libcpp/init.c +++ b/libcpp/init.c @@ -95,34 +95,35 @@ struct lang_flags char scope; char dfp_constants; char size_t_literals; + char elifdef; }; static const struct lang_flags lang_defaults[] = -{ /* c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt scope dfp szlit */ - /* GNUC89 */ { 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, - /* GNUC99 */ { 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, - /* GNUC11 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, - /* GNUC17 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, - /* GNUC2X */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0 }, - /* STDC89 */ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, - /* STDC94 */ { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, - /* STDC99 */ { 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, - /* STDC11 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, - /* STDC17 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, - /* STDC2X */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0 }, - /* GNUCXX */ { 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, - /* CXX98 */ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, - /* GNUCXX11 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0 }, - /* CXX11 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0 }, - /* GNUCXX14 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 }, - /* CXX14 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0 }, - /* GNUCXX17 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 }, - /* CXX17 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0 }, - /* GNUCXX20 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 }, - /* CXX20 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0 }, - /* GNUCXX23 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 }, - /* CXX23 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1 }, - /* ASM */ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } +{ /* c99 c++ xnum xid c11 std digr ulit rlit udlit bincst digsep trig u8chlit vaopt scope dfp szlit elifdef */ + /* GNUC89 */ { 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, + /* GNUC99 */ { 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, + /* GNUC11 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, + /* GNUC17 */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, + /* GNUC2X */ { 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1 }, + /* STDC89 */ { 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + /* STDC94 */ { 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + /* STDC99 */ { 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + /* STDC11 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + /* STDC17 */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + /* STDC2X */ { 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1 }, + /* GNUCXX */ { 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, + /* CXX98 */ { 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, + /* GNUCXX11 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, + /* CXX11 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0 }, + /* GNUCXX14 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0 }, + /* CXX14 */ { 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0 }, + /* GNUCXX17 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0 }, + /* CXX17 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0 }, + /* GNUCXX20 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0 }, + /* CXX20 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0 }, + /* GNUCXX23 */ { 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0 }, + /* CXX23 */ { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0 }, + /* ASM */ { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } }; /* Sets internal flags correctly for a given language. */ @@ -151,6 +152,7 @@ cpp_set_lang (cpp_reader *pfile, enum c_lang lang) CPP_OPTION (pfile, scope) = l->scope; CPP_OPTION (pfile, dfp_constants) = l->dfp_constants; CPP_OPTION (pfile, size_t_literals) = l->size_t_literals; + CPP_OPTION (pfile, elifdef) = l->elifdef; } /* Initialize library global state. */ diff --git a/libcpp/lex.c b/libcpp/lex.c index b7ce85a..6fd722a 100644 --- a/libcpp/lex.c +++ b/libcpp/lex.c @@ -3725,6 +3725,7 @@ cpp_avoid_paste (cpp_reader *pfile, const cpp_token *token1, || b == CPP_NAME || b == CPP_CHAR || b == CPP_STRING); /* L */ case CPP_NUMBER: return (b == CPP_NUMBER || b == CPP_NAME + || b == CPP_CHAR || c == '.' || c == '+' || c == '-'); /* UCNs */ case CPP_OTHER: return ((token1->val.str.text[0] == '\\' @@ -4767,7 +4768,18 @@ cpp_directive_only_process (cpp_reader *pfile, } if (buffer->rlimit > base && !pfile->state.skipping) - cb (pfile, CPP_DO_print, data, line_count, base, buffer->rlimit - base); + { + const unsigned char *limit = buffer->rlimit; + /* If the file was not newline terminated, add rlimit, which is + guaranteed to point to a newline, to the end of our range. */ + if (limit[-1] != '\n') + { + limit++; + CPP_INCREMENT_LINE (pfile, 0); + line_count++; + } + cb (pfile, CPP_DO_print, data, line_count, base, limit - base); + } _cpp_pop_buffer (pfile); } diff --git a/libgcc/ChangeLog b/libgcc/ChangeLog index a0bf18b..d562765 100644 --- a/libgcc/ChangeLog +++ b/libgcc/ChangeLog @@ -1,3 +1,11 @@ +2021-05-13 Dimitar Dimitrov <dimitar@dinux.eu> + + * config/pru/mpyll.S (__pruabi_mpyll): Place into own section. + +2021-05-13 Martin Liska <mliska@suse.cz> + + * libgcov-driver.c: Fix GNU coding style. + 2021-05-10 Martin Liska <mliska@suse.cz> * libgcov-driver.c (gcov_version): Use different name that does diff --git a/libgcc/config/pru/mpyll.S b/libgcc/config/pru/mpyll.S index 1aa12a6..cd093bb 100644 --- a/libgcc/config/pru/mpyll.S +++ b/libgcc/config/pru/mpyll.S @@ -29,6 +29,8 @@ #include "pru-asm.h" + .section .text.__pruabi_mpyll, "ax" + .global SYM(__pruabi_mpyll) FUNC(__pruabi_mpyll) SYM(__pruabi_mpyll): diff --git a/libgcc/libgcov-driver.c b/libgcc/libgcov-driver.c index cbdf2ca..df7ccb2 100644 --- a/libgcc/libgcov-driver.c +++ b/libgcc/libgcov-driver.c @@ -558,10 +558,8 @@ read_fatal:; fn_buffer = free_fn_data (gi_ptr, fn_buffer, GCOV_COUNTERS); if ((error = gcov_close ())) - gcov_error (error < 0 ? - GCOV_PROF_PREFIX "Overflow writing\n" : - GCOV_PROF_PREFIX "Error writing\n", - gf->filename); + gcov_error ((error < 0 ? GCOV_PROF_PREFIX "Overflow writing\n" + : GCOV_PROF_PREFIX "Error writing\n"), gf->filename); } diff --git a/libgomp/ChangeLog b/libgomp/ChangeLog index d9d88ca..424c574 100644 --- a/libgomp/ChangeLog +++ b/libgomp/ChangeLog @@ -1,3 +1,28 @@ +2021-05-14 Tobias Burnus <tobias@codesourcery.com> + + * testsuite/libgomp.fortran/parallel-master.f90: New test. + +2021-05-13 Martin Liska <mliska@suse.cz> + + PR testsuite/100569 + * testsuite/libgomp.c/omp-nested-3.c: Prune new LTO warning. + * testsuite/libgomp.c/pr46032-2.c: Likewise. + * testsuite/libgomp.oacc-c-c++-common/data-clauses-kernels-ipa-pta.c: Likewise. + * testsuite/libgomp.oacc-c-c++-common/data-clauses-parallel-ipa-pta.c: Likewise. + +2021-05-12 Tobias Burnus <tobias@codesourcery.com> + + * testsuite/libgomp.c-c++-common/task-detach-12.c: New test. + * testsuite/libgomp.fortran/task-detach-12.f90: New test. + +2021-05-11 Jakub Jelinek <jakub@redhat.com> + + PR middle-end/100471 + * taskloop.c (GOMP_taskloop): If GOMP_TASK_FLAG_REDUCTION and not + GOMP_TASK_FLAG_NOGROUP, when doing early return clear the task + reduction pointer. + * testsuite/libgomp.c/task-reduction-4.c: New test. + 2021-05-07 Tobias Burnus <tobias@codesourcery.com> Tom de Vries <tdevries@suse.de> diff --git a/libgomp/testsuite/libgomp.c-c++-common/task-detach-12.c b/libgomp/testsuite/libgomp.c-c++-common/task-detach-12.c new file mode 100644 index 0000000..6583318 --- /dev/null +++ b/libgomp/testsuite/libgomp.c-c++-common/task-detach-12.c @@ -0,0 +1,19 @@ +/* { dg-do run } */ +/* { dg-options "-fopenmp" } */ + +#include <omp.h> + +int +main () +{ + struct S { int a[7]; } s = { { 1, 2, 3, 4, 5, 6, 7 } }; + omp_event_handle_t x; + #pragma omp parallel master + #pragma omp task firstprivate (s) detach (x) + { + if (s.a[3] != 4) + __builtin_abort (); + omp_fulfill_event (x); + } + return 0; +} diff --git a/libgomp/testsuite/libgomp.c/omp-nested-3.c b/libgomp/testsuite/libgomp.c/omp-nested-3.c index 7790c58..446e6bd 100644 --- a/libgomp/testsuite/libgomp.c/omp-nested-3.c +++ b/libgomp/testsuite/libgomp.c/omp-nested-3.c @@ -1,4 +1,5 @@ // { dg-do run { target lto } } // { dg-additional-options "-fipa-pta -flto -flto-partition=max" } +// { dg-prune-output "warning: using serial compilation" } #include "omp-nested-1.c" diff --git a/libgomp/testsuite/libgomp.c/pr46032-2.c b/libgomp/testsuite/libgomp.c/pr46032-2.c index 1125f6e..36f3730 100644 --- a/libgomp/testsuite/libgomp.c/pr46032-2.c +++ b/libgomp/testsuite/libgomp.c/pr46032-2.c @@ -1,4 +1,5 @@ /* { dg-do run { target lto } } */ /* { dg-options "-O2 -ftree-vectorize -std=c99 -fipa-pta -flto -flto-partition=max" } */ +/* { dg-prune-output "warning: using serial compilation" } */ #include "pr46032.c" diff --git a/libgomp/testsuite/libgomp.fortran/parallel-master.f90 b/libgomp/testsuite/libgomp.fortran/parallel-master.f90 new file mode 100644 index 0000000..1e30b48 --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/parallel-master.f90 @@ -0,0 +1,14 @@ +! { dg-additional-options "-fdump-tree-original" } +program main + use omp_lib + implicit none (type, external) + integer :: p, a(20) + !$omp parallel master num_threads(4) private (p) shared(a) + p = omp_get_thread_num (); + if (p /= 0) stop 1 + a = 0 + !$omp end parallel master +end + +! { dg-final { scan-tree-dump "#pragma omp parallel private\\(p\\) shared\\(a\\) num_threads\\(4\\)" "original"} } +! { dg-final { scan-tree-dump "#pragma omp master" "original"} } diff --git a/libgomp/testsuite/libgomp.fortran/task-detach-12.f90 b/libgomp/testsuite/libgomp.fortran/task-detach-12.f90 new file mode 100644 index 0000000..88546fe --- /dev/null +++ b/libgomp/testsuite/libgomp.fortran/task-detach-12.f90 @@ -0,0 +1,22 @@ +program test + use omp_lib + implicit none + integer(omp_event_handle_kind) :: oevent, ievent + integer :: i + integer, allocatable :: temp(:) + ALLOCATE(temp(5)) + + !$omp parallel num_threads(3) + !$omp single + DO i=1,5 + !$omp task firstprivate(i) firstprivate(temp) detach(oevent) + temp(:) = 0; + temp(1) = -1; + !print *,temp + call omp_fulfill_event(oevent) + !$omp end task + ENDDO + !$omp taskwait + !$omp end single + !$omp end parallel +end program diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/data-clauses-kernels-ipa-pta.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/data-clauses-kernels-ipa-pta.c index 2cd98bd..49c11ac 100644 --- a/libgomp/testsuite/libgomp.oacc-c-c++-common/data-clauses-kernels-ipa-pta.c +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/data-clauses-kernels-ipa-pta.c @@ -1,4 +1,5 @@ /* { dg-do run { target lto } } */ /* { dg-additional-options "-fipa-pta -flto -flto-partition=max" } */ +/* { dg-prune-output "warning: using serial compilation" } */ #include "data-clauses-kernels.c" diff --git a/libgomp/testsuite/libgomp.oacc-c-c++-common/data-clauses-parallel-ipa-pta.c b/libgomp/testsuite/libgomp.oacc-c-c++-common/data-clauses-parallel-ipa-pta.c index ddcf4e3..4d61d84 100644 --- a/libgomp/testsuite/libgomp.oacc-c-c++-common/data-clauses-parallel-ipa-pta.c +++ b/libgomp/testsuite/libgomp.oacc-c-c++-common/data-clauses-parallel-ipa-pta.c @@ -1,4 +1,5 @@ /* { dg-do run { target lto } } */ /* { dg-additional-options "-fipa-pta -flto -flto-partition=max" } */ +/* { dg-prune-output "warning: using serial compilation" } */ #include "data-clauses-parallel.c" diff --git a/libphobos/ChangeLog b/libphobos/ChangeLog index 62118c0..7b57d92 100644 --- a/libphobos/ChangeLog +++ b/libphobos/ChangeLog @@ -1,3 +1,12 @@ +2021-05-14 Bernd Edlinger <bernd.edlinger@hotmail.de> + + * src/std/process.d (unittest): Remove tmpname on exit. + * src/MERGE: Merge upstream phobos 63f4caa90. + +2021-05-13 Iain Buclaw <ibuclaw@gdcproject.org> + + * libdruntime/MERGE: Merge upstream druntime 98c6ff0c. + 2021-05-10 Iain Buclaw <ibuclaw@gdcproject.org> * src/MERGE: Merge upstream phobos 32cfe9b61. diff --git a/libphobos/libdruntime/MERGE b/libphobos/libdruntime/MERGE index 25cbb95..0d554e0 100644 --- a/libphobos/libdruntime/MERGE +++ b/libphobos/libdruntime/MERGE @@ -1,4 +1,4 @@ -89f870b76710a4cfa96f711bb5b14a7439c5c2a7 +98c6ff0cf1241a0cfac196bf8a0523b1d4ecd3ac The first line of this file holds the git revision number of the last merge done from the dlang/druntime repository. diff --git a/libphobos/libdruntime/core/sys/dragonflybsd/dlfcn.d b/libphobos/libdruntime/core/sys/dragonflybsd/dlfcn.d index 1d3812f..2c5d8d7 100644 --- a/libphobos/libdruntime/core/sys/dragonflybsd/dlfcn.d +++ b/libphobos/libdruntime/core/sys/dragonflybsd/dlfcn.d @@ -82,16 +82,13 @@ struct __dlfunc_arg { alias dlfunc_t = void function(__dlfunc_arg); -private template __externC(RT, P...) -{ - alias __externC = extern(C) RT function(P) nothrow @nogc @system; -} - /* XSI functions first. */ -static assert(is(typeof(&dlclose) == __externC!(int, void*))); -static assert(is(typeof(&dlerror) == __externC!(char*))); -static assert(is(typeof(&dlopen) == __externC!(void*, const char*, int))); -static assert(is(typeof(&dlsym) == __externC!(void*, void*, const char*))); +extern(C) { + static assert(is(typeof(&dlclose) == int function(void*))); + static assert(is(typeof(&dlerror) == char* function())); + static assert(is(typeof(&dlopen) == void* function(const scope char*, int))); + static assert(is(typeof(&dlsym) == void* function(void*, const scope char*))); +} void* fdlopen(int, int); int dladdr(const(void)*, Dl_info*); diff --git a/libphobos/libdruntime/core/sys/freebsd/dlfcn.d b/libphobos/libdruntime/core/sys/freebsd/dlfcn.d index fad9141..7baacfe 100644 --- a/libphobos/libdruntime/core/sys/freebsd/dlfcn.d +++ b/libphobos/libdruntime/core/sys/freebsd/dlfcn.d @@ -90,8 +90,8 @@ static if (__BSD_VISIBLE) extern(C) { static assert(is(typeof(&dlclose) == int function(void*))); static assert(is(typeof(&dlerror) == char* function())); - static assert(is(typeof(&dlopen) == void* function(in char*, int))); - static assert(is(typeof(&dlsym) == void* function(void*, in char*))); + static assert(is(typeof(&dlopen) == void* function(const scope char*, int))); + static assert(is(typeof(&dlsym) == void* function(void*, const scope char*))); } static if (__BSD_VISIBLE) diff --git a/libphobos/libdruntime/core/sys/netbsd/dlfcn.d b/libphobos/libdruntime/core/sys/netbsd/dlfcn.d index dbbcc76..468ffbf 100644 --- a/libphobos/libdruntime/core/sys/netbsd/dlfcn.d +++ b/libphobos/libdruntime/core/sys/netbsd/dlfcn.d @@ -87,16 +87,13 @@ static if (__BSD_VISIBLE) } } -private template __externC(RT, P...) -{ - alias __externC = extern(C) RT function(P) nothrow @nogc; -} - /* XSI functions first. */ -static assert(is(typeof(&dlclose) == __externC!(int, void*))); -static assert(is(typeof(&dlerror) == __externC!(char*))); -static assert(is(typeof(&dlopen) == __externC!(void*, const char*, int))); -static assert(is(typeof(&dlsym) == __externC!(void*, void*, const char*))); +extern(C) { + static assert(is(typeof(&dlclose) == int function(void*))); + static assert(is(typeof(&dlerror) == char* function())); + static assert(is(typeof(&dlopen) == void* function(const scope char*, int))); + static assert(is(typeof(&dlsym) == void* function(void*, const scope char*))); +} static if (__BSD_VISIBLE) { diff --git a/libphobos/libdruntime/core/sys/posix/dlfcn.d b/libphobos/libdruntime/core/sys/posix/dlfcn.d index 2477e26..f6476ec 100644 --- a/libphobos/libdruntime/core/sys/posix/dlfcn.d +++ b/libphobos/libdruntime/core/sys/posix/dlfcn.d @@ -158,8 +158,8 @@ else version (FreeBSD) int dlclose(void*); char* dlerror(); - void* dlopen(in char*, int); - void* dlsym(void*, in char*); + void* dlopen(const scope char*, int); + void* dlsym(void*, const scope char*); int dladdr(const(void)* addr, Dl_info* info); struct Dl_info diff --git a/libphobos/src/MERGE b/libphobos/src/MERGE index 49622c5..ac709f9 100644 --- a/libphobos/src/MERGE +++ b/libphobos/src/MERGE @@ -1,4 +1,4 @@ -32cfe9b61570d52d9885b0208fd20de0d351b51e +63f4caa900e17c541042617b2fa187059b86bf88 The first line of this file holds the git revision number of the last merge done from the dlang/phobos repository. diff --git a/libphobos/src/std/process.d b/libphobos/src/std/process.d index 63ec493..1e977aa 100644 --- a/libphobos/src/std/process.d +++ b/libphobos/src/std/process.d @@ -2581,6 +2581,7 @@ private auto executeImpl(alias pipeFunc, Cmd, ExtraPipeFuncArgs...)( ReturnType!executeShell r; auto tmpname = uniqueTempPath; + scope(exit) if (exists(tmpname)) remove(tmpname); auto t = stderr; // Open a new scope to minimize code ran with stderr redirected. { diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index af12d4b..5833998 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,19 @@ +2021-05-13 Iain Sandoe <iain@sandoe.co.uk> + + * LOCAL_PATCHES: Add Darwin patch for __builtin_os_log_format. + +2021-05-13 Iain Sandoe <iain@sandoe.co.uk> + + * sanitizer_common/sanitizer_mac.cpp : Check for the + availability of __builtin_os_log_format before trying to + include a header depending on it. + (OS_LOG_DEFAULT): New. + (os_log_error): Define to a fall-back using an older API. + +2021-05-13 Martin Liska <mliska@suse.cz> + + * LOCAL_PATCHES: Update to the corresponding revision. + 2021-04-17 Jakub Jelinek <jakub@redhat.com> PR sanitizer/100114 diff --git a/libsanitizer/LOCAL_PATCHES b/libsanitizer/LOCAL_PATCHES index 09a1100..b1969fc 100644 --- a/libsanitizer/LOCAL_PATCHES +++ b/libsanitizer/LOCAL_PATCHES @@ -1,2 +1,2 @@ -d72227e29a083d7a8929d84894ba024651a1c3a7 -8eb12742e8ae5a16e05be627c701234dc7c13504 +fb73b1ce36c6ede097ecb220fcd0a1ed2df8fd01 +adab7b2bf42b469e51154a09a1b4fa0726a7073c diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE index 0fb64a9..c4731d0 100644 --- a/libsanitizer/MERGE +++ b/libsanitizer/MERGE @@ -1,4 +1,4 @@ -6e7dd1e3e1170080b76b5dcc5716bdd974343233 +f58e0513dd95944b81ce7a6e7b49ba656de7d75f The first line of this file holds the git revision number of the last merge done from the master library sources. diff --git a/libsanitizer/asan/asan_allocator.cpp b/libsanitizer/asan/asan_allocator.cpp index 58b496a..7c8bb50 100644 --- a/libsanitizer/asan/asan_allocator.cpp +++ b/libsanitizer/asan/asan_allocator.cpp @@ -476,7 +476,7 @@ struct Allocator { return false; if (m->Beg() != addr) return false; AsanThread *t = GetCurrentThread(); - m->SetAllocContext(t ? t->tid() : 0, StackDepotPut(*stack)); + m->SetAllocContext(t ? t->tid() : kMainTid, StackDepotPut(*stack)); return true; } @@ -570,7 +570,7 @@ struct Allocator { m->SetUsedSize(size); m->user_requested_alignment_log = user_requested_alignment_log; - m->SetAllocContext(t ? t->tid() : 0, StackDepotPut(*stack)); + m->SetAllocContext(t ? t->tid() : kMainTid, StackDepotPut(*stack)); uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY); @@ -1183,6 +1183,34 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) { m->lsan_tag = __lsan::kIgnored; return kIgnoreObjectSuccess; } + +void GetAdditionalThreadContextPtrs(ThreadContextBase *tctx, void *ptrs) { + // Look for the arg pointer of threads that have been created or are running. + // This is necessary to prevent false positive leaks due to the AsanThread + // holding the only live reference to a heap object. This can happen because + // the `pthread_create()` interceptor doesn't wait for the child thread to + // start before returning and thus loosing the the only live reference to the + // heap object on the stack. + + __asan::AsanThreadContext *atctx = + reinterpret_cast<__asan::AsanThreadContext *>(tctx); + __asan::AsanThread *asan_thread = atctx->thread; + + // Note ThreadStatusRunning is required because there is a small window where + // the thread status switches to `ThreadStatusRunning` but the `arg` pointer + // still isn't on the stack yet. + if (atctx->status != ThreadStatusCreated && + atctx->status != ThreadStatusRunning) + return; + + uptr thread_arg = reinterpret_cast<uptr>(asan_thread->get_arg()); + if (!thread_arg) + return; + + auto ptrsVec = reinterpret_cast<InternalMmapVector<uptr> *>(ptrs); + ptrsVec->push_back(thread_arg); +} + } // namespace __lsan // ---------------------- Interface ---------------- {{{1 diff --git a/libsanitizer/asan/asan_descriptions.cpp b/libsanitizer/asan/asan_descriptions.cpp index 153c874..2ba8a02 100644 --- a/libsanitizer/asan/asan_descriptions.cpp +++ b/libsanitizer/asan/asan_descriptions.cpp @@ -44,11 +44,11 @@ void DescribeThread(AsanThreadContext *context) { CHECK(context); asanThreadRegistry().CheckLocked(); // No need to announce the main thread. - if (context->tid == 0 || context->announced) { + if (context->tid == kMainTid || context->announced) { return; } context->announced = true; - InternalScopedString str(1024); + InternalScopedString str; str.append("Thread %s", AsanThreadIdAndName(context).c_str()); if (context->parent_tid == kInvalidTid) { str.append(" created by unknown thread\n"); @@ -77,7 +77,6 @@ static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) { } else if (AddrIsInLowShadow(addr)) { *shadow_kind = kShadowKindLow; } else { - CHECK(0 && "Address is not in memory and not in shadow?"); return false; } return true; @@ -126,7 +125,7 @@ static void GetAccessToHeapChunkInformation(ChunkAccess *descr, static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) { Decorator d; - InternalScopedString str(4096); + InternalScopedString str; str.append("%s", d.Location()); switch (descr.access_type) { case kAccessTypeLeft: @@ -243,7 +242,7 @@ static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end) pos_descr = "underflows"; } - InternalScopedString str(1024); + InternalScopedString str; str.append(" [%zd, %zd)", var.beg, var_end); // Render variable name. str.append(" '"); @@ -276,7 +275,7 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) { // Global descriptions static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, const __asan_global &g) { - InternalScopedString str(4096); + InternalScopedString str; Decorator d; str.append("%s", d.Location()); if (addr < g.beg) { @@ -464,7 +463,13 @@ AddressDescription::AddressDescription(uptr addr, uptr access_size, return; } data.kind = kAddressKindWild; - addr = 0; + data.wild.addr = addr; + data.wild.access_size = access_size; +} + +void WildAddressDescription::Print() const { + Printf("Address %p is a wild pointer inside of access range of size %p.\n", + addr, access_size); } void PrintAddressDescription(uptr addr, uptr access_size, diff --git a/libsanitizer/asan/asan_descriptions.h b/libsanitizer/asan/asan_descriptions.h index ee0e206..650e2eb 100644 --- a/libsanitizer/asan/asan_descriptions.h +++ b/libsanitizer/asan/asan_descriptions.h @@ -146,6 +146,13 @@ struct StackAddressDescription { bool GetStackAddressInformation(uptr addr, uptr access_size, StackAddressDescription *descr); +struct WildAddressDescription { + uptr addr; + uptr access_size; + + void Print() const; +}; + struct GlobalAddressDescription { uptr addr; // Assume address is close to at most four globals. @@ -193,7 +200,7 @@ class AddressDescription { HeapAddressDescription heap; StackAddressDescription stack; GlobalAddressDescription global; - uptr addr; + WildAddressDescription wild; }; }; @@ -211,7 +218,7 @@ class AddressDescription { uptr Address() const { switch (data.kind) { case kAddressKindWild: - return data.addr; + return data.wild.addr; case kAddressKindShadow: return data.shadow.addr; case kAddressKindHeap: @@ -226,7 +233,7 @@ class AddressDescription { void Print(const char *bug_descr = nullptr) const { switch (data.kind) { case kAddressKindWild: - Printf("Address %p is a wild pointer.\n", data.addr); + data.wild.Print(); return; case kAddressKindShadow: return data.shadow.Print(); diff --git a/libsanitizer/asan/asan_errors.cpp b/libsanitizer/asan/asan_errors.cpp index 541c6e0..e68e697 100644 --- a/libsanitizer/asan/asan_errors.cpp +++ b/libsanitizer/asan/asan_errors.cpp @@ -343,7 +343,8 @@ void ErrorODRViolation::Print() { Report("ERROR: AddressSanitizer: %s (%p):\n", scariness.GetDescription(), global1.beg); Printf("%s", d.Default()); - InternalScopedString g1_loc(256), g2_loc(256); + InternalScopedString g1_loc; + InternalScopedString g2_loc; PrintGlobalLocation(&g1_loc, global1); PrintGlobalLocation(&g2_loc, global2); Printf(" [1] size=%zd '%s' %s\n", global1.size, @@ -360,7 +361,7 @@ void ErrorODRViolation::Print() { Report( "HINT: if you don't care about these errors you may set " "ASAN_OPTIONS=detect_odr_violation=0\n"); - InternalScopedString error_msg(256); + InternalScopedString error_msg; error_msg.append("%s: global '%s' at %s", scariness.GetDescription(), MaybeDemangleGlobalName(global1.name), g1_loc.data()); ReportErrorSummary(error_msg.data()); @@ -554,7 +555,7 @@ static void PrintShadowMemoryForAddress(uptr addr) { uptr shadow_addr = MemToShadow(addr); const uptr n_bytes_per_row = 16; uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1); - InternalScopedString str(4096 * 8); + InternalScopedString str; str.append("Shadow bytes around the buggy address:\n"); for (int i = -5; i <= 5; i++) { uptr row_shadow_addr = aligned_shadow + i * n_bytes_per_row; diff --git a/libsanitizer/asan/asan_fake_stack.cpp b/libsanitizer/asan/asan_fake_stack.cpp index 295e6de..1f873fe 100644 --- a/libsanitizer/asan/asan_fake_stack.cpp +++ b/libsanitizer/asan/asan_fake_stack.cpp @@ -65,7 +65,7 @@ FakeStack *FakeStack::Create(uptr stack_size_log) { void FakeStack::Destroy(int tid) { PoisonAll(0); if (Verbosity() >= 2) { - InternalScopedString str(kNumberOfSizeClasses * 50); + InternalScopedString str; for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) str.append("%zd: %zd/%zd; ", class_id, hint_position_[class_id], NumberOfFrames(stack_size_log(), class_id)); diff --git a/libsanitizer/asan/asan_fuchsia.cpp b/libsanitizer/asan/asan_fuchsia.cpp index 6c61344..b0c7255 100644 --- a/libsanitizer/asan/asan_fuchsia.cpp +++ b/libsanitizer/asan/asan_fuchsia.cpp @@ -81,7 +81,7 @@ void AsanTSDInit(void (*destructor)(void *tsd)) { void PlatformTSDDtor(void *tsd) { UNREACHABLE(__func__); } static inline size_t AsanThreadMmapSize() { - return RoundUpTo(sizeof(AsanThread), PAGE_SIZE); + return RoundUpTo(sizeof(AsanThread), _zx_system_get_page_size()); } struct AsanThread::InitOptions { diff --git a/libsanitizer/asan/asan_interceptors.cpp b/libsanitizer/asan/asan_interceptors.cpp index 4e68b3b..9db7db8 100644 --- a/libsanitizer/asan/asan_interceptors.cpp +++ b/libsanitizer/asan/asan_interceptors.cpp @@ -191,20 +191,11 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) #include "sanitizer_common/sanitizer_common_syscalls.inc" #include "sanitizer_common/sanitizer_syscalls_netbsd.inc" -struct ThreadStartParam { - atomic_uintptr_t t; - atomic_uintptr_t is_registered; -}; - #if ASAN_INTERCEPT_PTHREAD_CREATE static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { - ThreadStartParam *param = reinterpret_cast<ThreadStartParam *>(arg); - AsanThread *t = nullptr; - while ((t = reinterpret_cast<AsanThread *>( - atomic_load(¶m->t, memory_order_acquire))) == nullptr) - internal_sched_yield(); + AsanThread *t = (AsanThread *)arg; SetCurrentThread(t); - return t->ThreadStart(GetTid(), ¶m->is_registered); + return t->ThreadStart(GetTid()); } INTERCEPTOR(int, pthread_create, void *thread, @@ -217,9 +208,11 @@ INTERCEPTOR(int, pthread_create, void *thread, int detached = 0; if (attr) REAL(pthread_attr_getdetachstate)(attr, &detached); - ThreadStartParam param; - atomic_store(¶m.t, 0, memory_order_relaxed); - atomic_store(¶m.is_registered, 0, memory_order_relaxed); + + u32 current_tid = GetCurrentTidOrInvalid(); + AsanThread *t = + AsanThread::Create(start_routine, arg, current_tid, &stack, detached); + int result; { // Ignore all allocations made by pthread_create: thread stack/TLS may be @@ -229,21 +222,13 @@ INTERCEPTOR(int, pthread_create, void *thread, #if CAN_SANITIZE_LEAKS __lsan::ScopedInterceptorDisabler disabler; #endif - result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m); + result = REAL(pthread_create)(thread, attr, asan_thread_start, t); } - if (result == 0) { - u32 current_tid = GetCurrentTidOrInvalid(); - AsanThread *t = - AsanThread::Create(start_routine, arg, current_tid, &stack, detached); - atomic_store(¶m.t, reinterpret_cast<uptr>(t), memory_order_release); - // Wait until the AsanThread object is initialized and the ThreadRegistry - // entry is in "started" state. One reason for this is that after this - // interceptor exits, the child thread's stack may be the only thing holding - // the |arg| pointer. This may cause LSan to report a leak if leak checking - // happens at a point when the interceptor has already exited, but the stack - // range for the child thread is not yet known. - while (atomic_load(¶m.is_registered, memory_order_acquire) == 0) - internal_sched_yield(); + if (result != 0) { + // If the thread didn't start delete the AsanThread to avoid leaking it. + // Note AsanThreadContexts never get destroyed so the AsanThreadContext + // that was just created for the AsanThread is wasted. + t->Destroy(); } return result; } diff --git a/libsanitizer/asan/asan_interceptors.h b/libsanitizer/asan/asan_interceptors.h index 56dc34b..5bd5be6 100644 --- a/libsanitizer/asan/asan_interceptors.h +++ b/libsanitizer/asan/asan_interceptors.h @@ -60,7 +60,7 @@ void InitializePlatformInterceptors(); # define ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX 0 #endif -#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_SOLARIS +#if SANITIZER_GLIBC || SANITIZER_SOLARIS # define ASAN_INTERCEPT_SWAPCONTEXT 1 #else # define ASAN_INTERCEPT_SWAPCONTEXT 0 @@ -72,7 +72,7 @@ void InitializePlatformInterceptors(); # define ASAN_INTERCEPT_SIGLONGJMP 0 #endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if SANITIZER_GLIBC # define ASAN_INTERCEPT___LONGJMP_CHK 1 #else # define ASAN_INTERCEPT___LONGJMP_CHK 0 @@ -111,7 +111,7 @@ void InitializePlatformInterceptors(); # define ASAN_INTERCEPT_ATEXIT 0 #endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if SANITIZER_GLIBC # define ASAN_INTERCEPT___STRDUP 1 #else # define ASAN_INTERCEPT___STRDUP 0 @@ -139,10 +139,10 @@ DECLARE_REAL(uptr, strnlen, const char *s, uptr maxlen) DECLARE_REAL(char*, strstr, const char *s1, const char *s2) #if !SANITIZER_MAC -#define ASAN_INTERCEPT_FUNC(name) \ - do { \ - if (!INTERCEPT_FUNCTION(name)) \ - VReport(1, "AddressSanitizer: failed to intercept '%s'\n'", #name); \ +#define ASAN_INTERCEPT_FUNC(name) \ + do { \ + if (!INTERCEPT_FUNCTION(name)) \ + VReport(1, "AddressSanitizer: failed to intercept '%s'\n", #name); \ } while (0) #define ASAN_INTERCEPT_FUNC_VER(name, ver) \ do { \ diff --git a/libsanitizer/asan/asan_linux.cpp b/libsanitizer/asan/asan_linux.cpp index fb1a442..4bcbe5d 100644 --- a/libsanitizer/asan/asan_linux.cpp +++ b/libsanitizer/asan/asan_linux.cpp @@ -55,6 +55,7 @@ extern Elf_Dyn _DYNAMIC; #else #include <sys/ucontext.h> #include <link.h> +extern ElfW(Dyn) _DYNAMIC[]; #endif // x86-64 FreeBSD 9.2 and older define 'ucontext_t' incorrectly in @@ -84,7 +85,7 @@ bool IsSystemHeapAddress (uptr addr) { return false; } void *AsanDoesNotSupportStaticLinkage() { // This will fail to link with -static. - return &_DYNAMIC; // defined in link.h + return &_DYNAMIC; } #if ASAN_PREMAP_SHADOW diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index a7136de..2511e70 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -72,6 +72,13 @@ // || `[0x2000000000, 0x23ffffffff]` || LowShadow || // || `[0x0000000000, 0x1fffffffff]` || LowMem || // +// Default Linux/RISCV64 Sv39 mapping: +// || `[0x1555550000, 0x3fffffffff]` || HighMem || +// || `[0x0fffffa000, 0x1555555fff]` || HighShadow || +// || `[0x0effffa000, 0x0fffff9fff]` || ShadowGap || +// || `[0x0d55550000, 0x0effff9fff]` || LowShadow || +// || `[0x0000000000, 0x0d5554ffff]` || LowMem || +// // Default Linux/AArch64 (39-bit VMA) mapping: // || `[0x2000000000, 0x7fffffffff]` || highmem || // || `[0x1400000000, 0x1fffffffff]` || highshadow || @@ -79,20 +86,6 @@ // || `[0x1000000000, 0x11ffffffff]` || lowshadow || // || `[0x0000000000, 0x0fffffffff]` || lowmem || // -// RISC-V has only 38 bits for task size -// Low mem size is set with kRiscv64_ShadowOffset64 in -// compiler-rt/lib/asan/asan_allocator.h and in -// llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp with -// kRiscv64_ShadowOffset64, High mem top border is set with -// GetMaxVirtualAddress() in -// compiler-rt/lib/sanitizer_common/sanitizer_linux.cpp -// Default Linux/RISCV64 Sv39/Sv48 mapping: -// || `[0x000820000000, 0x003fffffffff]` || HighMem || -// || `[0x000124000000, 0x00081fffffff]` || HighShadow || -// || `[0x000024000000, 0x000123ffffff]` || ShadowGap || -// || `[0x000020000000, 0x000023ffffff]` || LowShadow || -// || `[0x000000000000, 0x00001fffffff]` || LowMem || -// // Default Linux/AArch64 (42-bit VMA) mapping: // || `[0x10000000000, 0x3ffffffffff]` || highmem || // || `[0x0a000000000, 0x0ffffffffff]` || highshadow || @@ -175,7 +168,7 @@ static const u64 kDefaultShadowOffset64 = 1ULL << 44; static const u64 kDefaultShort64bitShadowOffset = 0x7FFFFFFF & (~0xFFFULL << kDefaultShadowScale); // < 2G. static const u64 kAArch64_ShadowOffset64 = 1ULL << 36; -static const u64 kRiscv64_ShadowOffset64 = 0x20000000; +static const u64 kRiscv64_ShadowOffset64 = 0xd55550000; static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000; static const u64 kMIPS64_ShadowOffset64 = 1ULL << 37; static const u64 kPPC64_ShadowOffset64 = 1ULL << 41; diff --git a/libsanitizer/asan/asan_new_delete.cpp b/libsanitizer/asan/asan_new_delete.cpp index 5dfcc00..92a8648 100644 --- a/libsanitizer/asan/asan_new_delete.cpp +++ b/libsanitizer/asan/asan_new_delete.cpp @@ -45,7 +45,7 @@ COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[] #endif #undef COMMENT_EXPORT #else -#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE +#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE #endif using namespace __asan; diff --git a/libsanitizer/asan/asan_poisoning.cpp b/libsanitizer/asan/asan_poisoning.cpp index 44f872e..fa149ec 100644 --- a/libsanitizer/asan/asan_poisoning.cpp +++ b/libsanitizer/asan/asan_poisoning.cpp @@ -364,7 +364,7 @@ void __sanitizer_annotate_contiguous_container(const void *beg_p, &stack); } CHECK_LE(end - beg, - FIRST_32_SECOND_64(1UL << 30, 1ULL << 34)); // Sanity check. + FIRST_32_SECOND_64(1UL << 30, 1ULL << 40)); // Sanity check. uptr a = RoundDownTo(Min(old_mid, new_mid), granularity); uptr c = RoundUpTo(Max(old_mid, new_mid), granularity); diff --git a/libsanitizer/asan/asan_posix.cpp b/libsanitizer/asan/asan_posix.cpp index d7f19d8..63ad735 100644 --- a/libsanitizer/asan/asan_posix.cpp +++ b/libsanitizer/asan/asan_posix.cpp @@ -56,7 +56,7 @@ bool PlatformUnpoisonStacks() { if (signal_stack.ss_flags != SS_ONSTACK) return false; - // Since we're on the signal altnerate stack, we cannot find the DEFAULT + // Since we're on the signal alternate stack, we cannot find the DEFAULT // stack bottom using a local variable. uptr default_bottom, tls_addr, tls_size, stack_size; GetThreadStackAndTls(/*main=*/false, &default_bottom, &stack_size, &tls_addr, diff --git a/libsanitizer/asan/asan_rtl.cpp b/libsanitizer/asan/asan_rtl.cpp index 7b5a929..e715d77 100644 --- a/libsanitizer/asan/asan_rtl.cpp +++ b/libsanitizer/asan/asan_rtl.cpp @@ -62,19 +62,9 @@ static void AsanDie() { } } -static void AsanCheckFailed(const char *file, int line, const char *cond, - u64 v1, u64 v2) { - Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file, - line, cond, (uptr)v1, (uptr)v2); - - // Print a stack trace the first time we come here. Otherwise, we probably - // failed a CHECK during symbolization. - static atomic_uint32_t num_calls; - if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) == 0) { - PRINT_CURRENT_STACK_CHECK(); - } - - Die(); +static void CheckUnwind() { + GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_check); + stack.Print(); } // -------------------------- Globals --------------------- {{{1 @@ -432,7 +422,7 @@ static void AsanInitInternal() { // Install tool-specific callbacks in sanitizer_common. AddDieCallback(AsanDie); - SetCheckFailedCallback(AsanCheckFailed); + SetCheckUnwindCallback(CheckUnwind); SetPrintfAndReportCallback(AppendToErrorMessageBuffer); __sanitizer_set_report_path(common_flags()->log_path); @@ -568,7 +558,7 @@ void UnpoisonStack(uptr bottom, uptr top, const char *type) { type, top, bottom, top - bottom, top - bottom); return; } - PoisonShadow(bottom, top - bottom, 0); + PoisonShadow(bottom, RoundUpTo(top - bottom, SHADOW_GRANULARITY), 0); } static void UnpoisonDefaultStack() { diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h index 47ca85a..b9575d2 100644 --- a/libsanitizer/asan/asan_stack.h +++ b/libsanitizer/asan/asan_stack.h @@ -54,9 +54,6 @@ u32 GetMallocContextSize(); #define GET_STACK_TRACE_FATAL_HERE \ GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal) -#define GET_STACK_TRACE_CHECK_HERE \ - GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_check) - #define GET_STACK_TRACE_THREAD \ GET_STACK_TRACE(kStackTraceMax, true) @@ -71,10 +68,4 @@ u32 GetMallocContextSize(); stack.Print(); \ } -#define PRINT_CURRENT_STACK_CHECK() \ - { \ - GET_STACK_TRACE_CHECK_HERE; \ - stack.Print(); \ - } - #endif // ASAN_STACK_H diff --git a/libsanitizer/asan/asan_thread.cpp b/libsanitizer/asan/asan_thread.cpp index fb09af0..9c3c86f 100644 --- a/libsanitizer/asan/asan_thread.cpp +++ b/libsanitizer/asan/asan_thread.cpp @@ -100,18 +100,27 @@ void AsanThread::Destroy() { int tid = this->tid(); VReport(1, "T%d exited\n", tid); - malloc_storage().CommitBack(); - if (common_flags()->use_sigaltstack) UnsetAlternateSignalStack(); - asanThreadRegistry().FinishThread(tid); - FlushToDeadThreadStats(&stats_); - // We also clear the shadow on thread destruction because - // some code may still be executing in later TSD destructors - // and we don't want it to have any poisoned stack. - ClearShadowForThreadStackAndTLS(); - DeleteFakeStack(tid); + bool was_running = + (asanThreadRegistry().FinishThread(tid) == ThreadStatusRunning); + if (was_running) { + if (AsanThread *thread = GetCurrentThread()) + CHECK_EQ(this, thread); + malloc_storage().CommitBack(); + if (common_flags()->use_sigaltstack) + UnsetAlternateSignalStack(); + FlushToDeadThreadStats(&stats_); + // We also clear the shadow on thread destruction because + // some code may still be executing in later TSD destructors + // and we don't want it to have any poisoned stack. + ClearShadowForThreadStackAndTLS(); + DeleteFakeStack(tid); + } else { + CHECK_NE(this, GetCurrentThread()); + } uptr size = RoundUpTo(sizeof(AsanThread), GetPageSizeCached()); UnmapOrDie(this, size); - DTLS_Destroy(); + if (was_running) + DTLS_Destroy(); } void AsanThread::StartSwitchFiber(FakeStack **fake_stack_save, uptr bottom, @@ -219,7 +228,7 @@ FakeStack *AsanThread::AsyncSignalSafeLazyInitFakeStack() { } void AsanThread::Init(const InitOptions *options) { - DCHECK_NE(tid(), ThreadRegistry::kUnknownTid); + DCHECK_NE(tid(), kInvalidTid); next_stack_top_ = next_stack_bottom_ = 0; atomic_store(&stack_switching_, false, memory_order_release); CHECK_EQ(this->stack_size(), 0U); @@ -253,12 +262,9 @@ void AsanThread::Init(const InitOptions *options) { // SetThreadStackAndTls. #if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS -thread_return_t AsanThread::ThreadStart( - tid_t os_id, atomic_uintptr_t *signal_thread_is_registered) { +thread_return_t AsanThread::ThreadStart(tid_t os_id) { Init(); asanThreadRegistry().StartThread(tid(), os_id, ThreadType::Regular, nullptr); - if (signal_thread_is_registered) - atomic_store(signal_thread_is_registered, 1, memory_order_release); if (common_flags()->use_sigaltstack) SetAlternateSignalStack(); @@ -285,11 +291,10 @@ thread_return_t AsanThread::ThreadStart( AsanThread *CreateMainThread() { AsanThread *main_thread = AsanThread::Create( - /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ 0, + /* start_routine */ nullptr, /* arg */ nullptr, /* parent_tid */ kMainTid, /* stack */ nullptr, /* detached */ true); SetCurrentThread(main_thread); - main_thread->ThreadStart(internal_getpid(), - /* signal_thread_is_registered */ nullptr); + main_thread->ThreadStart(internal_getpid()); return main_thread; } @@ -300,9 +305,9 @@ void AsanThread::SetThreadStackAndTls(const InitOptions *options) { DCHECK_EQ(options, nullptr); uptr tls_size = 0; uptr stack_size = 0; - GetThreadStackAndTls(tid() == 0, &stack_bottom_, &stack_size, &tls_begin_, - &tls_size); - stack_top_ = stack_bottom_ + stack_size; + GetThreadStackAndTls(tid() == kMainTid, &stack_bottom_, &stack_size, + &tls_begin_, &tls_size); + stack_top_ = RoundDownTo(stack_bottom_ + stack_size, SHADOW_GRANULARITY); tls_end_ = tls_begin_ + tls_size; dtls_ = DTLS_Get(); @@ -426,7 +431,7 @@ AsanThread *GetCurrentThread() { // address. We are not entirely sure that we have correct main thread // limits, so only do this magic on Android, and only if the found thread // is the main thread. - AsanThreadContext *tctx = GetThreadContextByTidLocked(0); + AsanThreadContext *tctx = GetThreadContextByTidLocked(kMainTid); if (tctx && ThreadStackContainsAddress(tctx, &context)) { SetCurrentThread(tctx->thread); return tctx->thread; @@ -463,7 +468,7 @@ AsanThread *FindThreadByStackAddress(uptr addr) { void EnsureMainThreadIDIsCorrect() { AsanThreadContext *context = reinterpret_cast<AsanThreadContext *>(AsanTSDGet()); - if (context && (context->tid == 0)) + if (context && (context->tid == kMainTid)) context->os_id = GetTid(); } diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index ea58de4..200069c 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -28,7 +28,6 @@ struct DTLS; namespace __asan { -const u32 kInvalidTid = 0xffffff; // Must fit into 24 bits. const u32 kMaxNumberOfThreads = (1 << 22); // 4M class AsanThread; @@ -69,8 +68,7 @@ class AsanThread { struct InitOptions; void Init(const InitOptions *options = nullptr); - thread_return_t ThreadStart(tid_t os_id, - atomic_uintptr_t *signal_thread_is_registered); + thread_return_t ThreadStart(tid_t os_id); uptr stack_top(); uptr stack_bottom(); @@ -132,6 +130,8 @@ class AsanThread { void *extra_spill_area() { return &extra_spill_area_; } + void *get_arg() { return arg_; } + private: // NOTE: There is no AsanThread constructor. It is allocated // via mmap() and *must* be valid in zero-initialized state. diff --git a/libsanitizer/asan/asan_win.cpp b/libsanitizer/asan/asan_win.cpp index 8044ae1..1577c83 100644 --- a/libsanitizer/asan/asan_win.cpp +++ b/libsanitizer/asan/asan_win.cpp @@ -134,7 +134,7 @@ INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { AsanThread *t = (AsanThread *)arg; SetCurrentThread(t); - return t->ThreadStart(GetTid(), /* signal_thread_is_registered */ nullptr); + return t->ThreadStart(GetTid()); } INTERCEPTOR_WINAPI(HANDLE, CreateThread, LPSECURITY_ATTRIBUTES security, diff --git a/libsanitizer/builtins/assembly.h b/libsanitizer/builtins/assembly.h index f437cb8..9c01505 100644 --- a/libsanitizer/builtins/assembly.h +++ b/libsanitizer/builtins/assembly.h @@ -14,8 +14,8 @@ #ifndef COMPILERRT_ASSEMBLY_H #define COMPILERRT_ASSEMBLY_H -#if defined(__POWERPC__) || defined(__powerpc__) || defined(__ppc__) -#define SEPARATOR @ +#if defined(__APPLE__) && defined(__aarch64__) +#define SEPARATOR %% #else #define SEPARATOR ; #endif @@ -35,14 +35,14 @@ #define HIDDEN(name) .hidden name #define LOCAL_LABEL(name) .L_##name #define FILE_LEVEL_DIRECTIVE -#if defined(__arm__) +#if defined(__arm__) || defined(__aarch64__) #define SYMBOL_IS_FUNC(name) .type name,%function #else #define SYMBOL_IS_FUNC(name) .type name,@function #endif #define CONST_SECTION .section .rodata -#if defined(__GNU__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \ +#if defined(__GNU__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \ defined(__linux__) #define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits #else @@ -65,6 +65,68 @@ #endif +#if defined(__arm__) || defined(__aarch64__) +#define FUNC_ALIGN \ + .text SEPARATOR \ + .balign 16 SEPARATOR +#else +#define FUNC_ALIGN +#endif + +// BTI and PAC gnu property note +#define NT_GNU_PROPERTY_TYPE_0 5 +#define GNU_PROPERTY_AARCH64_FEATURE_1_AND 0xc0000000 +#define GNU_PROPERTY_AARCH64_FEATURE_1_BTI 1 +#define GNU_PROPERTY_AARCH64_FEATURE_1_PAC 2 + +#if defined(__ARM_FEATURE_BTI_DEFAULT) +#define BTI_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_BTI +#else +#define BTI_FLAG 0 +#endif + +#if __ARM_FEATURE_PAC_DEFAULT & 3 +#define PAC_FLAG GNU_PROPERTY_AARCH64_FEATURE_1_PAC +#else +#define PAC_FLAG 0 +#endif + +#define GNU_PROPERTY(type, value) \ + .pushsection .note.gnu.property, "a" SEPARATOR \ + .p2align 3 SEPARATOR \ + .word 4 SEPARATOR \ + .word 16 SEPARATOR \ + .word NT_GNU_PROPERTY_TYPE_0 SEPARATOR \ + .asciz "GNU" SEPARATOR \ + .word type SEPARATOR \ + .word 4 SEPARATOR \ + .word value SEPARATOR \ + .word 0 SEPARATOR \ + .popsection + +#if BTI_FLAG != 0 +#define BTI_C hint #34 +#define BTI_J hint #36 +#else +#define BTI_C +#define BTI_J +#endif + +#if (BTI_FLAG | PAC_FLAG) != 0 +#define GNU_PROPERTY_BTI_PAC \ + GNU_PROPERTY(GNU_PROPERTY_AARCH64_FEATURE_1_AND, BTI_FLAG | PAC_FLAG) +#else +#define GNU_PROPERTY_BTI_PAC +#endif + +#if defined(__clang__) || defined(__GCC_HAVE_DWARF2_CFI_ASM) +#define CFI_START .cfi_startproc +#define CFI_END .cfi_endproc +#else +#define CFI_START +#define CFI_END +#endif + #if defined(__arm__) // Determine actual [ARM][THUMB[1][2]] ISA using compiler predefined macros: @@ -131,15 +193,24 @@ #define DEFINE_CODE_STATE #endif -#define GLUE2(a, b) a##b -#define GLUE(a, b) GLUE2(a, b) +#define GLUE2_(a, b) a##b +#define GLUE(a, b) GLUE2_(a, b) +#define GLUE2(a, b) GLUE2_(a, b) +#define GLUE3_(a, b, c) a##b##c +#define GLUE3(a, b, c) GLUE3_(a, b, c) +#define GLUE4_(a, b, c, d) a##b##c##d +#define GLUE4(a, b, c, d) GLUE4_(a, b, c, d) + #define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name) #ifdef VISIBILITY_HIDDEN #define DECLARE_SYMBOL_VISIBILITY(name) \ HIDDEN(SYMBOL_NAME(name)) SEPARATOR +#define DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name) \ + HIDDEN(name) SEPARATOR #else #define DECLARE_SYMBOL_VISIBILITY(name) +#define DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name) #endif #define DEFINE_COMPILERRT_FUNCTION(name) \ @@ -177,6 +248,16 @@ DECLARE_FUNC_ENCODING \ name: +#define DEFINE_COMPILERRT_OUTLINE_FUNCTION_UNMANGLED(name) \ + DEFINE_CODE_STATE \ + FUNC_ALIGN \ + .globl name SEPARATOR \ + SYMBOL_IS_FUNC(name) SEPARATOR \ + DECLARE_SYMBOL_VISIBILITY_UNMANGLED(name) SEPARATOR \ + CFI_START SEPARATOR \ + DECLARE_FUNC_ENCODING \ + name: SEPARATOR BTI_C + #define DEFINE_COMPILERRT_FUNCTION_ALIAS(name, target) \ .globl SYMBOL_NAME(name) SEPARATOR \ SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ @@ -193,8 +274,13 @@ #ifdef __ELF__ #define END_COMPILERRT_FUNCTION(name) \ .size SYMBOL_NAME(name), . - SYMBOL_NAME(name) +#define END_COMPILERRT_OUTLINE_FUNCTION(name) \ + CFI_END SEPARATOR \ + .size SYMBOL_NAME(name), . - SYMBOL_NAME(name) #else #define END_COMPILERRT_FUNCTION(name) +#define END_COMPILERRT_OUTLINE_FUNCTION(name) \ + CFI_END #endif #endif // COMPILERRT_ASSEMBLY_H diff --git a/libsanitizer/hwasan/hwasan.cpp b/libsanitizer/hwasan/hwasan.cpp index c532211..8d6c252 100644 --- a/libsanitizer/hwasan/hwasan.cpp +++ b/libsanitizer/hwasan/hwasan.cpp @@ -128,16 +128,11 @@ static void InitializeFlags() { if (common_flags()->help) parser.PrintFlagDescriptions(); } -static void HWAsanCheckFailed(const char *file, int line, const char *cond, - u64 v1, u64 v2) { - Report("HWAddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file, - line, cond, (uptr)v1, (uptr)v2); - PRINT_CURRENT_STACK_CHECK(); - Die(); +static void CheckUnwind() { + GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()); + stack.Print(); } -static constexpr uptr kMemoryUsageBufferSize = 4096; - static void HwasanFormatMemoryUsage(InternalScopedString &s) { HwasanThreadList &thread_list = hwasanThreadList(); auto thread_stats = thread_list.GetThreadStats(); @@ -155,6 +150,8 @@ static void HwasanFormatMemoryUsage(InternalScopedString &s) { } #if SANITIZER_ANDROID +static constexpr uptr kMemoryUsageBufferSize = 4096; + static char *memory_usage_buffer = nullptr; static void InitMemoryUsage() { @@ -171,7 +168,7 @@ void UpdateMemoryUsage() { return; if (!memory_usage_buffer) InitMemoryUsage(); - InternalScopedString s(kMemoryUsageBufferSize); + InternalScopedString s; HwasanFormatMemoryUsage(s); internal_strncpy(memory_usage_buffer, s.data(), kMemoryUsageBufferSize - 1); memory_usage_buffer[kMemoryUsageBufferSize - 1] = '\0'; @@ -271,7 +268,7 @@ void __hwasan_init() { InitializeFlags(); // Install tool-specific callbacks in sanitizer_common. - SetCheckFailedCallback(HWAsanCheckFailed); + SetCheckUnwindCallback(CheckUnwind); __sanitizer_set_report_path(common_flags()->log_path); @@ -493,7 +490,7 @@ extern "C" void *__hwasan_extra_spill_area() { } void __hwasan_print_memory_usage() { - InternalScopedString s(kMemoryUsageBufferSize); + InternalScopedString s; HwasanFormatMemoryUsage(s); Printf("%s\n", s.data()); } diff --git a/libsanitizer/hwasan/hwasan.h b/libsanitizer/hwasan/hwasan.h index d4521ef..8515df5 100644 --- a/libsanitizer/hwasan/hwasan.h +++ b/libsanitizer/hwasan/hwasan.h @@ -14,11 +14,12 @@ #ifndef HWASAN_H #define HWASAN_H +#include "hwasan_flags.h" +#include "hwasan_interface_internal.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stacktrace.h" -#include "hwasan_interface_internal.h" -#include "hwasan_flags.h" #include "ubsan/ubsan_platform.h" #ifndef HWASAN_CONTAINS_UBSAN @@ -35,10 +36,31 @@ typedef u8 tag_t; +#if defined(__x86_64__) +// Tags are done in middle bits using userspace aliasing. +constexpr unsigned kAddressTagShift = 39; +constexpr unsigned kTagBits = 3; + +// The alias region is placed next to the shadow so the upper bits of all +// taggable addresses matches the upper bits of the shadow base. This shift +// value determines which upper bits must match. It has a floor of 44 since the +// shadow is always 8TB. +// TODO(morehouse): In alias mode we can shrink the shadow and use a +// simpler/faster shadow calculation. +constexpr unsigned kTaggableRegionCheckShift = + __sanitizer::Max(kAddressTagShift + kTagBits + 1U, 44U); +#else // TBI (Top Byte Ignore) feature of AArch64: bits [63:56] are ignored in address // translation and can be used to store a tag. -const unsigned kAddressTagShift = 56; -const uptr kAddressTagMask = 0xFFUL << kAddressTagShift; +constexpr unsigned kAddressTagShift = 56; +constexpr unsigned kTagBits = 8; +#endif // defined(__x86_64__) + +// Mask for extracting tag bits from the lower 8 bits. +constexpr uptr kTagMask = (1UL << kTagBits) - 1; + +// Mask for extracting tag bits from full pointers. +constexpr uptr kAddressTagMask = kTagMask << kAddressTagShift; // Minimal alignment of the shadow base address. Determines the space available // for threads and stack histories. This is an ABI constant. @@ -50,7 +72,7 @@ const unsigned kRecordFPLShift = 4; const unsigned kRecordFPModulus = 1 << (64 - kRecordFPShift + kRecordFPLShift); static inline tag_t GetTagFromPointer(uptr p) { - return p >> kAddressTagShift; + return (p >> kAddressTagShift) & kTagMask; } static inline uptr UntagAddr(uptr tagged_addr) { @@ -105,15 +127,6 @@ void InstallAtExitHandler(); if (hwasan_inited) \ stack.Unwind(pc, bp, nullptr, common_flags()->fast_unwind_on_fatal) -#define GET_FATAL_STACK_TRACE_HERE \ - GET_FATAL_STACK_TRACE_PC_BP(StackTrace::GetCurrentPc(), GET_CURRENT_FRAME()) - -#define PRINT_CURRENT_STACK_CHECK() \ - { \ - GET_FATAL_STACK_TRACE_HERE; \ - stack.Print(); \ - } - void HwasanTSDInit(); void HwasanTSDThreadInit(); diff --git a/libsanitizer/hwasan/hwasan_allocator.cpp b/libsanitizer/hwasan/hwasan_allocator.cpp index 0b6b734..a6fc794 100644 --- a/libsanitizer/hwasan/hwasan_allocator.cpp +++ b/libsanitizer/hwasan/hwasan_allocator.cpp @@ -29,8 +29,8 @@ static AllocatorCache fallback_allocator_cache; static SpinMutex fallback_mutex; static atomic_uint8_t hwasan_allocator_tagging_enabled; -static const tag_t kFallbackAllocTag = 0xBB; -static const tag_t kFallbackFreeTag = 0xBC; +static constexpr tag_t kFallbackAllocTag = 0xBB & kTagMask; +static constexpr tag_t kFallbackFreeTag = 0xBC; enum RightAlignMode { kRightAlignNever, @@ -84,7 +84,8 @@ void HwasanAllocatorInit() { atomic_store_relaxed(&hwasan_allocator_tagging_enabled, !flags()->disable_allocator_tagging); SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); - allocator.Init(common_flags()->allocator_release_to_os_interval_ms); + allocator.Init(common_flags()->allocator_release_to_os_interval_ms, + kAliasRegionStart); for (uptr i = 0; i < sizeof(tail_magic); i++) tail_magic[i] = GetCurrentThread()->GenerateRandomTag(); } @@ -148,7 +149,8 @@ static void *HwasanAllocate(StackTrace *stack, uptr orig_size, uptr alignment, // Tagging can only be skipped when both tag_in_malloc and tag_in_free are // false. When tag_in_malloc = false and tag_in_free = true malloc needs to // retag to 0. - if ((flags()->tag_in_malloc || flags()->tag_in_free) && + if (InTaggableRegion(reinterpret_cast<uptr>(user_ptr)) && + (flags()->tag_in_malloc || flags()->tag_in_free) && atomic_load_relaxed(&hwasan_allocator_tagging_enabled)) { if (flags()->tag_in_malloc && malloc_bisect(stack, orig_size)) { tag_t tag = t ? t->GenerateRandomTag() : kFallbackAllocTag; @@ -175,6 +177,8 @@ static void *HwasanAllocate(StackTrace *stack, uptr orig_size, uptr alignment, static bool PointerAndMemoryTagsMatch(void *tagged_ptr) { CHECK(tagged_ptr); uptr tagged_uptr = reinterpret_cast<uptr>(tagged_ptr); + if (!InTaggableRegion(tagged_uptr)) + return true; tag_t mem_tag = *reinterpret_cast<tag_t *>( MemToShadow(reinterpret_cast<uptr>(UntagPtr(tagged_ptr)))); return PossiblyShortTagMatches(mem_tag, tagged_uptr, 1); @@ -187,7 +191,9 @@ static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) { if (!PointerAndMemoryTagsMatch(tagged_ptr)) ReportInvalidFree(stack, reinterpret_cast<uptr>(tagged_ptr)); - void *untagged_ptr = UntagPtr(tagged_ptr); + void *untagged_ptr = InTaggableRegion(reinterpret_cast<uptr>(tagged_ptr)) + ? UntagPtr(tagged_ptr) + : tagged_ptr; void *aligned_ptr = reinterpret_cast<void *>( RoundDownTo(reinterpret_cast<uptr>(untagged_ptr), kShadowAlignment)); Metadata *meta = @@ -219,10 +225,14 @@ static void HwasanDeallocate(StackTrace *stack, void *tagged_ptr) { Min(TaggedSize(orig_size), (uptr)flags()->max_free_fill_size); internal_memset(aligned_ptr, flags()->free_fill_byte, fill_size); } - if (flags()->tag_in_free && malloc_bisect(stack, 0) && - atomic_load_relaxed(&hwasan_allocator_tagging_enabled)) + if (InTaggableRegion(reinterpret_cast<uptr>(tagged_ptr)) && + flags()->tag_in_free && malloc_bisect(stack, 0) && + atomic_load_relaxed(&hwasan_allocator_tagging_enabled)) { + // Always store full 8-bit tags on free to maximize UAF detection. + tag_t tag = t ? t->GenerateRandomTag(/*num_bits=*/8) : kFallbackFreeTag; TagMemoryAligned(reinterpret_cast<uptr>(aligned_ptr), TaggedSize(orig_size), - t ? t->GenerateRandomTag() : kFallbackFreeTag); + tag); + } if (t) { allocator.Deallocate(t->allocator_cache(), aligned_ptr); if (auto *ha = t->heap_allocations()) @@ -365,7 +375,7 @@ int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size, // OOM error is already taken care of by HwasanAllocate. return errno_ENOMEM; CHECK(IsAligned((uptr)ptr, alignment)); - *(void **)UntagPtr(memptr) = ptr; + *memptr = ptr; return 0; } diff --git a/libsanitizer/hwasan/hwasan_allocator.h b/libsanitizer/hwasan/hwasan_allocator.h index 43670a6..03bbcff 100644 --- a/libsanitizer/hwasan/hwasan_allocator.h +++ b/libsanitizer/hwasan/hwasan_allocator.h @@ -13,13 +13,15 @@ #ifndef HWASAN_ALLOCATOR_H #define HWASAN_ALLOCATOR_H +#include "hwasan.h" +#include "hwasan_interface_internal.h" +#include "hwasan_poisoning.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_checks.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_allocator_report.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_ring_buffer.h" -#include "hwasan_poisoning.h" #if !defined(__aarch64__) && !defined(__x86_64__) #error Unsupported platform @@ -55,7 +57,12 @@ static const uptr kMaxAllowedMallocSize = 1UL << 40; // 1T struct AP64 { static const uptr kSpaceBeg = ~0ULL; + +#if defined(__x86_64__) + static const uptr kSpaceSize = 1ULL << kAddressTagShift; +#else static const uptr kSpaceSize = 0x2000000000ULL; +#endif static const uptr kMetadataSize = sizeof(Metadata); typedef __sanitizer::VeryDenseSizeClassMap SizeClassMap; using AddressSpaceView = LocalAddressSpaceView; @@ -102,6 +109,16 @@ typedef RingBuffer<HeapAllocationRecord> HeapAllocationsRingBuffer; void GetAllocatorStats(AllocatorStatCounters s); +inline bool InTaggableRegion(uptr addr) { +#if defined(__x86_64__) + // Aliases are mapped next to shadow so that the upper bits match the shadow + // base. + return (addr >> kTaggableRegionCheckShift) == + (__hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift); +#endif + return true; +} + } // namespace __hwasan #endif // HWASAN_ALLOCATOR_H diff --git a/libsanitizer/hwasan/hwasan_checks.h b/libsanitizer/hwasan/hwasan_checks.h index a8de0fe..ab543ea 100644 --- a/libsanitizer/hwasan/hwasan_checks.h +++ b/libsanitizer/hwasan/hwasan_checks.h @@ -13,6 +13,7 @@ #ifndef HWASAN_CHECKS_H #define HWASAN_CHECKS_H +#include "hwasan_allocator.h" #include "hwasan_mapping.h" #include "sanitizer_common/sanitizer_common.h" @@ -81,6 +82,8 @@ enum class AccessType { Load, Store }; template <ErrorAction EA, AccessType AT, unsigned LogSize> __attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) { + if (!InTaggableRegion(p)) + return; uptr ptr_raw = p & ~kAddressTagMask; tag_t mem_tag = *(tag_t *)MemToShadow(ptr_raw); if (UNLIKELY(!PossiblyShortTagMatches(mem_tag, p, 1 << LogSize))) { @@ -94,7 +97,7 @@ __attribute__((always_inline, nodebug)) static void CheckAddress(uptr p) { template <ErrorAction EA, AccessType AT> __attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p, uptr sz) { - if (sz == 0) + if (sz == 0 || !InTaggableRegion(p)) return; tag_t ptr_tag = GetTagFromPointer(p); uptr ptr_raw = p & ~kAddressTagMask; diff --git a/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp b/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp index 12730b2..f53276e 100644 --- a/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp +++ b/libsanitizer/hwasan/hwasan_dynamic_shadow.cpp @@ -12,15 +12,17 @@ /// //===----------------------------------------------------------------------===// -#include "hwasan.h" #include "hwasan_dynamic_shadow.h" -#include "hwasan_mapping.h" -#include "sanitizer_common/sanitizer_common.h" -#include "sanitizer_common/sanitizer_posix.h" #include <elf.h> #include <link.h> +#include "hwasan.h" +#include "hwasan_mapping.h" +#include "hwasan_thread_list.h" +#include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_posix.h" + // The code in this file needs to run in an unrelocated binary. It should not // access any external symbol, including its own non-hidden globals. @@ -117,6 +119,12 @@ namespace __hwasan { void InitShadowGOT() {} uptr FindDynamicShadowStart(uptr shadow_size_bytes) { +#if defined(__x86_64__) + constexpr uptr kAliasSize = 1ULL << kAddressTagShift; + constexpr uptr kNumAliases = 1ULL << kTagBits; + return MapDynamicShadowAndAliases(shadow_size_bytes, kAliasSize, kNumAliases, + RingBufferSize()); +#endif return MapDynamicShadow(shadow_size_bytes, kShadowScale, kShadowBaseAlignment, kHighMemEnd); } diff --git a/libsanitizer/hwasan/hwasan_flags.h b/libsanitizer/hwasan/hwasan_flags.h index 0a6998f..b177501 100644 --- a/libsanitizer/hwasan/hwasan_flags.h +++ b/libsanitizer/hwasan/hwasan_flags.h @@ -12,6 +12,8 @@ #ifndef HWASAN_FLAGS_H #define HWASAN_FLAGS_H +#include "sanitizer_common/sanitizer_internal_defs.h" + namespace __hwasan { struct Flags { diff --git a/libsanitizer/hwasan/hwasan_flags.inc b/libsanitizer/hwasan/hwasan_flags.inc index 8e431d9..18ea47f 100644 --- a/libsanitizer/hwasan/hwasan_flags.inc +++ b/libsanitizer/hwasan/hwasan_flags.inc @@ -72,3 +72,12 @@ HWASAN_FLAG(uptr, malloc_bisect_right, 0, HWASAN_FLAG(bool, malloc_bisect_dump, false, "Print all allocations within [malloc_bisect_left, " "malloc_bisect_right] range ") + + +// Exit if we fail to enable the AArch64 kernel ABI relaxation which allows +// tagged pointers in syscalls. This is the default, but being able to disable +// that behaviour is useful for running the testsuite on more platforms (the +// testsuite can run since we manually ensure any pointer arguments to syscalls +// are untagged before the call. +HWASAN_FLAG(bool, fail_without_syscall_abi, true, + "Exit if fail to request relaxed syscall ABI.") diff --git a/libsanitizer/hwasan/hwasan_interceptors.cpp b/libsanitizer/hwasan/hwasan_interceptors.cpp index 44e569e..ad67e27 100644 --- a/libsanitizer/hwasan/hwasan_interceptors.cpp +++ b/libsanitizer/hwasan/hwasan_interceptors.cpp @@ -221,8 +221,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), ThreadStartArg *A = reinterpret_cast<ThreadStartArg *> (MmapOrDie( GetPageSizeCached(), "pthread_create")); *A = {callback, param}; - int res = REAL(pthread_create)(UntagPtr(th), UntagPtr(attr), - &HwasanThreadStartFunc, A); + int res = REAL(pthread_create)(th, attr, &HwasanThreadStartFunc, A); return res; } diff --git a/libsanitizer/hwasan/hwasan_interceptors_vfork.S b/libsanitizer/hwasan/hwasan_interceptors_vfork.S index 23d5659..fd20825 100644 --- a/libsanitizer/hwasan/hwasan_interceptors_vfork.S +++ b/libsanitizer/hwasan/hwasan_interceptors_vfork.S @@ -1,4 +1,5 @@ #include "sanitizer_common/sanitizer_asm.h" +#include "builtins/assembly.h" #if defined(__linux__) && HWASAN_WITH_INTERCEPTORS #define COMMON_INTERCEPTOR_SPILL_AREA __hwasan_extra_spill_area @@ -9,3 +10,5 @@ #endif NO_EXEC_STACK_DIRECTIVE + +GNU_PROPERTY_BTI_PAC diff --git a/libsanitizer/hwasan/hwasan_interface_internal.h b/libsanitizer/hwasan/hwasan_interface_internal.h index aedda31..25c0f94 100644 --- a/libsanitizer/hwasan/hwasan_interface_internal.h +++ b/libsanitizer/hwasan/hwasan_interface_internal.h @@ -222,6 +222,9 @@ SANITIZER_INTERFACE_ATTRIBUTE void *__hwasan_memset(void *s, int c, uptr n); SANITIZER_INTERFACE_ATTRIBUTE void *__hwasan_memmove(void *dest, const void *src, uptr n); + +SANITIZER_INTERFACE_ATTRIBUTE +void __hwasan_set_error_report_callback(void (*callback)(const char *)); } // extern "C" #endif // HWASAN_INTERFACE_INTERNAL_H diff --git a/libsanitizer/hwasan/hwasan_linux.cpp b/libsanitizer/hwasan/hwasan_linux.cpp index e99926d..8ce0ff7 100644 --- a/libsanitizer/hwasan/hwasan_linux.cpp +++ b/libsanitizer/hwasan/hwasan_linux.cpp @@ -76,6 +76,8 @@ uptr kHighShadowEnd; uptr kHighMemStart; uptr kHighMemEnd; +uptr kAliasRegionStart; // Always 0 on non-x86. + static void PrintRange(uptr start, uptr end, const char *name) { Printf("|| [%p, %p] || %.*s ||\n", (void *)start, (void *)end, 10, name); } @@ -119,9 +121,11 @@ void InitPrctl() { #define PR_GET_TAGGED_ADDR_CTRL 56 #define PR_TAGGED_ADDR_ENABLE (1UL << 0) // Check we're running on a kernel that can use the tagged address ABI. - if (internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0) == (uptr)-1 && - errno == EINVAL) { -#if SANITIZER_ANDROID + int local_errno = 0; + if (internal_iserror(internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0), + &local_errno) && + local_errno == EINVAL) { +#if SANITIZER_ANDROID || defined(__x86_64__) // Some older Android kernels have the tagged pointer ABI on // unconditionally, and hence don't have the tagged-addr prctl while still // allow the ABI. @@ -129,17 +133,20 @@ void InitPrctl() { // case. return; #else - Printf( - "FATAL: " - "HWAddressSanitizer requires a kernel with tagged address ABI.\n"); - Die(); + if (flags()->fail_without_syscall_abi) { + Printf( + "FATAL: " + "HWAddressSanitizer requires a kernel with tagged address ABI.\n"); + Die(); + } #endif } // Turn on the tagged address ABI. - if (internal_prctl(PR_SET_TAGGED_ADDR_CTRL, PR_TAGGED_ADDR_ENABLE, 0, 0, 0) == - (uptr)-1 || - !internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0)) { + if ((internal_iserror(internal_prctl(PR_SET_TAGGED_ADDR_CTRL, + PR_TAGGED_ADDR_ENABLE, 0, 0, 0)) || + !internal_prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0)) && + flags()->fail_without_syscall_abi) { Printf( "FATAL: HWAddressSanitizer failed to enable tagged address syscall " "ABI.\nSuggest check `sysctl abi.tagged_addr_disabled` " @@ -174,6 +181,18 @@ bool InitShadow() { // High memory starts where allocated shadow allows. kHighMemStart = ShadowToMem(kHighShadowStart); +#if defined(__x86_64__) + constexpr uptr kAliasRegionOffset = 1ULL << (kTaggableRegionCheckShift - 1); + kAliasRegionStart = + __hwasan_shadow_memory_dynamic_address + kAliasRegionOffset; + + CHECK_EQ(kAliasRegionStart >> kTaggableRegionCheckShift, + __hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift); + CHECK_EQ( + (kAliasRegionStart + kAliasRegionOffset - 1) >> kTaggableRegionCheckShift, + __hwasan_shadow_memory_dynamic_address >> kTaggableRegionCheckShift); +#endif + // Check the sanity of the defined memory ranges (there might be gaps). CHECK_EQ(kHighMemStart % GetMmapGranularity(), 0); CHECK_GT(kHighMemStart, kHighShadowEnd); @@ -217,7 +236,9 @@ void InitThreads() { } bool MemIsApp(uptr p) { +#if !defined(__x86_64__) // Memory outside the alias range has non-zero tags. CHECK(GetTagFromPointer(p) == 0); +#endif return p >= kHighMemStart || (p >= kLowMemStart && p <= kLowMemEnd); } diff --git a/libsanitizer/hwasan/hwasan_mapping.h b/libsanitizer/hwasan/hwasan_mapping.h index c149687..8243d1e 100644 --- a/libsanitizer/hwasan/hwasan_mapping.h +++ b/libsanitizer/hwasan/hwasan_mapping.h @@ -48,6 +48,8 @@ extern uptr kHighShadowEnd; extern uptr kHighMemStart; extern uptr kHighMemEnd; +extern uptr kAliasRegionStart; + inline uptr MemToShadow(uptr untagged_addr) { return (untagged_addr >> kShadowScale) + __hwasan_shadow_memory_dynamic_address; diff --git a/libsanitizer/hwasan/hwasan_memintrinsics.cpp b/libsanitizer/hwasan/hwasan_memintrinsics.cpp index e82d77a..fab017a 100644 --- a/libsanitizer/hwasan/hwasan_memintrinsics.cpp +++ b/libsanitizer/hwasan/hwasan_memintrinsics.cpp @@ -24,7 +24,7 @@ using namespace __hwasan; void *__hwasan_memset(void *block, int c, uptr size) { CheckAddressSized<ErrorAction::Recover, AccessType::Store>( reinterpret_cast<uptr>(block), size); - return memset(UntagPtr(block), c, size); + return memset(block, c, size); } void *__hwasan_memcpy(void *to, const void *from, uptr size) { @@ -32,7 +32,7 @@ void *__hwasan_memcpy(void *to, const void *from, uptr size) { reinterpret_cast<uptr>(to), size); CheckAddressSized<ErrorAction::Recover, AccessType::Load>( reinterpret_cast<uptr>(from), size); - return memcpy(UntagPtr(to), UntagPtr(from), size); + return memcpy(to, from, size); } void *__hwasan_memmove(void *to, const void *from, uptr size) { diff --git a/libsanitizer/hwasan/hwasan_new_delete.cpp b/libsanitizer/hwasan/hwasan_new_delete.cpp index 8d01d39..69cddda 100644 --- a/libsanitizer/hwasan/hwasan_new_delete.cpp +++ b/libsanitizer/hwasan/hwasan_new_delete.cpp @@ -27,6 +27,12 @@ void *res = hwasan_malloc(size, &stack);\ if (!nothrow && UNLIKELY(!res)) ReportOutOfMemory(size, &stack);\ return res +#define OPERATOR_NEW_ALIGN_BODY(nothrow) \ + GET_MALLOC_STACK_TRACE; \ + void *res = hwasan_aligned_alloc(static_cast<uptr>(align), size, &stack); \ + if (!nothrow && UNLIKELY(!res)) \ + ReportOutOfMemory(size, &stack); \ + return res #define OPERATOR_DELETE_BODY \ GET_MALLOC_STACK_TRACE; \ @@ -50,6 +56,7 @@ using namespace __hwasan; // Fake std::nothrow_t to avoid including <new>. namespace std { struct nothrow_t {}; + enum class align_val_t : size_t {}; } // namespace std @@ -66,6 +73,22 @@ INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY(true /*nothrow*/); } +INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void *operator new( + size_t size, std::align_val_t align) { + OPERATOR_NEW_ALIGN_BODY(false /*nothrow*/); +} +INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void *operator new[]( + size_t size, std::align_val_t align) { + OPERATOR_NEW_ALIGN_BODY(false /*nothrow*/); +} +INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void *operator new( + size_t size, std::align_val_t align, std::nothrow_t const &) { + OPERATOR_NEW_ALIGN_BODY(true /*nothrow*/); +} +INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void *operator new[]( + size_t size, std::align_val_t align, std::nothrow_t const &) { + OPERATOR_NEW_ALIGN_BODY(true /*nothrow*/); +} INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } @@ -77,5 +100,21 @@ INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } +INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete( + void *ptr, std::align_val_t align) NOEXCEPT { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete[]( + void *ptr, std::align_val_t) NOEXCEPT { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete( + void *ptr, std::align_val_t, std::nothrow_t const &) NOEXCEPT { + OPERATOR_DELETE_BODY; +} +INTERCEPTOR_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void operator delete[]( + void *ptr, std::align_val_t, std::nothrow_t const &) NOEXCEPT { + OPERATOR_DELETE_BODY; +} #endif // OPERATOR_NEW_BODY diff --git a/libsanitizer/hwasan/hwasan_report.cpp b/libsanitizer/hwasan/hwasan_report.cpp index 894a149..c021779 100644 --- a/libsanitizer/hwasan/hwasan_report.cpp +++ b/libsanitizer/hwasan/hwasan_report.cpp @@ -43,12 +43,16 @@ class ScopedReport { } ~ScopedReport() { + void (*report_cb)(const char *); { BlockingMutexLock lock(&error_message_lock_); - if (fatal) - SetAbortMessage(error_message_.data()); + report_cb = error_report_callback_; error_message_ptr_ = nullptr; } + if (report_cb) + report_cb(error_message_.data()); + if (fatal) + SetAbortMessage(error_message_.data()); if (common_flags()->print_module_map >= 2 || (fatal && common_flags()->print_module_map)) DumpProcessMap(); @@ -66,6 +70,12 @@ class ScopedReport { // overwrite old trailing '\0', keep new trailing '\0' untouched. internal_memcpy(&(*error_message_ptr_)[old_size - 1], msg, len); } + + static void SetErrorReportCallback(void (*callback)(const char *)) { + BlockingMutexLock lock(&error_message_lock_); + error_report_callback_ = callback; + } + private: ScopedErrorReportLock error_report_lock_; InternalMmapVector<char> error_message_; @@ -73,10 +83,12 @@ class ScopedReport { static InternalMmapVector<char> *error_message_ptr_; static BlockingMutex error_message_lock_; + static void (*error_report_callback_)(const char *); }; InternalMmapVector<char> *ScopedReport::error_message_ptr_; BlockingMutex ScopedReport::error_message_lock_; +void (*ScopedReport::error_report_callback_)(const char *); // If there is an active ScopedReport, append to its error message. void AppendToErrorMessageBuffer(const char *buffer) { @@ -212,7 +224,7 @@ static void PrintStackAllocations(StackAllocationsRingBuffer *sa, // We didn't find any locals. Most likely we don't have symbols, so dump // the information that we have for offline analysis. - InternalScopedString frame_desc(GetPageSizeCached() * 2); + InternalScopedString frame_desc; Printf("Previously allocated frames:\n"); for (uptr i = 0; i < frames; i++) { const uptr *record_addr = &(*sa)[i]; @@ -447,7 +459,7 @@ static void PrintTagInfoAroundAddr(tag_t *tag_ptr, uptr num_rows, RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len)); tag_t *beg_row = center_row_beg - row_len * (num_rows / 2); tag_t *end_row = center_row_beg + row_len * ((num_rows + 1) / 2); - InternalScopedString s(GetPageSizeCached() * 8); + InternalScopedString s; for (tag_t *row = beg_row; row < end_row; row += row_len) { s.append("%s", row == center_row_beg ? "=>" : " "); s.append("%p:", row); @@ -535,7 +547,7 @@ void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size, GetStackTraceFromId(chunk.GetAllocStackId()).Print(); } - InternalScopedString s(GetPageSizeCached() * 8); + InternalScopedString s; CHECK_GT(tail_size, 0U); CHECK_LT(tail_size, kShadowAlignment); u8 *tail = reinterpret_cast<u8*>(untagged_addr + orig_size); @@ -650,3 +662,7 @@ void ReportRegisters(uptr *frame, uptr pc) { } } // namespace __hwasan + +void __hwasan_set_error_report_callback(void (*callback)(const char *)) { + __hwasan::ScopedReport::SetErrorReportCallback(callback); +} diff --git a/libsanitizer/hwasan/hwasan_setjmp.S b/libsanitizer/hwasan/hwasan_setjmp.S index 0c13543..381af63 100644 --- a/libsanitizer/hwasan/hwasan_setjmp.S +++ b/libsanitizer/hwasan/hwasan_setjmp.S @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_asm.h" +#include "builtins/assembly.h" #if HWASAN_WITH_INTERCEPTORS && defined(__aarch64__) #include "sanitizer_common/sanitizer_platform.h" @@ -34,6 +35,7 @@ ASM_TYPE_FUNCTION(__interceptor_setjmp) __interceptor_setjmp: CFI_STARTPROC + BTI_C mov x1, #0 b __interceptor_sigsetjmp CFI_ENDPROC @@ -46,6 +48,7 @@ ASM_SIZE(__interceptor_setjmp) ASM_TYPE_FUNCTION(__interceptor_setjmp_bionic) __interceptor_setjmp_bionic: CFI_STARTPROC + BTI_C mov x1, #1 b __interceptor_sigsetjmp CFI_ENDPROC @@ -56,6 +59,7 @@ ASM_SIZE(__interceptor_setjmp_bionic) ASM_TYPE_FUNCTION(__interceptor_sigsetjmp) __interceptor_sigsetjmp: CFI_STARTPROC + BTI_C stp x19, x20, [x0, #0<<3] stp x21, x22, [x0, #2<<3] stp x23, x24, [x0, #4<<3] @@ -98,3 +102,5 @@ ALIAS __interceptor_setjmp, _setjmp // We do not need executable stack. NO_EXEC_STACK_DIRECTIVE + +GNU_PROPERTY_BTI_PAC diff --git a/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S index 08df127..bcb0df4 100644 --- a/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S +++ b/libsanitizer/hwasan/hwasan_tag_mismatch_aarch64.S @@ -1,4 +1,5 @@ #include "sanitizer_common/sanitizer_asm.h" +#include "builtins/assembly.h" // The content of this file is AArch64-only: #if defined(__aarch64__) @@ -74,6 +75,8 @@ .global __hwasan_tag_mismatch .type __hwasan_tag_mismatch, %function __hwasan_tag_mismatch: + BTI_J + // Compute the granule position one past the end of the access. mov x16, #1 and x17, x1, #0xf @@ -106,6 +109,7 @@ __hwasan_tag_mismatch: .type __hwasan_tag_mismatch_v2, %function __hwasan_tag_mismatch_v2: CFI_STARTPROC + BTI_J // Set the CFA to be the return address for caller of __hwasan_check_*. Note // that we do not emit CFI predicates to describe the contents of this stack @@ -150,3 +154,5 @@ __hwasan_tag_mismatch_v2: // We do not need executable stack. NO_EXEC_STACK_DIRECTIVE + +GNU_PROPERTY_BTI_PAC diff --git a/libsanitizer/hwasan/hwasan_thread.cpp b/libsanitizer/hwasan/hwasan_thread.cpp index b81a635..bb4d56a 100644 --- a/libsanitizer/hwasan/hwasan_thread.cpp +++ b/libsanitizer/hwasan/hwasan_thread.cpp @@ -35,6 +35,10 @@ void Thread::InitRandomState() { } void Thread::Init(uptr stack_buffer_start, uptr stack_buffer_size) { + CHECK_EQ(0, unique_id_); // try to catch bad stack reuse + CHECK_EQ(0, stack_top_); + CHECK_EQ(0, stack_bottom_); + static u64 unique_id; unique_id_ = unique_id++; if (auto sz = flags()->heap_history_size) @@ -113,18 +117,21 @@ static u32 xorshift(u32 state) { } // Generate a (pseudo-)random non-zero tag. -tag_t Thread::GenerateRandomTag() { +tag_t Thread::GenerateRandomTag(uptr num_bits) { + DCHECK_GT(num_bits, 0); if (tagging_disabled_) return 0; tag_t tag; + const uptr tag_mask = (1ULL << num_bits) - 1; do { if (flags()->random_tags) { if (!random_buffer_) random_buffer_ = random_state_ = xorshift(random_state_); CHECK(random_buffer_); - tag = random_buffer_ & 0xFF; - random_buffer_ >>= 8; + tag = random_buffer_ & tag_mask; + random_buffer_ >>= num_bits; } else { - tag = random_state_ = (random_state_ + 1) & 0xFF; + random_state_ += 1; + tag = random_state_ & tag_mask; } } while (!tag); return tag; diff --git a/libsanitizer/hwasan/hwasan_thread.h b/libsanitizer/hwasan/hwasan_thread.h index ebcdb79..1c71cab 100644 --- a/libsanitizer/hwasan/hwasan_thread.h +++ b/libsanitizer/hwasan/hwasan_thread.h @@ -42,7 +42,7 @@ class Thread { HeapAllocationsRingBuffer *heap_allocations() { return heap_allocations_; } StackAllocationsRingBuffer *stack_allocations() { return stack_allocations_; } - tag_t GenerateRandomTag(); + tag_t GenerateRandomTag(uptr num_bits = kTagBits); void DisableTagging() { tagging_disabled_++; } void EnableTagging() { tagging_disabled_--; } @@ -74,8 +74,6 @@ class Thread { HeapAllocationsRingBuffer *heap_allocations_; StackAllocationsRingBuffer *stack_allocations_; - Thread *next_; // All live threads form a linked list. - u64 unique_id_; // counting from zero. u32 tagging_disabled_; // if non-zero, malloc uses zero tag in this thread. diff --git a/libsanitizer/hwasan/hwasan_thread_list.h b/libsanitizer/hwasan/hwasan_thread_list.h index 914b632..11c5863 100644 --- a/libsanitizer/hwasan/hwasan_thread_list.h +++ b/libsanitizer/hwasan/hwasan_thread_list.h @@ -66,40 +66,6 @@ static uptr RingBufferSize() { return 0; } -struct ThreadListHead { - Thread *list_; - - ThreadListHead() : list_(nullptr) {} - - void Push(Thread *t) { - t->next_ = list_; - list_ = t; - } - - Thread *Pop() { - Thread *t = list_; - if (t) - list_ = t->next_; - return t; - } - - void Remove(Thread *t) { - Thread **cur = &list_; - while (*cur != t) cur = &(*cur)->next_; - CHECK(*cur && "thread not found"); - *cur = (*cur)->next_; - } - - template <class CB> - void ForEach(CB cb) { - Thread *t = list_; - while (t) { - cb(t); - t = t->next_; - } - } -}; - struct ThreadStats { uptr n_live_threads; uptr total_stack_size; @@ -120,17 +86,23 @@ class HwasanThreadList { } Thread *CreateCurrentThread() { - Thread *t; + Thread *t = nullptr; { - SpinMutexLock l(&list_mutex_); - t = free_list_.Pop(); - if (t) { - uptr start = (uptr)t - ring_buffer_size_; - internal_memset((void *)start, 0, ring_buffer_size_ + sizeof(Thread)); - } else { - t = AllocThread(); + SpinMutexLock l(&free_list_mutex_); + if (!free_list_.empty()) { + t = free_list_.back(); + free_list_.pop_back(); } - live_list_.Push(t); + } + if (t) { + uptr start = (uptr)t - ring_buffer_size_; + internal_memset((void *)start, 0, ring_buffer_size_ + sizeof(Thread)); + } else { + t = AllocThread(); + } + { + SpinMutexLock l(&live_list_mutex_); + live_list_.push_back(t); } t->Init((uptr)t - ring_buffer_size_, ring_buffer_size_); AddThreadStats(t); @@ -142,13 +114,26 @@ class HwasanThreadList { ReleaseMemoryPagesToOS(start, start + thread_alloc_size_); } + void RemoveThreadFromLiveList(Thread *t) { + SpinMutexLock l(&live_list_mutex_); + for (Thread *&t2 : live_list_) + if (t2 == t) { + // To remove t2, copy the last element of the list in t2's position, and + // pop_back(). This works even if t2 is itself the last element. + t2 = live_list_.back(); + live_list_.pop_back(); + return; + } + CHECK(0 && "thread not found in live list"); + } + void ReleaseThread(Thread *t) { RemoveThreadStats(t); t->Destroy(); - SpinMutexLock l(&list_mutex_); - live_list_.Remove(t); - free_list_.Push(t); DontNeedThread(t); + RemoveThreadFromLiveList(t); + SpinMutexLock l(&free_list_mutex_); + free_list_.push_back(t); } Thread *GetThreadByBufferAddress(uptr p) { @@ -165,8 +150,8 @@ class HwasanThreadList { template <class CB> void VisitAllLiveThreads(CB cb) { - SpinMutexLock l(&list_mutex_); - live_list_.ForEach(cb); + SpinMutexLock l(&live_list_mutex_); + for (Thread *t : live_list_) cb(t); } void AddThreadStats(Thread *t) { @@ -188,6 +173,7 @@ class HwasanThreadList { private: Thread *AllocThread() { + SpinMutexLock l(&free_space_mutex_); uptr align = ring_buffer_size_ * 2; CHECK(IsAligned(free_space_, align)); Thread *t = (Thread *)(free_space_ + ring_buffer_size_); @@ -196,14 +182,16 @@ class HwasanThreadList { return t; } + SpinMutex free_space_mutex_; uptr free_space_; uptr free_space_end_; uptr ring_buffer_size_; uptr thread_alloc_size_; - ThreadListHead free_list_; - ThreadListHead live_list_; - SpinMutex list_mutex_; + SpinMutex free_list_mutex_; + InternalMmapVector<Thread *> free_list_; + SpinMutex live_list_mutex_; + InternalMmapVector<Thread *> live_list_; ThreadStats stats_; SpinMutex stats_mutex_; diff --git a/libsanitizer/include/sanitizer/common_interface_defs.h b/libsanitizer/include/sanitizer/common_interface_defs.h index b4f977b..cd69285 100644 --- a/libsanitizer/include/sanitizer/common_interface_defs.h +++ b/libsanitizer/include/sanitizer/common_interface_defs.h @@ -43,6 +43,9 @@ void __sanitizer_set_report_path(const char *path); // Tell the tools to write their reports to the provided file descriptor // (casted to void *). void __sanitizer_set_report_fd(void *fd); +// Get the current full report file path, if a path was specified by +// an earlier call to __sanitizer_set_report_path. Returns null otherwise. +const char *__sanitizer_get_report_path(); // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions diff --git a/libsanitizer/include/sanitizer/dfsan_interface.h b/libsanitizer/include/sanitizer/dfsan_interface.h index 18b2c81..40f9379 100644 --- a/libsanitizer/include/sanitizer/dfsan_interface.h +++ b/libsanitizer/include/sanitizer/dfsan_interface.h @@ -22,6 +22,7 @@ extern "C" { #endif typedef uint16_t dfsan_label; +typedef uint32_t dfsan_origin; /// Stores information associated with a specific label identifier. A label /// may be a base label created using dfsan_create_label, with associated @@ -63,6 +64,12 @@ void dfsan_add_label(dfsan_label label, void *addr, size_t size); /// value. dfsan_label dfsan_get_label(long data); +/// Retrieves the immediate origin associated with the given data. The returned +/// origin may point to another origin. +/// +/// The type of 'data' is arbitrary. +dfsan_origin dfsan_get_origin(long data); + /// Retrieves the label associated with the data at the given address. dfsan_label dfsan_read_label(const void *addr, size_t size); @@ -110,6 +117,15 @@ void dfsan_weak_hook_memcmp(void *caller_pc, const void *s1, const void *s2, void dfsan_weak_hook_strncmp(void *caller_pc, const char *s1, const char *s2, size_t n, dfsan_label s1_label, dfsan_label s2_label, dfsan_label n_label); + +/// Prints the origin trace of the label at the address addr to stderr. It also +/// prints description at the beginning of the trace. If origin tracking is not +/// on, or the address is not labeled, it prints nothing. +void dfsan_print_origin_trace(const void *addr, const char *description); + +/// Retrieves the very first origin associated with the data at the given +/// address. +dfsan_origin dfsan_get_init_origin(const void *addr); #ifdef __cplusplus } // extern "C" diff --git a/libsanitizer/include/sanitizer/hwasan_interface.h b/libsanitizer/include/sanitizer/hwasan_interface.h index 4c9ad13..14035c0 100644 --- a/libsanitizer/include/sanitizer/hwasan_interface.h +++ b/libsanitizer/include/sanitizer/hwasan_interface.h @@ -73,6 +73,9 @@ extern "C" { * accessed through the pointer in x, or -1 if the whole range is good. */ intptr_t __hwasan_test_shadow(const volatile void *x, size_t size); + /* Sets the callback function to be called during HWASan error reporting. */ + void __hwasan_set_error_report_callback(void (*callback)(const char *)); + int __sanitizer_posix_memalign(void **memptr, size_t alignment, size_t size); void * __sanitizer_memalign(size_t alignment, size_t size); void * __sanitizer_aligned_alloc(size_t alignment, size_t size); diff --git a/libsanitizer/include/sanitizer/memprof_interface.h b/libsanitizer/include/sanitizer/memprof_interface.h index a721260..76031de 100644 --- a/libsanitizer/include/sanitizer/memprof_interface.h +++ b/libsanitizer/include/sanitizer/memprof_interface.h @@ -53,6 +53,11 @@ void __memprof_print_accumulated_stats(void); /// \returns Default options string. const char *__memprof_default_options(void); +/// Prints the memory profile to the current profile file. +/// +/// \returns 0 on success. +int __memprof_profile_dump(void); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/tsan_interface.h b/libsanitizer/include/sanitizer/tsan_interface.h index 96b8ad5..565aa39 100644 --- a/libsanitizer/include/sanitizer/tsan_interface.h +++ b/libsanitizer/include/sanitizer/tsan_interface.h @@ -67,6 +67,12 @@ static const unsigned __tsan_mutex_recursive_lock = 1 << 6; // the corresponding __tsan_mutex_post_lock annotation. static const unsigned __tsan_mutex_recursive_unlock = 1 << 7; +// Convenient composed constants. +static const unsigned __tsan_mutex_try_read_lock = + __tsan_mutex_read_lock | __tsan_mutex_try_lock; +static const unsigned __tsan_mutex_try_read_lock_failed = + __tsan_mutex_try_read_lock | __tsan_mutex_try_lock_failed; + // Annotate creation of a mutex. // Supported flags: mutex creation flags. void __tsan_mutex_create(void *addr, unsigned flags); @@ -141,7 +147,7 @@ void __tsan_external_write(void *addr, void *caller_pc, void *tag); // and freed by __tsan_destroy_fiber. // - TSAN context of current fiber or thread can be obtained // by calling __tsan_get_current_fiber. -// - __tsan_switch_to_fiber should be called immediatly before switch +// - __tsan_switch_to_fiber should be called immediately before switch // to fiber, such as call of swapcontext. // - Fiber name can be set by __tsan_set_fiber_name. void *__tsan_get_current_fiber(void); @@ -154,6 +160,15 @@ void __tsan_set_fiber_name(void *fiber, const char *name); // Do not establish a happens-before relation between fibers static const unsigned __tsan_switch_to_fiber_no_sync = 1 << 0; +// User-provided callback invoked on TSan initialization. +void __tsan_on_initialize(); + +// User-provided callback invoked on TSan shutdown. +// `failed` - Nonzero if TSan did detect issues, zero otherwise. +// Return `0` if TSan should exit as if no issues were detected. Return nonzero +// if TSan should exit as if issues were detected. +int __tsan_on_finalize(int failed); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/tsan_interface_atomic.h b/libsanitizer/include/sanitizer/tsan_interface_atomic.h index 8052bc1..5e41e22 100644 --- a/libsanitizer/include/sanitizer/tsan_interface_atomic.h +++ b/libsanitizer/include/sanitizer/tsan_interface_atomic.h @@ -30,7 +30,7 @@ __extension__ typedef __int128 __tsan_atomic128; #endif // Part of ABI, do not change. -// https://github.com/llvm/llvm-project/blob/master/libcxx/include/atomic +// https://github.com/llvm/llvm-project/blob/main/libcxx/include/atomic typedef enum { __tsan_memory_order_relaxed, __tsan_memory_order_consume, diff --git a/libsanitizer/interception/interception_linux.cpp b/libsanitizer/interception/interception_linux.cpp index 6883608..5111a87 100644 --- a/libsanitizer/interception/interception_linux.cpp +++ b/libsanitizer/interception/interception_linux.cpp @@ -63,8 +63,8 @@ bool InterceptFunction(const char *name, uptr *ptr_to_real, uptr func, return addr && (func == wrapper); } -// Android and Solaris do not have dlvsym -#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS +// dlvsym is a GNU extension supported by some other platforms. +#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD static void *GetFuncAddr(const char *name, const char *ver) { return dlvsym(RTLD_NEXT, name, ver); } @@ -75,7 +75,7 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real, *ptr_to_real = (uptr)addr; return addr && (func == wrapper); } -#endif // !SANITIZER_ANDROID +#endif // SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD } // namespace __interception diff --git a/libsanitizer/interception/interception_linux.h b/libsanitizer/interception/interception_linux.h index 097375f..a08f8cb 100644 --- a/libsanitizer/interception/interception_linux.h +++ b/libsanitizer/interception/interception_linux.h @@ -35,8 +35,8 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real, (::__interception::uptr) & (func), \ (::__interception::uptr) & WRAP(func)) -// Android and Solaris do not have dlvsym -#if !SANITIZER_ANDROID && !SANITIZER_SOLARIS +// dlvsym is a GNU extension supported by some other platforms. +#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD #define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ ::__interception::InterceptFunction( \ #func, symver, \ @@ -46,7 +46,7 @@ bool InterceptFunction(const char *name, const char *ver, uptr *ptr_to_real, #else #define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) -#endif // !SANITIZER_ANDROID && !SANITIZER_SOLARIS +#endif // SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD #endif // INTERCEPTION_LINUX_H #endif // SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_NETBSD || diff --git a/libsanitizer/interception/interception_win.cpp b/libsanitizer/interception/interception_win.cpp index 1a1c327..98bc756 100644 --- a/libsanitizer/interception/interception_win.cpp +++ b/libsanitizer/interception/interception_win.cpp @@ -136,7 +136,7 @@ namespace __interception { static const int kAddressLength = FIRST_32_SECOND_64(4, 8); static const int kJumpInstructionLength = 5; static const int kShortJumpInstructionLength = 2; -static const int kIndirectJumpInstructionLength = 6; +UNUSED static const int kIndirectJumpInstructionLength = 6; static const int kBranchLength = FIRST_32_SECOND_64(kJumpInstructionLength, kIndirectJumpInstructionLength); static const int kDirectBranchLength = kBranchLength + kAddressLength; @@ -165,7 +165,7 @@ static uptr GetMmapGranularity() { return si.dwAllocationGranularity; } -static uptr RoundUpTo(uptr size, uptr boundary) { +UNUSED static uptr RoundUpTo(uptr size, uptr boundary) { return (size + boundary - 1) & ~(boundary - 1); } @@ -309,7 +309,7 @@ struct TrampolineMemoryRegion { uptr max_size; }; -static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig +UNUSED static const uptr kTrampolineScanLimitRange = 1 << 31; // 2 gig static const int kMaxTrampolineRegion = 1024; static TrampolineMemoryRegion TrampolineRegions[kMaxTrampolineRegion]; diff --git a/libsanitizer/lsan/lsan_allocator.cpp b/libsanitizer/lsan/lsan_allocator.cpp index d86c392..91e34eb 100644 --- a/libsanitizer/lsan/lsan_allocator.cpp +++ b/libsanitizer/lsan/lsan_allocator.cpp @@ -123,14 +123,18 @@ void Deallocate(void *p) { void *Reallocate(const StackTrace &stack, void *p, uptr new_size, uptr alignment) { - RegisterDeallocation(p); if (new_size > max_malloc_size) { - allocator.Deallocate(GetAllocatorCache(), p); - return ReportAllocationSizeTooBig(new_size, stack); + ReportAllocationSizeTooBig(new_size, stack); + return nullptr; } - p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment); - RegisterAllocation(stack, p, new_size); - return p; + RegisterDeallocation(p); + void *new_p = + allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment); + if (new_p) + RegisterAllocation(stack, new_p, new_size); + else if (new_size != 0) + RegisterAllocation(stack, p, new_size); + return new_p; } void GetAllocatorCacheRange(uptr *begin, uptr *end) { @@ -309,6 +313,16 @@ IgnoreObjectResult IgnoreObjectLocked(const void *p) { return kIgnoreObjectInvalid; } } + +void GetAdditionalThreadContextPtrs(ThreadContextBase *tctx, void *ptrs) { + // This function can be used to treat memory reachable from `tctx` as live. + // This is useful for threads that have been created but not yet started. + + // This is currently a no-op because the LSan `pthread_create()` interceptor + // blocks until the child thread starts which keeps the thread's `arg` pointer + // live. +} + } // namespace __lsan using namespace __lsan; diff --git a/libsanitizer/lsan/lsan_allocator.h b/libsanitizer/lsan/lsan_allocator.h index 17e13cd..9d76378 100644 --- a/libsanitizer/lsan/lsan_allocator.h +++ b/libsanitizer/lsan/lsan_allocator.h @@ -50,7 +50,7 @@ struct ChunkMetadata { }; #if defined(__mips64) || defined(__aarch64__) || defined(__i386__) || \ - defined(__arm__) + defined(__arm__) || SANITIZER_RISCV64 template <typename AddressSpaceViewTy> struct AP32 { static const uptr kSpaceBeg = 0; diff --git a/libsanitizer/lsan/lsan_common.cpp b/libsanitizer/lsan/lsan_common.cpp index 9e23aa9..74400d2 100644 --- a/libsanitizer/lsan/lsan_common.cpp +++ b/libsanitizer/lsan/lsan_common.cpp @@ -65,8 +65,34 @@ void RegisterLsanFlags(FlagParser *parser, Flags *f) { if (flags()->log_threads) Report(__VA_ARGS__); \ } while (0) -ALIGNED(64) static char suppression_placeholder[sizeof(SuppressionContext)]; -static SuppressionContext *suppression_ctx = nullptr; +class LeakSuppressionContext { + bool parsed = false; + SuppressionContext context; + bool suppressed_stacks_sorted = true; + InternalMmapVector<u32> suppressed_stacks; + + Suppression *GetSuppressionForAddr(uptr addr); + void LazyInit(); + + public: + LeakSuppressionContext(const char *supprression_types[], + int suppression_types_num) + : context(supprression_types, suppression_types_num) {} + + Suppression *GetSuppressionForStack(u32 stack_trace_id); + + const InternalMmapVector<u32> &GetSortedSuppressedStacks() { + if (!suppressed_stacks_sorted) { + suppressed_stacks_sorted = true; + SortAndDedup(suppressed_stacks); + } + return suppressed_stacks; + } + void PrintMatchedSuppressions(); +}; + +ALIGNED(64) static char suppression_placeholder[sizeof(LeakSuppressionContext)]; +static LeakSuppressionContext *suppression_ctx = nullptr; static const char kSuppressionLeak[] = "leak"; static const char *kSuppressionTypes[] = { kSuppressionLeak }; static const char kStdSuppressions[] = @@ -86,14 +112,20 @@ static const char kStdSuppressions[] = void InitializeSuppressions() { CHECK_EQ(nullptr, suppression_ctx); suppression_ctx = new (suppression_placeholder) - SuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); - suppression_ctx->ParseFromFile(flags()->suppressions); - if (&__lsan_default_suppressions) - suppression_ctx->Parse(__lsan_default_suppressions()); - suppression_ctx->Parse(kStdSuppressions); + LeakSuppressionContext(kSuppressionTypes, ARRAY_SIZE(kSuppressionTypes)); +} + +void LeakSuppressionContext::LazyInit() { + if (!parsed) { + parsed = true; + context.ParseFromFile(flags()->suppressions); + if (&__lsan_default_suppressions) + context.Parse(__lsan_default_suppressions()); + context.Parse(kStdSuppressions); + } } -static SuppressionContext *GetSuppressionContext() { +static LeakSuppressionContext *GetSuppressionContext() { CHECK(suppression_ctx); return suppression_ctx; } @@ -221,6 +253,27 @@ extern "C" SANITIZER_WEAK_ATTRIBUTE void __libc_iterate_dynamic_tls( pid_t, void (*cb)(void *, void *, uptr, void *), void *); #endif +static void ProcessThreadRegistry(Frontier *frontier) { + InternalMmapVector<uptr> ptrs; + GetThreadRegistryLocked()->RunCallbackForEachThreadLocked( + GetAdditionalThreadContextPtrs, &ptrs); + + for (uptr i = 0; i < ptrs.size(); ++i) { + void *ptr = reinterpret_cast<void *>(ptrs[i]); + uptr chunk = PointsIntoChunk(ptr); + if (!chunk) + continue; + LsanMetadata m(chunk); + if (!m.allocated()) + continue; + + // Mark as reachable and add to frontier. + LOG_POINTERS("Treating pointer %p from ThreadContext as reachable\n", ptr); + m.set_tag(kReachable); + frontier->push_back(chunk); + } +} + // Scans thread data (stacks and TLS) for heap pointers. static void ProcessThreads(SuspendedThreadsList const &suspended_threads, Frontier *frontier) { @@ -315,15 +368,15 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, __libc_iterate_dynamic_tls(os_id, cb, frontier); #else if (dtls && !DTLSInDestruction(dtls)) { - for (uptr j = 0; j < dtls->dtv_size; ++j) { - uptr dtls_beg = dtls->dtv[j].beg; - uptr dtls_end = dtls_beg + dtls->dtv[j].size; + ForEachDVT(dtls, [&](const DTLS::DTV &dtv, int id) { + uptr dtls_beg = dtv.beg; + uptr dtls_end = dtls_beg + dtv.size; if (dtls_beg < dtls_end) { - LOG_THREADS("DTLS %zu at %p-%p.\n", j, dtls_beg, dtls_end); + LOG_THREADS("DTLS %zu at %p-%p.\n", id, dtls_beg, dtls_end); ScanRangeForPointers(dtls_beg, dtls_end, frontier, "DTLS", kReachable); } - } + }); } else { // We are handling a thread with DTLS under destruction. Log about // this and continue. @@ -332,6 +385,9 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, #endif } } + + // Add pointers reachable from ThreadContexts + ProcessThreadRegistry(frontier); } #endif // SANITIZER_FUCHSIA @@ -390,6 +446,24 @@ static void MarkIndirectlyLeakedCb(uptr chunk, void *arg) { } } +static void IgnoredSuppressedCb(uptr chunk, void *arg) { + CHECK(arg); + chunk = GetUserBegin(chunk); + LsanMetadata m(chunk); + if (!m.allocated() || m.tag() == kIgnored) + return; + + const InternalMmapVector<u32> &suppressed = + *static_cast<const InternalMmapVector<u32> *>(arg); + uptr idx = InternalLowerBound(suppressed, m.stack_trace_id()); + if (idx >= suppressed.size() || m.stack_trace_id() != suppressed[idx]) + return; + + LOG_POINTERS("Suppressed: chunk %p-%p of size %zu.\n", chunk, + chunk + m.requested_size(), m.requested_size()); + m.set_tag(kIgnored); +} + // ForEachChunk callback. If chunk is marked as ignored, adds its address to // frontier. static void CollectIgnoredCb(uptr chunk, void *arg) { @@ -473,6 +547,12 @@ void ProcessPC(Frontier *frontier) { // Sets the appropriate tag on each chunk. static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads, Frontier *frontier) { + const InternalMmapVector<u32> &suppressed_stacks = + GetSuppressionContext()->GetSortedSuppressedStacks(); + if (!suppressed_stacks.empty()) { + ForEachChunk(IgnoredSuppressedCb, + const_cast<InternalMmapVector<u32> *>(&suppressed_stacks)); + } ForEachChunk(CollectIgnoredCb, frontier); ProcessGlobalRegions(frontier); ProcessThreads(suspended_threads, frontier); @@ -532,18 +612,20 @@ static void CollectLeaksCb(uptr chunk, void *arg) { } } -static void PrintMatchedSuppressions() { +void LeakSuppressionContext::PrintMatchedSuppressions() { InternalMmapVector<Suppression *> matched; - GetSuppressionContext()->GetMatched(&matched); + context.GetMatched(&matched); if (!matched.size()) return; const char *line = "-----------------------------------------------------"; Printf("%s\n", line); Printf("Suppressions used:\n"); Printf(" count bytes template\n"); - for (uptr i = 0; i < matched.size(); i++) - Printf("%7zu %10zu %s\n", static_cast<uptr>(atomic_load_relaxed( - &matched[i]->hit_count)), matched[i]->weight, matched[i]->templ); + for (uptr i = 0; i < matched.size(); i++) { + Printf("%7zu %10zu %s\n", + static_cast<uptr>(atomic_load_relaxed(&matched[i]->hit_count)), + matched[i]->weight, matched[i]->templ); + } Printf("%s\n\n", line); } @@ -551,8 +633,7 @@ static void ReportIfNotSuspended(ThreadContextBase *tctx, void *arg) { const InternalMmapVector<tid_t> &suspended_threads = *(const InternalMmapVector<tid_t> *)arg; if (tctx->status == ThreadStatusRunning) { - uptr i = InternalLowerBound(suspended_threads, 0, suspended_threads.size(), - tctx->os_id, CompareLess<int>()); + uptr i = InternalLowerBound(suspended_threads, tctx->os_id); if (i >= suspended_threads.size() || suspended_threads[i] != tctx->os_id) Report("Running thread %d was not suspended. False leaks are possible.\n", tctx->os_id); @@ -595,43 +676,68 @@ static void CheckForLeaksCallback(const SuspendedThreadsList &suspended_threads, param->success = true; } -static bool CheckForLeaks() { - if (&__lsan_is_turned_off && __lsan_is_turned_off()) - return false; - EnsureMainThreadIDIsCorrect(); - CheckForLeaksParam param; - LockStuffAndStopTheWorld(CheckForLeaksCallback, ¶m); - - if (!param.success) { - Report("LeakSanitizer has encountered a fatal error.\n"); - Report( - "HINT: For debugging, try setting environment variable " - "LSAN_OPTIONS=verbosity=1:log_threads=1\n"); - Report( - "HINT: LeakSanitizer does not work under ptrace (strace, gdb, etc)\n"); - Die(); - } - param.leak_report.ApplySuppressions(); - uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount(); - if (unsuppressed_count > 0) { +static bool PrintResults(LeakReport &report) { + uptr unsuppressed_count = report.UnsuppressedLeakCount(); + if (unsuppressed_count) { Decorator d; - Printf("\n" - "=================================================================" - "\n"); + Printf( + "\n" + "=================================================================" + "\n"); Printf("%s", d.Error()); Report("ERROR: LeakSanitizer: detected memory leaks\n"); Printf("%s", d.Default()); - param.leak_report.ReportTopLeaks(flags()->max_leaks); + report.ReportTopLeaks(flags()->max_leaks); } if (common_flags()->print_suppressions) - PrintMatchedSuppressions(); + GetSuppressionContext()->PrintMatchedSuppressions(); if (unsuppressed_count > 0) { - param.leak_report.PrintSummary(); + report.PrintSummary(); return true; } return false; } +static bool CheckForLeaks() { + if (&__lsan_is_turned_off && __lsan_is_turned_off()) + return false; + // Inside LockStuffAndStopTheWorld we can't run symbolizer, so we can't match + // suppressions. However if a stack id was previously suppressed, it should be + // suppressed in future checks as well. + for (int i = 0;; ++i) { + EnsureMainThreadIDIsCorrect(); + CheckForLeaksParam param; + LockStuffAndStopTheWorld(CheckForLeaksCallback, ¶m); + if (!param.success) { + Report("LeakSanitizer has encountered a fatal error.\n"); + Report( + "HINT: For debugging, try setting environment variable " + "LSAN_OPTIONS=verbosity=1:log_threads=1\n"); + Report( + "HINT: LeakSanitizer does not work under ptrace (strace, gdb, " + "etc)\n"); + Die(); + } + // No new suppressions stacks, so rerun will not help and we can report. + if (!param.leak_report.ApplySuppressions()) + return PrintResults(param.leak_report); + + // No indirect leaks to report, so we are done here. + if (!param.leak_report.IndirectUnsuppressedLeakCount()) + return PrintResults(param.leak_report); + + if (i >= 8) { + Report("WARNING: LeakSanitizer gave up on indirect leaks suppression.\n"); + return PrintResults(param.leak_report); + } + + // We found a new previously unseen suppressed call stack. Rerun to make + // sure it does not hold indirect leaks. + VReport(1, "Rerun with %zu suppressed stacks.", + GetSuppressionContext()->GetSortedSuppressedStacks().size()); + } +} + static bool has_reported_leaks = false; bool HasReportedLeaks() { return has_reported_leaks; } @@ -652,21 +758,20 @@ static int DoRecoverableLeakCheck() { void DoRecoverableLeakCheckVoid() { DoRecoverableLeakCheck(); } -static Suppression *GetSuppressionForAddr(uptr addr) { +Suppression *LeakSuppressionContext::GetSuppressionForAddr(uptr addr) { Suppression *s = nullptr; // Suppress by module name. - SuppressionContext *suppressions = GetSuppressionContext(); if (const char *module_name = Symbolizer::GetOrInit()->GetModuleNameForPc(addr)) - if (suppressions->Match(module_name, kSuppressionLeak, &s)) + if (context.Match(module_name, kSuppressionLeak, &s)) return s; // Suppress by file or function name. SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(addr); for (SymbolizedStack *cur = frames; cur; cur = cur->next) { - if (suppressions->Match(cur->info.function, kSuppressionLeak, &s) || - suppressions->Match(cur->info.file, kSuppressionLeak, &s)) { + if (context.Match(cur->info.function, kSuppressionLeak, &s) || + context.Match(cur->info.file, kSuppressionLeak, &s)) { break; } } @@ -674,12 +779,18 @@ static Suppression *GetSuppressionForAddr(uptr addr) { return s; } -static Suppression *GetSuppressionForStack(u32 stack_trace_id) { +Suppression *LeakSuppressionContext::GetSuppressionForStack( + u32 stack_trace_id) { + LazyInit(); StackTrace stack = StackDepotGet(stack_trace_id); for (uptr i = 0; i < stack.size; i++) { Suppression *s = GetSuppressionForAddr( StackTrace::GetPreviousInstructionPc(stack.trace[i])); - if (s) return s; + if (s) { + suppressed_stacks_sorted = false; + suppressed_stacks.push_back(stack_trace_id); + return s; + } } return nullptr; } @@ -784,22 +895,27 @@ void LeakReport::PrintSummary() { bytes += leaks_[i].total_size; allocations += leaks_[i].hit_count; } - InternalScopedString summary(kMaxSummaryLength); + InternalScopedString summary; summary.append("%zu byte(s) leaked in %zu allocation(s).", bytes, allocations); ReportErrorSummary(summary.data()); } -void LeakReport::ApplySuppressions() { +uptr LeakReport::ApplySuppressions() { + LeakSuppressionContext *suppressions = GetSuppressionContext(); + uptr new_suppressions = false; for (uptr i = 0; i < leaks_.size(); i++) { - Suppression *s = GetSuppressionForStack(leaks_[i].stack_trace_id); + Suppression *s = + suppressions->GetSuppressionForStack(leaks_[i].stack_trace_id); if (s) { s->weight += leaks_[i].total_size; atomic_store_relaxed(&s->hit_count, atomic_load_relaxed(&s->hit_count) + leaks_[i].hit_count); leaks_[i].is_suppressed = true; + ++new_suppressions; } } + return new_suppressions; } uptr LeakReport::UnsuppressedLeakCount() { @@ -809,6 +925,14 @@ uptr LeakReport::UnsuppressedLeakCount() { return result; } +uptr LeakReport::IndirectUnsuppressedLeakCount() { + uptr result = 0; + for (uptr i = 0; i < leaks_.size(); i++) + if (!leaks_[i].is_suppressed && !leaks_[i].is_directly_leaked) + result++; + return result; +} + } // namespace __lsan #else // CAN_SANITIZE_LEAKS namespace __lsan { diff --git a/libsanitizer/lsan/lsan_common.h b/libsanitizer/lsan/lsan_common.h index 1fdce08..fe855cf 100644 --- a/libsanitizer/lsan/lsan_common.h +++ b/libsanitizer/lsan/lsan_common.h @@ -41,6 +41,8 @@ #define CAN_SANITIZE_LEAKS 1 #elif defined(__arm__) && SANITIZER_LINUX #define CAN_SANITIZE_LEAKS 1 +#elif SANITIZER_RISCV64 && SANITIZER_LINUX +#define CAN_SANITIZE_LEAKS 1 #elif SANITIZER_NETBSD || SANITIZER_FUCHSIA #define CAN_SANITIZE_LEAKS 1 #else @@ -50,6 +52,7 @@ namespace __sanitizer { class FlagParser; class ThreadRegistry; +class ThreadContextBase; struct DTLS; } @@ -63,8 +66,6 @@ enum ChunkTag { kIgnored = 3 }; -const u32 kInvalidTid = (u32) -1; - struct Flags { #define LSAN_FLAG(Type, Name, DefaultValue, Description) Type Name; #include "lsan_flags.inc" @@ -103,8 +104,9 @@ class LeakReport { ChunkTag tag); void ReportTopLeaks(uptr max_leaks); void PrintSummary(); - void ApplySuppressions(); + uptr ApplySuppressions(); uptr UnsuppressedLeakCount(); + uptr IndirectUnsuppressedLeakCount(); private: void PrintReportForLeak(uptr index); @@ -141,6 +143,7 @@ InternalMmapVector<RootRegion> const *GetRootRegions(); void ScanRootRegion(Frontier *frontier, RootRegion const ®ion, uptr region_begin, uptr region_end, bool is_readable); void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg); +void GetAdditionalThreadContextPtrs(ThreadContextBase *tctx, void *ptrs); // Run stoptheworld while holding any platform-specific locks, as well as the // allocator and thread registry locks. void LockStuffAndStopTheWorld(StopTheWorldCallback callback, diff --git a/libsanitizer/lsan/lsan_common_fuchsia.cpp b/libsanitizer/lsan/lsan_common_fuchsia.cpp index 3c62c94..2d35fa5 100644 --- a/libsanitizer/lsan/lsan_common_fuchsia.cpp +++ b/libsanitizer/lsan/lsan_common_fuchsia.cpp @@ -107,9 +107,7 @@ void LockStuffAndStopTheWorld(StopTheWorldCallback callback, auto params = static_cast<const Params *>(data); uptr begin = reinterpret_cast<uptr>(chunk); uptr end = begin + size; - auto i = __sanitizer::InternalLowerBound(params->allocator_caches, 0, - params->allocator_caches.size(), - begin, CompareLess<uptr>()); + auto i = __sanitizer::InternalLowerBound(params->allocator_caches, begin); if (i < params->allocator_caches.size() && params->allocator_caches[i] >= begin && end - params->allocator_caches[i] <= sizeof(AllocatorCache)) { diff --git a/libsanitizer/lsan/lsan_fuchsia.h b/libsanitizer/lsan/lsan_fuchsia.h index 65d20ea..e730d8f 100644 --- a/libsanitizer/lsan/lsan_fuchsia.h +++ b/libsanitizer/lsan/lsan_fuchsia.h @@ -23,7 +23,7 @@ namespace __lsan { -class ThreadContext : public ThreadContextLsanBase { +class ThreadContext final : public ThreadContextLsanBase { public: explicit ThreadContext(int tid); void OnCreated(void *arg) override; diff --git a/libsanitizer/lsan/lsan_interceptors.cpp b/libsanitizer/lsan/lsan_interceptors.cpp index bf8d316..90a90a5 100644 --- a/libsanitizer/lsan/lsan_interceptors.cpp +++ b/libsanitizer/lsan/lsan_interceptors.cpp @@ -460,7 +460,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, if (res == 0) { int tid = ThreadCreate(GetCurrentThread(), *(uptr *)th, IsStateDetached(detached)); - CHECK_NE(tid, 0); + CHECK_NE(tid, kMainTid); atomic_store(&p.tid, tid, memory_order_release); while (atomic_load(&p.tid, memory_order_acquire) != 0) internal_sched_yield(); diff --git a/libsanitizer/lsan/lsan_posix.cpp b/libsanitizer/lsan/lsan_posix.cpp index 8e05915..5d1c3f6 100644 --- a/libsanitizer/lsan/lsan_posix.cpp +++ b/libsanitizer/lsan/lsan_posix.cpp @@ -48,7 +48,7 @@ void ThreadStart(u32 tid, tid_t os_id, ThreadType thread_type) { OnStartedArgs args; uptr stack_size = 0; uptr tls_size = 0; - GetThreadStackAndTls(tid == 0, &args.stack_begin, &stack_size, + GetThreadStackAndTls(tid == kMainTid, &args.stack_begin, &stack_size, &args.tls_begin, &tls_size); args.stack_end = args.stack_begin + stack_size; args.tls_end = args.tls_begin + tls_size; @@ -75,8 +75,8 @@ bool GetThreadRangesLocked(tid_t os_id, uptr *stack_begin, uptr *stack_end, } void InitializeMainThread() { - u32 tid = ThreadCreate(0, 0, true); - CHECK_EQ(tid, 0); + u32 tid = ThreadCreate(kMainTid, 0, true); + CHECK_EQ(tid, kMainTid); ThreadStart(tid, GetTid()); } diff --git a/libsanitizer/lsan/lsan_thread.cpp b/libsanitizer/lsan/lsan_thread.cpp index 371a1f2..8efb54a 100644 --- a/libsanitizer/lsan/lsan_thread.cpp +++ b/libsanitizer/lsan/lsan_thread.cpp @@ -94,7 +94,7 @@ void ThreadJoin(u32 tid) { } void EnsureMainThreadIDIsCorrect() { - if (GetCurrentThread() == 0) + if (GetCurrentThread() == kMainTid) CurrentThreadContext()->os_id = GetTid(); } diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h index 33f89d6..eb836bc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_combined.h @@ -35,9 +35,9 @@ class CombinedAllocator { secondary_.InitLinkerInitialized(); } - void Init(s32 release_to_os_interval_ms) { + void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) { stats_.Init(); - primary_.Init(release_to_os_interval_ms); + primary_.Init(release_to_os_interval_ms, heap_start); secondary_.Init(); } diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h b/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h index b90dabb..fb5394c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_primary32.h @@ -119,7 +119,8 @@ class SizeClassAllocator32 { typedef SizeClassAllocator32<Params> ThisT; typedef SizeClassAllocator32LocalCache<ThisT> AllocatorCache; - void Init(s32 release_to_os_interval_ms) { + void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) { + CHECK(!heap_start); possible_regions.Init(); internal_memset(size_class_info_array, 0, sizeof(size_class_info_array)); } diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h b/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h index 0a18b0c..db30e13 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_primary64.h @@ -19,7 +19,7 @@ template<class SizeClassAllocator> struct SizeClassAllocator64LocalCache; // The template parameter Params is a class containing the actual parameters. // // Space: a portion of address space of kSpaceSize bytes starting at SpaceBeg. -// If kSpaceBeg is ~0 then SpaceBeg is chosen dynamically my mmap. +// If kSpaceBeg is ~0 then SpaceBeg is chosen dynamically by mmap. // Otherwise SpaceBeg=kSpaceBeg (fixed address). // kSpaceSize is a power of two. // At the beginning the entire space is mprotect-ed, then small parts of it @@ -69,25 +69,45 @@ class SizeClassAllocator64 { return base + (static_cast<uptr>(ptr32) << kCompactPtrScale); } - void Init(s32 release_to_os_interval_ms) { + // If heap_start is nonzero, assumes kSpaceSize bytes are already mapped R/W + // at heap_start and places the heap there. This mode requires kSpaceBeg == + // ~(uptr)0. + void Init(s32 release_to_os_interval_ms, uptr heap_start = 0) { uptr TotalSpaceSize = kSpaceSize + AdditionalSize(); - if (kUsingConstantSpaceBeg) { - CHECK(IsAligned(kSpaceBeg, SizeClassMap::kMaxSize)); - CHECK_EQ(kSpaceBeg, address_range.Init(TotalSpaceSize, - PrimaryAllocatorName, kSpaceBeg)); + PremappedHeap = heap_start != 0; + if (PremappedHeap) { + CHECK(!kUsingConstantSpaceBeg); + NonConstSpaceBeg = heap_start; + uptr RegionInfoSize = AdditionalSize(); + RegionInfoSpace = + address_range.Init(RegionInfoSize, PrimaryAllocatorName); + CHECK_NE(RegionInfoSpace, ~(uptr)0); + CHECK_EQ(RegionInfoSpace, + address_range.MapOrDie(RegionInfoSpace, RegionInfoSize, + "SizeClassAllocator: region info")); + MapUnmapCallback().OnMap(RegionInfoSpace, RegionInfoSize); } else { - // Combined allocator expects that an 2^N allocation is always aligned to - // 2^N. For this to work, the start of the space needs to be aligned as - // high as the largest size class (which also needs to be a power of 2). - NonConstSpaceBeg = address_range.InitAligned( - TotalSpaceSize, SizeClassMap::kMaxSize, PrimaryAllocatorName); - CHECK_NE(NonConstSpaceBeg, ~(uptr)0); + if (kUsingConstantSpaceBeg) { + CHECK(IsAligned(kSpaceBeg, SizeClassMap::kMaxSize)); + CHECK_EQ(kSpaceBeg, + address_range.Init(TotalSpaceSize, PrimaryAllocatorName, + kSpaceBeg)); + } else { + // Combined allocator expects that an 2^N allocation is always aligned + // to 2^N. For this to work, the start of the space needs to be aligned + // as high as the largest size class (which also needs to be a power of + // 2). + NonConstSpaceBeg = address_range.InitAligned( + TotalSpaceSize, SizeClassMap::kMaxSize, PrimaryAllocatorName); + CHECK_NE(NonConstSpaceBeg, ~(uptr)0); + } + RegionInfoSpace = SpaceEnd(); + MapWithCallbackOrDie(RegionInfoSpace, AdditionalSize(), + "SizeClassAllocator: region info"); } SetReleaseToOSIntervalMs(release_to_os_interval_ms); - MapWithCallbackOrDie(SpaceEnd(), AdditionalSize(), - "SizeClassAllocator: region info"); // Check that the RegionInfo array is aligned on the CacheLine size. - DCHECK_EQ(SpaceEnd() % kCacheLineSize, 0); + DCHECK_EQ(RegionInfoSpace % kCacheLineSize, 0); } s32 ReleaseToOSIntervalMs() const { @@ -144,6 +164,17 @@ class SizeClassAllocator64 { CompactPtrT *free_array = GetFreeArray(region_beg); BlockingMutexLock l(®ion->mutex); +#if SANITIZER_WINDOWS + /* On Windows unmapping of memory during __sanitizer_purge_allocator is + explicit and immediate, so unmapped regions must be explicitly mapped back + in when they are accessed again. */ + if (region->rtoi.last_released_bytes > 0) { + MmapFixedOrDie(region_beg, region->mapped_user, + "SizeClassAllocator: region data"); + region->rtoi.n_freed_at_last_release = 0; + region->rtoi.last_released_bytes = 0; + } +#endif if (UNLIKELY(region->num_freed_chunks < n_chunks)) { if (UNLIKELY(!PopulateFreeArray(stat, class_id, region, n_chunks - region->num_freed_chunks))) @@ -360,8 +391,7 @@ class SizeClassAllocator64 { } ~PackedCounterArray() { if (buffer) { - memory_mapper->UnmapPackedCounterArrayBuffer( - reinterpret_cast<uptr>(buffer), buffer_size); + memory_mapper->UnmapPackedCounterArrayBuffer(buffer, buffer_size); } } @@ -586,6 +616,11 @@ class SizeClassAllocator64 { atomic_sint32_t release_to_os_interval_ms_; + uptr RegionInfoSpace; + + // True if the user has already mapped the entire heap R/W. + bool PremappedHeap; + struct Stats { uptr n_allocated; uptr n_freed; @@ -615,7 +650,7 @@ class SizeClassAllocator64 { RegionInfo *GetRegionInfo(uptr class_id) const { DCHECK_LT(class_id, kNumClasses); - RegionInfo *regions = reinterpret_cast<RegionInfo *>(SpaceEnd()); + RegionInfo *regions = reinterpret_cast<RegionInfo *>(RegionInfoSpace); return ®ions[class_id]; } @@ -640,6 +675,9 @@ class SizeClassAllocator64 { } bool MapWithCallback(uptr beg, uptr size, const char *name) { + if (PremappedHeap) + return beg >= NonConstSpaceBeg && + beg + size <= NonConstSpaceBeg + kSpaceSize; uptr mapped = address_range.Map(beg, size, name); if (UNLIKELY(!mapped)) return false; @@ -649,11 +687,18 @@ class SizeClassAllocator64 { } void MapWithCallbackOrDie(uptr beg, uptr size, const char *name) { + if (PremappedHeap) { + CHECK_GE(beg, NonConstSpaceBeg); + CHECK_LE(beg + size, NonConstSpaceBeg + kSpaceSize); + return; + } CHECK_EQ(beg, address_range.MapOrDie(beg, size, name)); MapUnmapCallback().OnMap(beg, size); } void UnmapWithCallbackOrDie(uptr beg, uptr size) { + if (PremappedHeap) + return; MapUnmapCallback().OnUnmap(beg, size); address_range.Unmap(beg, size); } @@ -792,17 +837,16 @@ class SizeClassAllocator64 { return released_bytes; } - uptr MapPackedCounterArrayBuffer(uptr buffer_size) { + void *MapPackedCounterArrayBuffer(uptr buffer_size) { // TODO(alekseyshl): The idea to explore is to check if we have enough // space between num_freed_chunks*sizeof(CompactPtrT) and // mapped_free_array to fit buffer_size bytes and use that space instead // of mapping a temporary one. - return reinterpret_cast<uptr>( - MmapOrDieOnFatalError(buffer_size, "ReleaseToOSPageCounters")); + return MmapOrDieOnFatalError(buffer_size, "ReleaseToOSPageCounters"); } - void UnmapPackedCounterArrayBuffer(uptr buffer, uptr buffer_size) { - UnmapOrDie(reinterpret_cast<void *>(buffer), buffer_size); + void UnmapPackedCounterArrayBuffer(void *buffer, uptr buffer_size) { + UnmapOrDie(buffer, buffer_size); } // Releases [from, to) range of pages back to OS. @@ -823,6 +867,9 @@ class SizeClassAllocator64 { // Attempts to release RAM occupied by freed chunks back to OS. The region is // expected to be locked. + // + // TODO(morehouse): Support a callback on memory release so HWASan can release + // aliases as well. void MaybeReleaseToOS(uptr class_id, bool force) { RegionInfo *region = GetRegionInfo(class_id); const uptr chunk_size = ClassIdToSize(class_id); diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_size_class_map.h b/libsanitizer/sanitizer_common/sanitizer_allocator_size_class_map.h index 12d8c89..c50d133 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_size_class_map.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_size_class_map.h @@ -24,7 +24,7 @@ // E.g. with kNumBits==3 all size classes after 2^kMidSizeLog // look like 0b1xx0..0, where x is either 0 or 1. // -// Example: kNumBits=3, kMidSizeLog=4, kMidSizeLog=8, kMaxSizeLog=17: +// Example: kNumBits=3, kMinSizeLog=4, kMidSizeLog=8, kMaxSizeLog=17: // // Classes 1 - 16 correspond to sizes 16 to 256 (size = class_id * 16). // Next 4 classes: 256 + i * 64 (i = 1 to 4). diff --git a/libsanitizer/sanitizer_common/sanitizer_atomic_clang_mips.h b/libsanitizer/sanitizer_common/sanitizer_atomic_clang_mips.h index 59155e9..2b39097 100644 --- a/libsanitizer/sanitizer_common/sanitizer_atomic_clang_mips.h +++ b/libsanitizer/sanitizer_common/sanitizer_atomic_clang_mips.h @@ -41,7 +41,7 @@ inline atomic_uint64_t::Type atomic_fetch_add(volatile atomic_uint64_t *ptr, atomic_uint64_t::Type val, memory_order mo) { DCHECK(mo & - (memory_order_relaxed | memory_order_releasae | memory_order_seq_cst)); + (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); DCHECK(!((uptr)ptr % sizeof(*ptr))); atomic_uint64_t::Type ret; @@ -67,7 +67,7 @@ inline bool atomic_compare_exchange_strong(volatile atomic_uint64_t *ptr, atomic_uint64_t::Type xchg, memory_order mo) { DCHECK(mo & - (memory_order_relaxed | memory_order_releasae | memory_order_seq_cst)); + (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); DCHECK(!((uptr)ptr % sizeof(*ptr))); typedef atomic_uint64_t::Type Type; @@ -90,7 +90,7 @@ template <> inline atomic_uint64_t::Type atomic_load(const volatile atomic_uint64_t *ptr, memory_order mo) { DCHECK(mo & - (memory_order_relaxed | memory_order_releasae | memory_order_seq_cst)); + (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); DCHECK(!((uptr)ptr % sizeof(*ptr))); atomic_uint64_t::Type zero = 0; @@ -103,7 +103,7 @@ template <> inline void atomic_store(volatile atomic_uint64_t *ptr, atomic_uint64_t::Type v, memory_order mo) { DCHECK(mo & - (memory_order_relaxed | memory_order_releasae | memory_order_seq_cst)); + (memory_order_relaxed | memory_order_release | memory_order_seq_cst)); DCHECK(!((uptr)ptr % sizeof(*ptr))); __spin_lock(&lock.lock); diff --git a/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.cpp b/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.cpp new file mode 100644 index 0000000..250ac39 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.cpp @@ -0,0 +1,108 @@ +//===-- sanitizer_chained_origin_depot.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// A storage for chained origins. +//===----------------------------------------------------------------------===// + +#include "sanitizer_chained_origin_depot.h" + +namespace __sanitizer { + +bool ChainedOriginDepot::ChainedOriginDepotNode::eq( + u32 hash, const args_type &args) const { + return here_id == args.here_id && prev_id == args.prev_id; +} + +uptr ChainedOriginDepot::ChainedOriginDepotNode::storage_size( + const args_type &args) { + return sizeof(ChainedOriginDepotNode); +} + +/* This is murmur2 hash for the 64->32 bit case. + It does not behave all that well because the keys have a very biased + distribution (I've seen 7-element buckets with the table only 14% full). + + here_id is built of + * (1 bits) Reserved, zero. + * (8 bits) Part id = bits 13..20 of the hash value of here_id's key. + * (23 bits) Sequential number (each part has each own sequence). + + prev_id has either the same distribution as here_id (but with 3:8:21) + split, or one of two reserved values (-1) or (-2). Either case can + dominate depending on the workload. +*/ +u32 ChainedOriginDepot::ChainedOriginDepotNode::hash(const args_type &args) { + const u32 m = 0x5bd1e995; + const u32 seed = 0x9747b28c; + const u32 r = 24; + u32 h = seed; + u32 k = args.here_id; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + + k = args.prev_id; + k *= m; + k ^= k >> r; + k *= m; + h *= m; + h ^= k; + + h ^= h >> 13; + h *= m; + h ^= h >> 15; + return h; +} + +bool ChainedOriginDepot::ChainedOriginDepotNode::is_valid( + const args_type &args) { + return true; +} + +void ChainedOriginDepot::ChainedOriginDepotNode::store(const args_type &args, + u32 other_hash) { + here_id = args.here_id; + prev_id = args.prev_id; +} + +ChainedOriginDepot::ChainedOriginDepotNode::args_type +ChainedOriginDepot::ChainedOriginDepotNode::load() const { + args_type ret = {here_id, prev_id}; + return ret; +} + +ChainedOriginDepot::ChainedOriginDepotNode::Handle +ChainedOriginDepot::ChainedOriginDepotNode::get_handle() { + return Handle(this); +} + +ChainedOriginDepot::ChainedOriginDepot() {} + +StackDepotStats *ChainedOriginDepot::GetStats() { return depot.GetStats(); } + +bool ChainedOriginDepot::Put(u32 here_id, u32 prev_id, u32 *new_id) { + ChainedOriginDepotDesc desc = {here_id, prev_id}; + bool inserted; + ChainedOriginDepotNode::Handle h = depot.Put(desc, &inserted); + *new_id = h.valid() ? h.id() : 0; + return inserted; +} + +u32 ChainedOriginDepot::Get(u32 id, u32 *other) { + ChainedOriginDepotDesc desc = depot.Get(id); + *other = desc.prev_id; + return desc.here_id; +} + +void ChainedOriginDepot::LockAll() { depot.LockAll(); } + +void ChainedOriginDepot::UnlockAll() { depot.UnlockAll(); } + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.h b/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.h new file mode 100644 index 0000000..453cdf6 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_chained_origin_depot.h @@ -0,0 +1,88 @@ +//===-- sanitizer_chained_origin_depot.h ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// A storage for chained origins. +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_CHAINED_ORIGIN_DEPOT_H +#define SANITIZER_CHAINED_ORIGIN_DEPOT_H + +#include "sanitizer_common.h" +#include "sanitizer_stackdepotbase.h" + +namespace __sanitizer { + +class ChainedOriginDepot { + public: + ChainedOriginDepot(); + + // Gets the statistic of the origin chain storage. + StackDepotStats *GetStats(); + + // Stores a chain with StackDepot ID here_id and previous chain ID prev_id. + // If successful, returns true and the new chain id new_id. + // If the same element already exists, returns false and sets new_id to the + // existing ID. + bool Put(u32 here_id, u32 prev_id, u32 *new_id); + + // Retrieves the stored StackDepot ID for the given origin ID. + u32 Get(u32 id, u32 *other); + + void LockAll(); + void UnlockAll(); + + private: + struct ChainedOriginDepotDesc { + u32 here_id; + u32 prev_id; + }; + + struct ChainedOriginDepotNode { + ChainedOriginDepotNode *link; + u32 id; + u32 here_id; + u32 prev_id; + + typedef ChainedOriginDepotDesc args_type; + + bool eq(u32 hash, const args_type &args) const; + + static uptr storage_size(const args_type &args); + + static u32 hash(const args_type &args); + + static bool is_valid(const args_type &args); + + void store(const args_type &args, u32 other_hash); + + args_type load() const; + + struct Handle { + ChainedOriginDepotNode *node_; + Handle() : node_(nullptr) {} + explicit Handle(ChainedOriginDepotNode *node) : node_(node) {} + bool valid() { return node_; } + u32 id() { return node_->id; } + int here_id() { return node_->here_id; } + int prev_id() { return node_->prev_id; } + }; + + Handle get_handle(); + + typedef Handle handle_type; + }; + + StackDepotBase<ChainedOriginDepotNode, 4, 20> depot; + + ChainedOriginDepot(const ChainedOriginDepot &) = delete; + void operator=(const ChainedOriginDepot &) = delete; +}; + +} // namespace __sanitizer + +#endif // SANITIZER_CHAINED_ORIGIN_DEPOT_H diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cpp b/libsanitizer/sanitizer_common/sanitizer_common.cpp index 87efda5..33960d9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_common.cpp @@ -87,7 +87,7 @@ const char *StripModuleName(const char *module) { void ReportErrorSummary(const char *error_message, const char *alt_tool_name) { if (!common_flags()->print_summary) return; - InternalScopedString buff(kMaxSummaryLength); + InternalScopedString buff; buff.append("SUMMARY: %s: %s", alt_tool_name ? alt_tool_name : SanitizerToolName, error_message); __sanitizer_report_error_summary(buff.data()); @@ -274,6 +274,14 @@ uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len) { return name_len; } +uptr ReadBinaryDir(/*out*/ char *buf, uptr buf_len) { + ReadBinaryNameCached(buf, buf_len); + const char *exec_name_pos = StripModuleName(buf); + uptr name_len = exec_name_pos - buf; + buf[name_len] = '\0'; + return name_len; +} + #if !SANITIZER_GO void PrintCmdline() { char **argv = GetArgv(); diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index bce24d6..7b65dd7 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -44,7 +44,7 @@ const uptr kMaxPathLength = 4096; const uptr kMaxThreadStackSize = 1 << 30; // 1Gb -static const uptr kErrorMessageBufferSize = 1 << 16; +const uptr kErrorMessageBufferSize = 1 << 16; // Denotes fake PC values that come from JIT/JAVA/etc. // For such PC values __tsan_symbolize_external_ex() will be called. @@ -135,6 +135,15 @@ void UnmapFromTo(uptr from, uptr to); uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale, uptr min_shadow_base_alignment, uptr &high_mem_end); +// Let S = max(shadow_size, num_aliases * alias_size, ring_buffer_size). +// Reserves 2*S bytes of address space to the right of the returned address and +// ring_buffer_size bytes to the left. The returned address is aligned to 2*S. +// Also creates num_aliases regions of accessible memory starting at offset S +// from the returned address. Each region has size alias_size and is backed by +// the same physical memory. +uptr MapDynamicShadowAndAliases(uptr shadow_size, uptr alias_size, + uptr num_aliases, uptr ring_buffer_size); + // Reserve memory range [beg, end]. If madvise_shadow is true then apply // madvise (e.g. hugepages, core dumping) requested by options. void ReserveShadowMemoryRange(uptr beg, uptr end, const char *name, @@ -248,6 +257,7 @@ const char *StripModuleName(const char *module); // OS uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len); +uptr ReadBinaryDir(/*out*/ char *buf, uptr buf_len); uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len); const char *GetProcessName(); void UpdateProcessName(); @@ -294,8 +304,8 @@ void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, const char *mmap_type, error_t err, bool raw_report = false); -// Specific tools may override behavior of "Die" and "CheckFailed" functions -// to do tool-specific job. +// Specific tools may override behavior of "Die" function to do tool-specific +// job. typedef void (*DieCallbackType)(void); // It's possible to add several callbacks that would be run when "Die" is @@ -307,9 +317,7 @@ bool RemoveDieCallback(DieCallbackType callback); void SetUserDieCallback(DieCallbackType callback); -typedef void (*CheckFailedCallbackType)(const char *, int, const char *, - u64, u64); -void SetCheckFailedCallback(CheckFailedCallbackType callback); +void SetCheckUnwindCallback(void (*callback)()); // Callback will be called if soft_rss_limit_mb is given and the limit is // exceeded (exceeded==true) or if rss went down below the limit @@ -343,8 +351,6 @@ void ReportDeadlySignal(const SignalContext &sig, u32 tid, void SetAlternateSignalStack(); void UnsetAlternateSignalStack(); -// We don't want a summary too long. -const int kMaxSummaryLength = 1024; // Construct a one-line string: // SUMMARY: SanitizerToolName: error_message // and pass it to __sanitizer_report_error_summary. @@ -441,8 +447,14 @@ inline uptr Log2(uptr x) { // Don't use std::min, std::max or std::swap, to minimize dependency // on libstdc++. -template<class T> T Min(T a, T b) { return a < b ? a : b; } -template<class T> T Max(T a, T b) { return a > b ? a : b; } +template <class T> +constexpr T Min(T a, T b) { + return a < b ? a : b; +} +template <class T> +constexpr T Max(T a, T b) { + return a > b ? a : b; +} template<class T> void Swap(T& a, T& b) { T tmp = a; a = b; @@ -467,6 +479,7 @@ inline int ToLower(int c) { template<typename T> class InternalMmapVectorNoCtor { public: + using value_type = T; void Initialize(uptr initial_capacity) { capacity_bytes_ = 0; size_ = 0; @@ -590,21 +603,21 @@ class InternalMmapVector : public InternalMmapVectorNoCtor<T> { InternalMmapVector &operator=(InternalMmapVector &&) = delete; }; -class InternalScopedString : public InternalMmapVector<char> { +class InternalScopedString { public: - explicit InternalScopedString(uptr max_length) - : InternalMmapVector<char>(max_length), length_(0) { - (*this)[0] = '\0'; - } - uptr length() { return length_; } + InternalScopedString() : buffer_(1) { buffer_[0] = '\0'; } + + uptr length() const { return buffer_.size() - 1; } void clear() { - (*this)[0] = '\0'; - length_ = 0; + buffer_.resize(1); + buffer_[0] = '\0'; } void append(const char *format, ...); + const char *data() const { return buffer_.data(); } + char *data() { return buffer_.data(); } private: - uptr length_; + InternalMmapVector<char> buffer_; }; template <class T> @@ -651,9 +664,13 @@ void Sort(T *v, uptr size, Compare comp = {}) { // Works like std::lower_bound: finds the first element that is not less // than the val. -template <class Container, class Value, class Compare> -uptr InternalLowerBound(const Container &v, uptr first, uptr last, - const Value &val, Compare comp) { +template <class Container, + class Compare = CompareLess<typename Container::value_type>> +uptr InternalLowerBound(const Container &v, + const typename Container::value_type &val, + Compare comp = {}) { + uptr first = 0; + uptr last = v.size(); while (last > first) { uptr mid = (first + last) / 2; if (comp(v[mid], val)) @@ -677,6 +694,27 @@ enum ModuleArch { kModuleArchRISCV64 }; +// Sorts and removes duplicates from the container. +template <class Container, + class Compare = CompareLess<typename Container::value_type>> +void SortAndDedup(Container &v, Compare comp = {}) { + Sort(v.data(), v.size(), comp); + uptr size = v.size(); + if (size < 2) + return; + uptr last = 0; + for (uptr i = 1; i < size; ++i) { + if (comp(v[last], v[i])) { + ++last; + if (last != i) + v[last] = v[i]; + } else { + CHECK(!comp(v[i], v[last])); + } + } + v.resize(last + 1); +} + // Opens the file 'file_name" and reads up to 'max_len' bytes. // The resulting buffer is mmaped and stored in '*buff'. // Returns true if file was successfully opened and read. diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc index 2f2787e..7867fcc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc @@ -239,7 +239,7 @@ extern const short *_tolower_tab_; COMMON_INTERCEPT_FUNCTION(fn) #endif -#ifdef __GLIBC__ +#if SANITIZER_GLIBC // If we could not find the versioned symbol, fall back to an unversioned // lookup. This is needed to work around a GLibc bug that causes dlsym // with RTLD_NEXT to return the oldest versioned symbol. @@ -2195,6 +2195,7 @@ INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) { } return res; } +#if SANITIZER_GLIBC namespace __sanitizer { extern "C" { int real_clock_gettime(u32 clk_id, void *tp) { @@ -2204,6 +2205,7 @@ int real_clock_gettime(u32 clk_id, void *tp) { } } // extern "C" } // namespace __sanitizer +#endif INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_settime, clk_id, tp); @@ -3355,7 +3357,7 @@ INTERCEPTOR(char *, setlocale, int category, char *locale) { COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1); char *res = REAL(setlocale)(category, locale); if (res) { - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); unpoison_ctype_arrays(ctx); } return res; @@ -4030,7 +4032,7 @@ INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(sigwait)(set, sig); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(sigwait)(set, sig); if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); return res; } @@ -4047,7 +4049,7 @@ INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(sigwaitinfo)(set, info); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(sigwaitinfo)(set, info); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } @@ -4066,7 +4068,7 @@ INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. - int res = REAL(sigtimedwait)(set, info, timeout); + int res = COMMON_INTERCEPTOR_BLOCK_REAL(sigtimedwait)(set, info, timeout); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } @@ -5995,6 +5997,9 @@ void unpoison_file(__sanitizer_FILE *fp) { if (fp->_IO_read_base && fp->_IO_read_base < fp->_IO_read_end) COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_read_base, fp->_IO_read_end - fp->_IO_read_base); + if (fp->_IO_write_base && fp->_IO_write_base < fp->_IO_write_end) + COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_write_base, + fp->_IO_write_end - fp->_IO_write_base); #endif #endif // SANITIZER_HAS_STRUCT_FILE } @@ -6221,6 +6226,8 @@ INTERCEPTOR(void, _obstack_newchunk, __sanitizer_obstack *obstack, int length) { INTERCEPTOR(int, fflush, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fflush, fp); + if (fp) + unpoison_file(fp); int res = REAL(fflush)(fp); // FIXME: handle fp == NULL if (fp) { @@ -6240,6 +6247,8 @@ INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) { COMMON_INTERCEPTOR_ENTER(ctx, fclose, fp); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); const FileMetadata *m = GetInterceptorMetadata(fp); + if (fp) + unpoison_file(fp); int res = REAL(fclose)(fp); if (m) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc index 490a04b..7f18125 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc @@ -330,13 +330,17 @@ static void ioctl_table_fill() { _(SOUND_PCM_WRITE_CHANNELS, WRITE, sizeof(int)); _(SOUND_PCM_WRITE_FILTER, WRITE, sizeof(int)); _(TCFLSH, NONE, 0); +#if SANITIZER_GLIBC _(TCGETA, WRITE, struct_termio_sz); +#endif _(TCGETS, WRITE, struct_termios_sz); _(TCSBRK, NONE, 0); _(TCSBRKP, NONE, 0); +#if SANITIZER_GLIBC _(TCSETA, READ, struct_termio_sz); _(TCSETAF, READ, struct_termio_sz); _(TCSETAW, READ, struct_termio_sz); +#endif _(TCSETS, READ, struct_termios_sz); _(TCSETSF, READ, struct_termios_sz); _(TCSETSW, READ, struct_termios_sz); @@ -364,7 +368,7 @@ static void ioctl_table_fill() { _(VT_WAITACTIVE, NONE, 0); #endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if SANITIZER_GLIBC // _(SIOCDEVPLIP, WRITE, struct_ifreq_sz); // the same as EQL_ENSLAVE _(CYGETDEFTHRESH, WRITE, sizeof(int)); _(CYGETDEFTIMEOUT, WRITE, sizeof(int)); diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S index 20f42f1..72e4827 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_vfork_aarch64.inc.S @@ -1,6 +1,7 @@ #if defined(__aarch64__) && defined(__linux__) #include "sanitizer_common/sanitizer_asm.h" +#include "builtins/assembly.h" ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA) @@ -9,6 +10,7 @@ ASM_HIDDEN(COMMON_INTERCEPTOR_SPILL_AREA) ASM_TYPE_FUNCTION(ASM_WRAPPER_NAME(vfork)) ASM_WRAPPER_NAME(vfork): // Save x30 in the off-stack spill area. + hint #25 // paciasp stp xzr, x30, [sp, #-16]! bl COMMON_INTERCEPTOR_SPILL_AREA ldp xzr, x30, [sp], 16 @@ -33,6 +35,7 @@ ASM_WRAPPER_NAME(vfork): bl COMMON_INTERCEPTOR_SPILL_AREA ldr x30, [x0] ldp x0, xzr, [sp], 16 + hint #29 // autiasp ret ASM_SIZE(vfork) @@ -40,4 +43,6 @@ ASM_SIZE(vfork) .weak vfork .set vfork, ASM_WRAPPER_NAME(vfork) +GNU_PROPERTY_BTI_PAC + #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interface.inc b/libsanitizer/sanitizer_common/sanitizer_common_interface.inc index c78b6e1..932e547 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interface.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interface.inc @@ -13,6 +13,7 @@ INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address) INTERFACE_FUNCTION(__sanitizer_set_death_callback) INTERFACE_FUNCTION(__sanitizer_set_report_path) INTERFACE_FUNCTION(__sanitizer_set_report_fd) +INTERFACE_FUNCTION(__sanitizer_get_report_path) INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container) INTERFACE_WEAK_FUNCTION(__sanitizer_on_print) INTERFACE_WEAK_FUNCTION(__sanitizer_report_error_summary) diff --git a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cpp index 047c5a1..1037938 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cpp @@ -92,14 +92,13 @@ void *BackgroundThread(void *arg) { #endif void WriteToSyslog(const char *msg) { - InternalScopedString msg_copy(kErrorMessageBufferSize); + InternalScopedString msg_copy; msg_copy.append("%s", msg); - char *p = msg_copy.data(); - char *q; + const char *p = msg_copy.data(); // Print one line at a time. // syslog, at least on Android, has an implicit message length limit. - while ((q = internal_strchr(p, '\n'))) { + while (char* q = internal_strchr(p, '\n')) { *q = '\0'; WriteOneLineToSyslog(p); p = q + 1; diff --git a/libsanitizer/sanitizer_common/sanitizer_file.cpp b/libsanitizer/sanitizer_common/sanitizer_file.cpp index 7cce609..0b92dcc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_file.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_file.cpp @@ -58,6 +58,9 @@ void ReportFile::ReopenIfNecessary() { } else { internal_snprintf(full_path, kMaxPathLength, "%s.%zu", path_prefix, pid); } + if (common_flags()->log_suffix) { + internal_strlcat(full_path, common_flags()->log_suffix, kMaxPathLength); + } error_t err; fd = OpenFile(full_path, WrOnly, &err); if (fd == kInvalidFd) { @@ -95,6 +98,12 @@ void ReportFile::SetReportPath(const char *path) { } } +const char *ReportFile::GetReportPath() { + SpinMutexLock l(mu); + ReopenIfNecessary(); + return full_path; +} + bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, uptr *read_len, uptr max_len, error_t *errno_p) { *buff = nullptr; @@ -213,6 +222,10 @@ void __sanitizer_set_report_fd(void *fd) { report_file.fd = (fd_t)reinterpret_cast<uptr>(fd); report_file.fd_pid = internal_getpid(); } + +const char *__sanitizer_get_report_path() { + return report_file.GetReportPath(); +} } // extern "C" #endif // !SANITIZER_FUCHSIA diff --git a/libsanitizer/sanitizer_common/sanitizer_file.h b/libsanitizer/sanitizer_common/sanitizer_file.h index 26681f0..08671ab 100644 --- a/libsanitizer/sanitizer_common/sanitizer_file.h +++ b/libsanitizer/sanitizer_common/sanitizer_file.h @@ -26,6 +26,7 @@ struct ReportFile { void Write(const char *buffer, uptr length); bool SupportsColors(); void SetReportPath(const char *path); + const char *GetReportPath(); // Don't use fields directly. They are only declared public to allow // aggregate initialization. diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.cpp b/libsanitizer/sanitizer_common/sanitizer_flags.cpp index 21048be..d52e96a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_flags.cpp @@ -35,6 +35,7 @@ void CommonFlags::CopyFrom(const CommonFlags &other) { // Copy the string from "s" to "out", making the following substitutions: // %b = binary basename // %p = pid +// %d = binary directory void SubstituteForFlagValue(const char *s, char *out, uptr out_size) { char *out_end = out + out_size; while (*s && out < out_end - 1) { @@ -64,6 +65,12 @@ void SubstituteForFlagValue(const char *s, char *out, uptr out_size) { s += 2; // skip "%p" break; } + case 'd': { + uptr len = ReadBinaryDir(out, out_end - out); + out += len; + s += 2; // skip "%d" + break; + } default: *out++ = *s++; break; diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.inc b/libsanitizer/sanitizer_common/sanitizer_flags.inc index cfb5822..3bc44c6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.inc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.inc @@ -59,6 +59,8 @@ COMMON_FLAG( bool, log_exe_name, false, "Mention name of executable when reporting error and " "append executable name to logs (as in \"log_path.exe_name.pid\").") +COMMON_FLAG(const char *, log_suffix, nullptr, + "String to append to log file name, e.g. \".txt\".") COMMON_FLAG( bool, log_to_syslog, (bool)SANITIZER_ANDROID || (bool)SANITIZER_MAC, "Write all sanitizer output to syslog in addition to other means of " diff --git a/libsanitizer/sanitizer_common/sanitizer_fuchsia.cpp b/libsanitizer/sanitizer_common/sanitizer_fuchsia.cpp index 7200ffd..4f692f9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_fuchsia.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_fuchsia.cpp @@ -14,7 +14,6 @@ #include "sanitizer_fuchsia.h" #if SANITIZER_FUCHSIA -#include <limits.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> @@ -69,9 +68,7 @@ uptr internal_getpid() { return pid; } -int internal_dlinfo(void *handle, int request, void *p) { - UNIMPLEMENTED(); -} +int internal_dlinfo(void *handle, int request, void *p) { UNIMPLEMENTED(); } uptr GetThreadSelf() { return reinterpret_cast<uptr>(thrd_current()); } @@ -153,9 +150,9 @@ void BlockingMutex::CheckLocked() { CHECK_NE(MtxUnlocked, atomic_load(m, memory_order_relaxed)); } -uptr GetPageSize() { return PAGE_SIZE; } +uptr GetPageSize() { return _zx_system_get_page_size(); } -uptr GetMmapGranularity() { return PAGE_SIZE; } +uptr GetMmapGranularity() { return _zx_system_get_page_size(); } sanitizer_shadow_bounds_t ShadowBounds; @@ -168,7 +165,7 @@ uptr GetMaxVirtualAddress() { return GetMaxUserVirtualAddress(); } static void *DoAnonymousMmapOrDie(uptr size, const char *mem_type, bool raw_report, bool die_for_nomem) { - size = RoundUpTo(size, PAGE_SIZE); + size = RoundUpTo(size, GetPageSize()); zx_handle_t vmo; zx_status_t status = _zx_vmo_create(size, 0, &vmo); @@ -214,15 +211,14 @@ void *MmapOrDieOnFatalError(uptr size, const char *mem_type) { uptr ReservedAddressRange::Init(uptr init_size, const char *name, uptr fixed_addr) { - init_size = RoundUpTo(init_size, PAGE_SIZE); + init_size = RoundUpTo(init_size, GetPageSize()); DCHECK_EQ(os_handle_, ZX_HANDLE_INVALID); uintptr_t base; zx_handle_t vmar; - zx_status_t status = - _zx_vmar_allocate( - _zx_vmar_root_self(), - ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, - 0, init_size, &vmar, &base); + zx_status_t status = _zx_vmar_allocate( + _zx_vmar_root_self(), + ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0, + init_size, &vmar, &base); if (status != ZX_OK) ReportMmapFailureAndDie(init_size, name, "zx_vmar_allocate", status); base_ = reinterpret_cast<void *>(base); @@ -236,7 +232,7 @@ uptr ReservedAddressRange::Init(uptr init_size, const char *name, static uptr DoMmapFixedOrDie(zx_handle_t vmar, uptr fixed_addr, uptr map_size, void *base, const char *name, bool die_for_nomem) { uptr offset = fixed_addr - reinterpret_cast<uptr>(base); - map_size = RoundUpTo(map_size, PAGE_SIZE); + map_size = RoundUpTo(map_size, GetPageSize()); zx_handle_t vmo; zx_status_t status = _zx_vmo_create(map_size, 0, &vmo); if (status != ZX_OK) { @@ -264,19 +260,19 @@ static uptr DoMmapFixedOrDie(zx_handle_t vmar, uptr fixed_addr, uptr map_size, uptr ReservedAddressRange::Map(uptr fixed_addr, uptr map_size, const char *name) { - return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_, - name_, false); + return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_, name_, + false); } uptr ReservedAddressRange::MapOrDie(uptr fixed_addr, uptr map_size, const char *name) { - return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_, - name_, true); + return DoMmapFixedOrDie(os_handle_, fixed_addr, map_size, base_, name_, true); } void UnmapOrDieVmar(void *addr, uptr size, zx_handle_t target_vmar) { - if (!addr || !size) return; - size = RoundUpTo(size, PAGE_SIZE); + if (!addr || !size) + return; + size = RoundUpTo(size, GetPageSize()); zx_status_t status = _zx_vmar_unmap(target_vmar, reinterpret_cast<uintptr_t>(addr), size); @@ -316,7 +312,7 @@ void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) { void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, const char *mem_type) { - CHECK_GE(size, PAGE_SIZE); + CHECK_GE(size, GetPageSize()); CHECK(IsPowerOfTwo(size)); CHECK(IsPowerOfTwo(alignment)); @@ -356,7 +352,8 @@ void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, _zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_SPECIFIC_OVERWRITE, addr - info.base, vmo, 0, size, &new_addr); - if (status == ZX_OK) CHECK_EQ(new_addr, addr); + if (status == ZX_OK) + CHECK_EQ(new_addr, addr); } } if (status == ZX_OK && addr != map_addr) @@ -381,9 +378,18 @@ void UnmapOrDie(void *addr, uptr size) { UnmapOrDieVmar(addr, size, _zx_vmar_root_self()); } -// This is used on the shadow mapping, which cannot be changed. -// Zircon doesn't have anything like MADV_DONTNEED. -void ReleaseMemoryPagesToOS(uptr beg, uptr end) {} +void ReleaseMemoryPagesToOS(uptr beg, uptr end) { + uptr beg_aligned = RoundUpTo(beg, GetPageSize()); + uptr end_aligned = RoundDownTo(end, GetPageSize()); + if (beg_aligned < end_aligned) { + zx_handle_t root_vmar = _zx_vmar_root_self(); + CHECK_NE(root_vmar, ZX_HANDLE_INVALID); + zx_status_t status = + _zx_vmar_op_range(root_vmar, ZX_VMAR_OP_DECOMMIT, beg_aligned, + end_aligned - beg_aligned, nullptr, 0); + CHECK_EQ(status, ZX_OK); + } +} void DumpProcessMap() { // TODO(mcgrathr): write it @@ -412,8 +418,9 @@ bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, uint64_t vmo_size; status = _zx_vmo_get_size(vmo, &vmo_size); if (status == ZX_OK) { - if (vmo_size < max_len) max_len = vmo_size; - size_t map_size = RoundUpTo(max_len, PAGE_SIZE); + if (vmo_size < max_len) + max_len = vmo_size; + size_t map_size = RoundUpTo(max_len, GetPageSize()); uintptr_t addr; status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo, 0, map_size, &addr); @@ -425,7 +432,8 @@ bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, } _zx_handle_close(vmo); } - if (status != ZX_OK && errno_p) *errno_p = status; + if (status != ZX_OK && errno_p) + *errno_p = status; return status == ZX_OK; } @@ -499,9 +507,7 @@ bool GetRandom(void *buffer, uptr length, bool blocking) { return true; } -u32 GetNumberOfCPUs() { - return zx_system_get_num_cpus(); -} +u32 GetNumberOfCPUs() { return zx_system_get_num_cpus(); } uptr GetRSS() { UNIMPLEMENTED(); } @@ -529,6 +535,10 @@ void __sanitizer_set_report_path(const char *path) { void __sanitizer_set_report_fd(void *fd) { UNREACHABLE("not available on Fuchsia"); } + +const char *__sanitizer_get_report_path() { + UNREACHABLE("not available on Fuchsia"); +} } // extern "C" #endif // SANITIZER_FUCHSIA diff --git a/libsanitizer/sanitizer_common/sanitizer_interface_internal.h b/libsanitizer/sanitizer_common/sanitizer_interface_internal.h index be8023e..0b001c1 100644 --- a/libsanitizer/sanitizer_common/sanitizer_interface_internal.h +++ b/libsanitizer/sanitizer_common/sanitizer_interface_internal.h @@ -28,6 +28,10 @@ extern "C" { // (casted to void *). SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_set_report_fd(void *fd); + // Get the current full report file path, if a path was specified by + // an earlier call to __sanitizer_set_report_path. Returns null otherwise. + SANITIZER_INTERFACE_ATTRIBUTE + const char *__sanitizer_get_report_path(); typedef struct { int coverage_sandboxed; diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h index d8f0540..84053fe 100644 --- a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h +++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h @@ -409,6 +409,9 @@ inline void Trap() { (void)enable_fp; \ } while (0) +constexpr u32 kInvalidTid = -1; +constexpr u32 kMainTid = 0; + } // namespace __sanitizer namespace __asan { diff --git a/libsanitizer/sanitizer_common/sanitizer_libignore.cpp b/libsanitizer/sanitizer_common/sanitizer_libignore.cpp index 9ea19bc..a65d3d8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libignore.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_libignore.cpp @@ -38,7 +38,7 @@ void LibIgnore::AddIgnoredLibrary(const char *name_templ) { void LibIgnore::OnLibraryLoaded(const char *name) { BlockingMutexLock lock(&mutex_); // Try to match suppressions with symlink target. - InternalScopedString buf(kMaxPathLength); + InternalMmapVector<char> buf(kMaxPathLength); if (name && internal_readlink(name, buf.data(), buf.size() - 1) > 0 && buf[0]) { for (uptr i = 0; i < count_; i++) { diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cpp b/libsanitizer/sanitizer_common/sanitizer_linux.cpp index 379f6d9..b371477 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_linux.cpp @@ -183,6 +183,14 @@ uptr internal_munmap(void *addr, uptr length) { return internal_syscall(SYSCALL(munmap), (uptr)addr, length); } +#if SANITIZER_LINUX +uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags, + void *new_address) { + return internal_syscall(SYSCALL(mremap), (uptr)old_address, old_size, + new_size, flags, (uptr)new_address); +} +#endif + int internal_mprotect(void *addr, uptr length, int prot) { return internal_syscall(SYSCALL(mprotect), (uptr)addr, length, prot); } @@ -489,22 +497,24 @@ int TgKill(pid_t pid, tid_t tid, int sig) { } #endif -#if !SANITIZER_SOLARIS && !SANITIZER_NETBSD +#if SANITIZER_GLIBC u64 NanoTime() { -#if SANITIZER_FREEBSD - timeval tv; -#else kernel_timeval tv; -#endif internal_memset(&tv, 0, sizeof(tv)); internal_syscall(SYSCALL(gettimeofday), &tv, 0); - return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; + return (u64)tv.tv_sec * 1000 * 1000 * 1000 + tv.tv_usec * 1000; } - +// Used by real_clock_gettime. uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp) { return internal_syscall(SYSCALL(clock_gettime), clk_id, tp); } -#endif // !SANITIZER_SOLARIS && !SANITIZER_NETBSD +#elif !SANITIZER_SOLARIS && !SANITIZER_NETBSD +u64 NanoTime() { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return (u64)ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; +} +#endif // Like getenv, but reads env directly from /proc (on Linux) or parses the // 'environ' array (on some others) and does not use libc. This function @@ -1334,50 +1344,42 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, #elif SANITIZER_RISCV64 uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr) { - long long res; if (!fn || !child_stack) return -EINVAL; + CHECK_EQ(0, (uptr)child_stack % 16); - child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); - ((unsigned long long *)child_stack)[0] = (uptr)fn; - ((unsigned long long *)child_stack)[1] = (uptr)arg; - register int (*__fn)(void *) __asm__("a0") = fn; + register int res __asm__("a0"); + register int __flags __asm__("a0") = flags; register void *__stack __asm__("a1") = child_stack; - register int __flags __asm__("a2") = flags; - register void *__arg __asm__("a3") = arg; - register int *__ptid __asm__("a4") = parent_tidptr; - register void *__tls __asm__("a5") = newtls; - register int *__ctid __asm__("a6") = child_tidptr; + register int *__ptid __asm__("a2") = parent_tidptr; + register void *__tls __asm__("a3") = newtls; + register int *__ctid __asm__("a4") = child_tidptr; + register int (*__fn)(void *) __asm__("a5") = fn; + register void *__arg __asm__("a6") = arg; + register int nr_clone __asm__("a7") = __NR_clone; __asm__ __volatile__( - "mv a0,a2\n" /* flags */ - "mv a2,a4\n" /* ptid */ - "mv a3,a5\n" /* tls */ - "mv a4,a6\n" /* ctid */ - "addi a7, zero, %9\n" /* clone */ - "ecall\n" - /* if (%r0 != 0) - * return %r0; + /* if (a0 != 0) + * return a0; */ "bnez a0, 1f\n" - /* In the child, now. Call "fn(arg)". */ - "ld a0, 8(sp)\n" - "ld a1, 16(sp)\n" - "jalr a1\n" + // In the child, now. Call "fn(arg)". + "mv a0, a6\n" + "jalr a5\n" - /* Call _exit(%r0). */ - "addi a7, zero, %10\n" + // Call _exit(a0). + "addi a7, zero, %9\n" "ecall\n" "1:\n" : "=r"(res) - : "i"(-EINVAL), "r"(__fn), "r"(__stack), "r"(__flags), "r"(__arg), - "r"(__ptid), "r"(__tls), "r"(__ctid), "i"(__NR_clone), "i"(__NR_exit) - : "ra", "memory"); + : "0"(__flags), "r"(__stack), "r"(__ptid), "r"(__tls), "r"(__ctid), + "r"(__fn), "r"(__arg), "r"(nr_clone), "i"(__NR_exit) + : "memory"); return res; } #elif defined(__aarch64__) diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.h b/libsanitizer/sanitizer_common/sanitizer_linux.h index 24902d1..9a23fcf 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.h +++ b/libsanitizer/sanitizer_common/sanitizer_linux.h @@ -49,7 +49,9 @@ uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); uptr internal_sigaltstack(const void* ss, void* oss); uptr internal_sigprocmask(int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset); +#if SANITIZER_GLIBC uptr internal_clock_gettime(__sanitizer_clockid_t clk_id, void *tp); +#endif // Linux-only syscalls. #if SANITIZER_LINUX @@ -96,7 +98,6 @@ class ThreadLister { // Exposed for testing. uptr ThreadDescriptorSize(); uptr ThreadSelf(); -uptr ThreadSelfOffset(); // Matches a library's file name against a base name (stripping path and version // information). diff --git a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp index bc10e89..4f9577a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cpp @@ -36,6 +36,7 @@ #include <link.h> #include <pthread.h> #include <signal.h> +#include <sys/mman.h> #include <sys/resource.h> #include <syslog.h> @@ -48,6 +49,10 @@ #include <osreldate.h> #include <sys/sysctl.h> #define pthread_getattr_np pthread_attr_get_np +// The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before +// that, it was never implemented. So just define it to zero. +#undef MAP_NORESERVE +#define MAP_NORESERVE 0 #endif #if SANITIZER_NETBSD @@ -183,85 +188,35 @@ __attribute__((unused)) static bool GetLibcVersion(int *major, int *minor, #endif } -#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_GO && \ - !SANITIZER_NETBSD && !SANITIZER_SOLARIS -static uptr g_tls_size; +// True if we can use dlpi_tls_data. glibc before 2.25 may leave NULL (BZ +// #19826) so dlpi_tls_data cannot be used. +// +// musl before 1.2.3 and FreeBSD as of 12.2 incorrectly set dlpi_tls_data to +// the TLS initialization image +// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=254774 +__attribute__((unused)) static int g_use_dlpi_tls_data; -#ifdef __i386__ -#define CHECK_GET_TLS_STATIC_INFO_VERSION (!__GLIBC_PREREQ(2, 27)) -#else -#define CHECK_GET_TLS_STATIC_INFO_VERSION 0 -#endif +#if SANITIZER_GLIBC && !SANITIZER_GO +__attribute__((unused)) static size_t g_tls_size; +void InitTlsSize() { + int major, minor, patch; + g_use_dlpi_tls_data = + GetLibcVersion(&major, &minor, &patch) && major == 2 && minor >= 25; -#if CHECK_GET_TLS_STATIC_INFO_VERSION -#define DL_INTERNAL_FUNCTION __attribute__((regparm(3), stdcall)) -#else -#define DL_INTERNAL_FUNCTION +#if defined(__x86_64__) || defined(__powerpc64__) + void *get_tls_static_info = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); + size_t tls_align; + ((void (*)(size_t *, size_t *))get_tls_static_info)(&g_tls_size, &tls_align); #endif - -namespace { -struct GetTlsStaticInfoCall { - typedef void (*get_tls_func)(size_t*, size_t*); -}; -struct GetTlsStaticInfoRegparmCall { - typedef void (*get_tls_func)(size_t*, size_t*) DL_INTERNAL_FUNCTION; -}; - -template <typename T> -void CallGetTls(void* ptr, size_t* size, size_t* align) { - typename T::get_tls_func get_tls; - CHECK_EQ(sizeof(get_tls), sizeof(ptr)); - internal_memcpy(&get_tls, &ptr, sizeof(ptr)); - CHECK_NE(get_tls, 0); - get_tls(size, align); -} - -bool CmpLibcVersion(int major, int minor, int patch) { - int ma; - int mi; - int pa; - if (!GetLibcVersion(&ma, &mi, &pa)) - return false; - if (ma > major) - return true; - if (ma < major) - return false; - if (mi > minor) - return true; - if (mi < minor) - return false; - return pa >= patch; -} - -} // namespace - -void InitTlsSize() { - // all current supported platforms have 16 bytes stack alignment - const size_t kStackAlign = 16; - void *get_tls_static_info_ptr = dlsym(RTLD_NEXT, "_dl_get_tls_static_info"); - size_t tls_size = 0; - size_t tls_align = 0; - // On i?86, _dl_get_tls_static_info used to be internal_function, i.e. - // __attribute__((regparm(3), stdcall)) before glibc 2.27 and is normal - // function in 2.27 and later. - if (CHECK_GET_TLS_STATIC_INFO_VERSION && !CmpLibcVersion(2, 27, 0)) - CallGetTls<GetTlsStaticInfoRegparmCall>(get_tls_static_info_ptr, - &tls_size, &tls_align); - else - CallGetTls<GetTlsStaticInfoCall>(get_tls_static_info_ptr, - &tls_size, &tls_align); - if (tls_align < kStackAlign) - tls_align = kStackAlign; - g_tls_size = RoundUpTo(tls_size, tls_align); } #else void InitTlsSize() { } -#endif +#endif // SANITIZER_GLIBC && !SANITIZER_GO -#if (defined(__x86_64__) || defined(__i386__) || defined(__mips__) || \ - defined(__aarch64__) || defined(__powerpc64__) || defined(__s390__) || \ - defined(__arm__) || SANITIZER_RISCV64) && \ - SANITIZER_LINUX && !SANITIZER_ANDROID +// On glibc x86_64, ThreadDescriptorSize() needs to be precise due to the usage +// of g_tls_size. On other targets, ThreadDescriptorSize() is only used by lsan +// to get the pointer to thread-specific data keys in the thread control block. +#if (SANITIZER_FREEBSD || SANITIZER_LINUX) && !SANITIZER_ANDROID // sizeof(struct pthread) from glibc. static atomic_uintptr_t thread_descriptor_size; @@ -294,9 +249,18 @@ uptr ThreadDescriptorSize() { val = FIRST_32_SECOND_64(1168, 2288); else if (minor <= 14) val = FIRST_32_SECOND_64(1168, 2304); - else + else if (minor < 32) // Unknown version val = FIRST_32_SECOND_64(1216, 2304); + else // minor == 32 + val = FIRST_32_SECOND_64(1344, 2496); } +#elif defined(__s390__) || defined(__sparc__) + // The size of a prefix of TCB including pthread::{specific_1stblock,specific} + // suffices. Just return offsetof(struct pthread, specific_used), which hasn't + // changed since 2007-05. Technically this applies to i386/x86_64 as well but + // we call _dl_get_tls_static_info and need the precise size of struct + // pthread. + return FIRST_32_SECOND_64(524, 1552); #elif defined(__mips__) // TODO(sagarthakur): add more values as per different glibc versions. val = FIRST_32_SECOND_64(1152, 1776); @@ -320,21 +284,12 @@ uptr ThreadDescriptorSize() { val = 1776; #elif defined(__powerpc64__) val = 1776; // from glibc.ppc64le 2.20-8.fc21 -#elif defined(__s390__) - val = FIRST_32_SECOND_64(1152, 1776); // valid for glibc 2.22 #endif if (val) atomic_store_relaxed(&thread_descriptor_size, val); return val; } -// The offset at which pointer to self is located in the thread descriptor. -const uptr kThreadSelfOffset = FIRST_32_SECOND_64(8, 16); - -uptr ThreadSelfOffset() { - return kThreadSelfOffset; -} - #if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 // TlsPreTcbSize includes size of struct pthread_descr and size of tcb // head structure. It lies before the static tls blocks. @@ -353,68 +308,74 @@ static uptr TlsPreTcbSize() { } #endif -uptr ThreadSelf() { - uptr descr_addr; -#if defined(__i386__) - asm("mov %%gs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); -#elif defined(__x86_64__) - asm("mov %%fs:%c1,%0" : "=r"(descr_addr) : "i"(kThreadSelfOffset)); -#elif defined(__mips__) - // MIPS uses TLS variant I. The thread pointer (in hardware register $29) - // points to the end of the TCB + 0x7000. The pthread_descr structure is - // immediately in front of the TCB. TlsPreTcbSize() includes the size of the - // TCB and the size of pthread_descr. - const uptr kTlsTcbOffset = 0x7000; - uptr thread_pointer; - asm volatile(".set push;\ - .set mips64r2;\ - rdhwr %0,$29;\ - .set pop" : "=r" (thread_pointer)); - descr_addr = thread_pointer - kTlsTcbOffset - TlsPreTcbSize(); -#elif defined(__aarch64__) || defined(__arm__) - descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer()) - - ThreadDescriptorSize(); -#elif SANITIZER_RISCV64 - // https://github.com/riscv/riscv-elf-psabi-doc/issues/53 - uptr thread_pointer = reinterpret_cast<uptr>(__builtin_thread_pointer()); - descr_addr = thread_pointer - TlsPreTcbSize(); -#elif defined(__s390__) - descr_addr = reinterpret_cast<uptr>(__builtin_thread_pointer()); -#elif defined(__powerpc64__) - // PPC64LE uses TLS variant I. The thread pointer (in GPR 13) - // points to the end of the TCB + 0x7000. The pthread_descr structure is - // immediately in front of the TCB. TlsPreTcbSize() includes the size of the - // TCB and the size of pthread_descr. - const uptr kTlsTcbOffset = 0x7000; - uptr thread_pointer; - asm("addi %0,13,%1" : "=r"(thread_pointer) : "I"(-kTlsTcbOffset)); - descr_addr = thread_pointer - TlsPreTcbSize(); -#else -#error "unsupported CPU arch" -#endif - return descr_addr; -} -#endif // (x86_64 || i386 || MIPS) && SANITIZER_LINUX +#if !SANITIZER_GO +namespace { +struct TlsBlock { + uptr begin, end, align; + size_t tls_modid; + bool operator<(const TlsBlock &rhs) const { return begin < rhs.begin; } +}; +} // namespace -#if SANITIZER_FREEBSD -static void **ThreadSelfSegbase() { - void **segbase = 0; -#if defined(__i386__) - // sysarch(I386_GET_GSBASE, segbase); - __asm __volatile("mov %%gs:0, %0" : "=r" (segbase)); -#elif defined(__x86_64__) - // sysarch(AMD64_GET_FSBASE, segbase); - __asm __volatile("movq %%fs:0, %0" : "=r" (segbase)); -#else -#error "unsupported CPU arch" +extern "C" void *__tls_get_addr(size_t *); + +static int CollectStaticTlsBlocks(struct dl_phdr_info *info, size_t size, + void *data) { + if (!info->dlpi_tls_modid) + return 0; + uptr begin = (uptr)info->dlpi_tls_data; +#ifndef __s390__ + if (!g_use_dlpi_tls_data) { + // Call __tls_get_addr as a fallback. This forces TLS allocation on glibc + // and FreeBSD. + size_t mod_and_off[2] = {info->dlpi_tls_modid, 0}; + begin = (uptr)__tls_get_addr(mod_and_off); + } #endif - return segbase; + for (unsigned i = 0; i != info->dlpi_phnum; ++i) + if (info->dlpi_phdr[i].p_type == PT_TLS) { + static_cast<InternalMmapVector<TlsBlock> *>(data)->push_back( + TlsBlock{begin, begin + info->dlpi_phdr[i].p_memsz, + info->dlpi_phdr[i].p_align, info->dlpi_tls_modid}); + break; + } + return 0; } -uptr ThreadSelf() { - return (uptr)ThreadSelfSegbase()[2]; -} -#endif // SANITIZER_FREEBSD +__attribute__((unused)) static void GetStaticTlsBoundary(uptr *addr, uptr *size, + uptr *align) { + InternalMmapVector<TlsBlock> ranges; + dl_iterate_phdr(CollectStaticTlsBlocks, &ranges); + uptr len = ranges.size(); + Sort(ranges.begin(), len); + // Find the range with tls_modid=1. For glibc, because libc.so uses PT_TLS, + // this module is guaranteed to exist and is one of the initially loaded + // modules. + uptr one = 0; + while (one != len && ranges[one].tls_modid != 1) ++one; + if (one == len) { + // This may happen with musl if no module uses PT_TLS. + *addr = 0; + *size = 0; + *align = 1; + return; + } + // Find the maximum consecutive ranges. We consider two modules consecutive if + // the gap is smaller than the alignment. The dynamic loader places static TLS + // blocks this way not to waste space. + uptr l = one; + *align = ranges[l].align; + while (l != 0 && ranges[l].begin < ranges[l - 1].end + ranges[l - 1].align) + *align = Max(*align, ranges[--l].align); + uptr r = one + 1; + while (r != len && ranges[r].begin < ranges[r - 1].end + ranges[r - 1].align) + *align = Max(*align, ranges[r++].align); + *addr = ranges[l].begin; + *size = ranges[r - 1].end - ranges[l].begin; +} +#endif // !SANITIZER_GO +#endif // (x86_64 || i386 || mips || ...) && (SANITIZER_FREEBSD || + // SANITIZER_LINUX) && !SANITIZER_ANDROID #if SANITIZER_NETBSD static struct tls_tcb * ThreadSelfTlsTcb() { @@ -465,33 +426,67 @@ static void GetTls(uptr *addr, uptr *size) { *addr = 0; *size = 0; } -#elif SANITIZER_LINUX -#if defined(__x86_64__) || defined(__i386__) || defined(__s390__) - *addr = ThreadSelf(); - *size = GetTlsSize(); +#elif SANITIZER_GLIBC && defined(__x86_64__) + // For x86-64, use an O(1) approach which requires precise + // ThreadDescriptorSize. g_tls_size was initialized in InitTlsSize. + asm("mov %%fs:16,%0" : "=r"(*addr)); + *size = g_tls_size; *addr -= *size; *addr += ThreadDescriptorSize(); -#elif defined(__mips__) || defined(__aarch64__) || defined(__powerpc64__) || \ - defined(__arm__) || SANITIZER_RISCV64 - *addr = ThreadSelf(); - *size = GetTlsSize(); +#elif SANITIZER_GLIBC && defined(__powerpc64__) + // Workaround for glibc<2.25(?). 2.27 is known to not need this. + uptr tp; + asm("addi %0,13,-0x7000" : "=r"(tp)); + const uptr pre_tcb_size = TlsPreTcbSize(); + *addr = tp - pre_tcb_size; + *size = g_tls_size + pre_tcb_size; +#elif SANITIZER_FREEBSD || SANITIZER_LINUX + uptr align; + GetStaticTlsBoundary(addr, size, &align); +#if defined(__x86_64__) || defined(__i386__) || defined(__s390__) || \ + defined(__sparc__) + if (SANITIZER_GLIBC) { +#if defined(__x86_64__) || defined(__i386__) + align = Max<uptr>(align, 64); #else - *addr = 0; - *size = 0; + align = Max<uptr>(align, 16); #endif -#elif SANITIZER_FREEBSD - void** segbase = ThreadSelfSegbase(); - *addr = 0; - *size = 0; - if (segbase != 0) { - // tcbalign = 16 - // tls_size = round(tls_static_space, tcbalign); - // dtv = segbase[1]; - // dtv[2] = segbase - tls_static_space; - void **dtv = (void**) segbase[1]; - *addr = (uptr) dtv[2]; - *size = (*addr == 0) ? 0 : ((uptr) segbase[0] - (uptr) dtv[2]); } + const uptr tp = RoundUpTo(*addr + *size, align); + + // lsan requires the range to additionally cover the static TLS surplus + // (elf/dl-tls.c defines 1664). Otherwise there may be false positives for + // allocations only referenced by tls in dynamically loaded modules. + if (SANITIZER_GLIBC) + *size += 1644; + else if (SANITIZER_FREEBSD) + *size += 128; // RTLD_STATIC_TLS_EXTRA + + // Extend the range to include the thread control block. On glibc, lsan needs + // the range to include pthread::{specific_1stblock,specific} so that + // allocations only referenced by pthread_setspecific can be scanned. This may + // underestimate by at most TLS_TCB_ALIGN-1 bytes but it should be fine + // because the number of bytes after pthread::specific is larger. + *addr = tp - RoundUpTo(*size, align); + *size = tp - *addr + ThreadDescriptorSize(); +#else + if (SANITIZER_GLIBC) + *size += 1664; + else if (SANITIZER_FREEBSD) + *size += 128; // RTLD_STATIC_TLS_EXTRA +#if defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 + const uptr pre_tcb_size = TlsPreTcbSize(); + *addr -= pre_tcb_size; + *size += pre_tcb_size; +#else + // arm and aarch64 reserve two words at TP, so this underestimates the range. + // However, this is sufficient for the purpose of finding the pointers to + // thread-specific data keys. + const uptr tcb_size = ThreadDescriptorSize(); + *addr -= tcb_size; + *size += tcb_size; +#endif +#endif #elif SANITIZER_NETBSD struct tls_tcb * const tcb = ThreadSelfTlsTcb(); *addr = 0; @@ -518,15 +513,13 @@ static void GetTls(uptr *addr, uptr *size) { #if !SANITIZER_GO uptr GetTlsSize() { -#if SANITIZER_FREEBSD || SANITIZER_ANDROID || SANITIZER_NETBSD || \ +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ SANITIZER_SOLARIS uptr addr, size; GetTls(&addr, &size); return size; -#elif defined(__mips__) || defined(__powerpc64__) || SANITIZER_RISCV64 - return RoundUpTo(g_tls_size + TlsPreTcbSize(), 16); #else - return g_tls_size; + return 0; #endif } #endif @@ -547,10 +540,9 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, if (!main) { // If stack and tls intersect, make them non-intersecting. if (*tls_addr > *stk_addr && *tls_addr < *stk_addr + *stk_size) { - CHECK_GT(*tls_addr + *tls_size, *stk_addr); - CHECK_LE(*tls_addr + *tls_size, *stk_addr + *stk_size); - *stk_size -= *tls_size; - *tls_addr = *stk_addr + *stk_size; + if (*stk_addr + *stk_size < *tls_addr + *tls_size) + *tls_size = *stk_addr + *stk_size - *tls_addr; + *stk_size = *tls_addr - *stk_addr; } } #endif @@ -569,20 +561,12 @@ struct DlIteratePhdrData { bool first; }; -static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { - DlIteratePhdrData *data = (DlIteratePhdrData*)arg; - InternalScopedString module_name(kMaxPathLength); - if (data->first) { - data->first = false; - // First module is the binary itself. - ReadBinaryNameCached(module_name.data(), module_name.size()); - } else if (info->dlpi_name) { - module_name.append("%s", info->dlpi_name); - } +static int AddModuleSegments(const char *module_name, dl_phdr_info *info, + InternalMmapVectorNoCtor<LoadedModule> *modules) { if (module_name[0] == '\0') return 0; LoadedModule cur_module; - cur_module.set(module_name.data(), info->dlpi_addr); + cur_module.set(module_name, info->dlpi_addr); for (int i = 0; i < (int)info->dlpi_phnum; i++) { const Elf_Phdr *phdr = &info->dlpi_phdr[i]; if (phdr->p_type == PT_LOAD) { @@ -594,7 +578,26 @@ static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { writable); } } - data->modules->push_back(cur_module); + modules->push_back(cur_module); + return 0; +} + +static int dl_iterate_phdr_cb(dl_phdr_info *info, size_t size, void *arg) { + DlIteratePhdrData *data = (DlIteratePhdrData *)arg; + if (data->first) { + InternalMmapVector<char> module_name(kMaxPathLength); + data->first = false; + // First module is the binary itself. + ReadBinaryNameCached(module_name.data(), module_name.size()); + return AddModuleSegments(module_name.data(), info, data->modules); + } + + if (info->dlpi_name) { + InternalScopedString module_name; + module_name.append("%s", info->dlpi_name); + return AddModuleSegments(module_name.data(), info, data->modules); + } + return 0; } @@ -802,20 +805,13 @@ void LogMessageOnPrintf(const char *str) { #endif // SANITIZER_LINUX -#if SANITIZER_LINUX && !SANITIZER_GO +#if SANITIZER_GLIBC && !SANITIZER_GO // glibc crashes when using clock_gettime from a preinit_array function as the // vDSO function pointers haven't been initialized yet. __progname is // initialized after the vDSO function pointers, so if it exists, is not null // and is not empty, we can use clock_gettime. extern "C" SANITIZER_WEAK_ATTRIBUTE char *__progname; -inline bool CanUseVDSO() { - // Bionic is safe, it checks for the vDSO function pointers to be initialized. - if (SANITIZER_ANDROID) - return true; - if (&__progname && __progname && *__progname) - return true; - return false; -} +inline bool CanUseVDSO() { return &__progname && __progname && *__progname; } // MonotonicNanoTime is a timing function that can leverage the vDSO by calling // clock_gettime. real_clock_gettime only exists if clock_gettime is @@ -835,13 +831,13 @@ u64 MonotonicNanoTime() { return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec; } #else -// Non-Linux & Go always use the syscall. +// Non-glibc & Go always use the regular function. u64 MonotonicNanoTime() { timespec ts; - internal_clock_gettime(CLOCK_MONOTONIC, &ts); + clock_gettime(CLOCK_MONOTONIC, &ts); return (u64)ts.tv_sec * (1000ULL * 1000 * 1000) + ts.tv_nsec; } -#endif // SANITIZER_LINUX && !SANITIZER_GO +#endif // SANITIZER_GLIBC && !SANITIZER_GO void ReExec() { const char *pathname = "/proc/self/exe"; @@ -910,6 +906,65 @@ uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale, return shadow_start; } +static uptr MmapSharedNoReserve(uptr addr, uptr size) { + return internal_mmap( + reinterpret_cast<void *>(addr), size, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); +} + +static uptr MremapCreateAlias(uptr base_addr, uptr alias_addr, + uptr alias_size) { +#if SANITIZER_LINUX + return internal_mremap(reinterpret_cast<void *>(base_addr), 0, alias_size, + MREMAP_MAYMOVE | MREMAP_FIXED, + reinterpret_cast<void *>(alias_addr)); +#else + CHECK(false && "mremap is not supported outside of Linux"); + return 0; +#endif +} + +static void CreateAliases(uptr start_addr, uptr alias_size, uptr num_aliases) { + uptr total_size = alias_size * num_aliases; + uptr mapped = MmapSharedNoReserve(start_addr, total_size); + CHECK_EQ(mapped, start_addr); + + for (uptr i = 1; i < num_aliases; ++i) { + uptr alias_addr = start_addr + i * alias_size; + CHECK_EQ(MremapCreateAlias(start_addr, alias_addr, alias_size), alias_addr); + } +} + +uptr MapDynamicShadowAndAliases(uptr shadow_size, uptr alias_size, + uptr num_aliases, uptr ring_buffer_size) { + CHECK_EQ(alias_size & (alias_size - 1), 0); + CHECK_EQ(num_aliases & (num_aliases - 1), 0); + CHECK_EQ(ring_buffer_size & (ring_buffer_size - 1), 0); + + const uptr granularity = GetMmapGranularity(); + shadow_size = RoundUpTo(shadow_size, granularity); + CHECK_EQ(shadow_size & (shadow_size - 1), 0); + + const uptr alias_region_size = alias_size * num_aliases; + const uptr alignment = + 2 * Max(Max(shadow_size, alias_region_size), ring_buffer_size); + const uptr left_padding = ring_buffer_size; + + const uptr right_size = alignment; + const uptr map_size = left_padding + 2 * alignment; + + const uptr map_start = reinterpret_cast<uptr>(MmapNoAccess(map_size)); + CHECK_NE(map_start, static_cast<uptr>(-1)); + const uptr right_start = RoundUpTo(map_start + left_padding, alignment); + + UnmapFromTo(map_start, right_start - left_padding); + UnmapFromTo(right_start + right_size, map_start + map_size); + + CreateAliases(right_start + right_size / 2, alias_size, num_aliases); + + return right_start; +} + void InitializePlatformCommonFlags(CommonFlags *cf) { #if SANITIZER_ANDROID if (&__libc_get_static_tls_bounds == nullptr) diff --git a/libsanitizer/sanitizer_common/sanitizer_local_address_space_view.h b/libsanitizer/sanitizer_common/sanitizer_local_address_space_view.h index 5d1b526..0e19c4d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_local_address_space_view.h +++ b/libsanitizer/sanitizer_common/sanitizer_local_address_space_view.h @@ -7,7 +7,7 @@ //===----------------------------------------------------------------------===// // // `LocalAddressSpaceView` provides the local (i.e. target and current address -// space are the same) implementation of the `AddressSpaveView` interface which +// space are the same) implementation of the `AddressSpaceView` interface which // provides a simple interface to load memory from another process (i.e. // out-of-process) // diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cpp b/libsanitizer/sanitizer_common/sanitizer_mac.cpp index 011ec6f..30a94fc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cpp @@ -44,6 +44,14 @@ extern char **environ; #define SANITIZER_OS_TRACE 0 #endif +// import new crash reporting api +#if defined(__has_include) && __has_include(<CrashReporterClient.h>) +#define HAVE_CRASHREPORTERCLIENT_H 1 +#include <CrashReporterClient.h> +#else +#define HAVE_CRASHREPORTERCLIENT_H 0 +#endif + #if !SANITIZER_IOS #include <crt_externs.h> // for _NSGetArgv and _NSGetEnviron #else @@ -62,6 +70,15 @@ extern "C" { #include <mach/mach_time.h> #include <mach/vm_statistics.h> #include <malloc/malloc.h> +#if defined(__has_builtin) && __has_builtin(__builtin_os_log_format) +# include <os/log.h> +#else + /* Without support for __builtin_os_log_format, fall back to the older + method. */ +# define OS_LOG_DEFAULT 0 +# define os_log_error(A,B,C) \ + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", (C)); +#endif #include <pthread.h> #include <sched.h> #include <signal.h> @@ -133,6 +150,12 @@ uptr internal_munmap(void *addr, uptr length) { return munmap(addr, length); } +uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags, + void *new_address) { + CHECK(false && "internal_mremap is unimplemented on Mac"); + return 0; +} + int internal_mprotect(void *addr, uptr length, int prot) { return mprotect(addr, length, prot); } @@ -444,7 +467,7 @@ uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { // On OS X the executable path is saved to the stack by dyld. Reading it // from there is much faster than calling dladdr, especially for large // binaries with symbols. - InternalScopedString exe_path(kMaxPathLength); + InternalMmapVector<char> exe_path(kMaxPathLength); uint32_t size = exe_path.size(); if (_NSGetExecutablePath(exe_path.data(), &size) == 0 && realpath(exe_path.data(), buf) != 0) { @@ -620,6 +643,23 @@ constexpr u16 GetOSMajorKernelOffset() { using VersStr = char[64]; +static uptr ApproximateOSVersionViaKernelVersion(VersStr vers) { + u16 kernel_major = GetDarwinKernelVersion().major; + u16 offset = GetOSMajorKernelOffset(); + CHECK_GE(kernel_major, offset); + u16 os_major = kernel_major - offset; + + const char *format = "%d.0"; + if (TARGET_OS_OSX) { + if (os_major >= 16) { // macOS 11+ + os_major -= 5; + } else { // macOS 10.15 and below + format = "10.%d"; + } + } + return internal_snprintf(vers, sizeof(VersStr), format, os_major); +} + static void GetOSVersion(VersStr vers) { uptr len = sizeof(VersStr); if (SANITIZER_IOSSIM) { @@ -633,17 +673,19 @@ static void GetOSVersion(VersStr vers) { } else { int res = internal_sysctlbyname("kern.osproductversion", vers, &len, nullptr, 0); - if (res) { - // Fallback for XNU 17 (macOS 10.13) and below that do not provide the - // `kern.osproductversion` property. - u16 kernel_major = GetDarwinKernelVersion().major; - u16 offset = GetOSMajorKernelOffset(); - CHECK_LE(kernel_major, 17); - CHECK_GE(kernel_major, offset); - u16 os_major = kernel_major - offset; - - auto format = TARGET_OS_OSX ? "10.%d" : "%d.0"; - len = internal_snprintf(vers, len, format, os_major); + + // XNU 17 (macOS 10.13) and below do not provide the sysctl + // `kern.osproductversion` entry (res != 0). + bool no_os_version = res != 0; + + // For launchd, sanitizer initialization runs before sysctl is setup + // (res == 0 && len != strlen(vers), vers is not a valid version). However, + // the kernel version `kern.osrelease` is available. + bool launchd = (res == 0 && internal_strlen(vers) < 3); + if (launchd) CHECK_EQ(internal_getpid(), 1); + + if (no_os_version || launchd) { + len = ApproximateOSVersionViaKernelVersion(vers); } } CHECK_LT(len, sizeof(VersStr)); @@ -681,7 +723,7 @@ static void MapToMacos(u16 *major, u16 *minor) { } static MacosVersion GetMacosAlignedVersionInternal() { - VersStr vers; + VersStr vers = {}; GetOSVersion(vers); u16 major, minor; @@ -707,7 +749,7 @@ MacosVersion GetMacosAlignedVersion() { } DarwinKernelVersion GetDarwinKernelVersion() { - VersStr vers; + VersStr vers = {}; uptr len = sizeof(VersStr); int res = internal_sysctlbyname("kern.osrelease", vers, &len, nullptr, 0); CHECK_EQ(res, 0); @@ -751,7 +793,51 @@ static BlockingMutex syslog_lock(LINKER_INITIALIZED); void WriteOneLineToSyslog(const char *s) { #if !SANITIZER_GO syslog_lock.CheckLocked(); - asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s); + if (GetMacosAlignedVersion() >= MacosVersion(10, 12)) { + os_log_error(OS_LOG_DEFAULT, "%{public}s", s); + } else { + asl_log(nullptr, nullptr, ASL_LEVEL_ERR, "%s", s); + } +#endif +} + +// buffer to store crash report application information +static char crashreporter_info_buff[__sanitizer::kErrorMessageBufferSize] = {}; +static BlockingMutex crashreporter_info_mutex(LINKER_INITIALIZED); + +extern "C" { +// Integrate with crash reporter libraries. +#if HAVE_CRASHREPORTERCLIENT_H +CRASH_REPORTER_CLIENT_HIDDEN +struct crashreporter_annotations_t gCRAnnotations + __attribute__((section("__DATA," CRASHREPORTER_ANNOTATIONS_SECTION))) = { + CRASHREPORTER_ANNOTATIONS_VERSION, + 0, + 0, + 0, + 0, + 0, + 0, +#if CRASHREPORTER_ANNOTATIONS_VERSION > 4 + 0, +#endif +}; + +#else +// fall back to old crashreporter api +static const char *__crashreporter_info__ __attribute__((__used__)) = + &crashreporter_info_buff[0]; +asm(".desc ___crashreporter_info__, 0x10"); +#endif + +} // extern "C" + +static void CRAppendCrashLogMessage(const char *msg) { + BlockingMutexLock l(&crashreporter_info_mutex); + internal_strlcat(crashreporter_info_buff, msg, + sizeof(crashreporter_info_buff)); +#if HAVE_CRASHREPORTERCLIENT_H + (void)CRSetCrashLogMessage(crashreporter_info_buff); #endif } @@ -947,7 +1033,7 @@ void MaybeReexec() { if (DyldNeedsEnvVariable() && !lib_is_in_env) { // DYLD_INSERT_LIBRARIES is not set or does not contain the runtime // library. - InternalScopedString program_name(1024); + InternalMmapVector<char> program_name(1024); uint32_t buf_size = program_name.size(); _NSGetExecutablePath(program_name.data(), &buf_size); char *new_env = const_cast<char*>(info.dli_fname); @@ -1066,7 +1152,7 @@ char **GetArgv() { return *_NSGetArgv(); } -#if SANITIZER_IOS +#if SANITIZER_IOS && !SANITIZER_IOSSIM // The task_vm_info struct is normally provided by the macOS SDK, but we need // fields only available in 10.12+. Declare the struct manually to be able to // build against older SDKs. @@ -1106,26 +1192,35 @@ static uptr GetTaskInfoMaxAddress() { uptr GetMaxUserVirtualAddress() { static uptr max_vm = GetTaskInfoMaxAddress(); - if (max_vm != 0) - return max_vm - 1; + if (max_vm != 0) { + const uptr ret_value = max_vm - 1; + CHECK_LE(ret_value, SANITIZER_MMAP_RANGE_SIZE); + return ret_value; + } // xnu cannot provide vm address limit # if SANITIZER_WORDSIZE == 32 - return 0xffe00000 - 1; + constexpr uptr fallback_max_vm = 0xffe00000 - 1; # else - return 0x200000000 - 1; + constexpr uptr fallback_max_vm = 0x200000000 - 1; # endif + static_assert(fallback_max_vm <= SANITIZER_MMAP_RANGE_SIZE, + "Max virtual address must be less than mmap range size."); + return fallback_max_vm; } #else // !SANITIZER_IOS uptr GetMaxUserVirtualAddress() { # if SANITIZER_WORDSIZE == 64 - return (1ULL << 47) - 1; // 0x00007fffffffffffUL; + constexpr uptr max_vm = (1ULL << 47) - 1; // 0x00007fffffffffffUL; # else // SANITIZER_WORDSIZE == 32 static_assert(SANITIZER_WORDSIZE == 32, "Wrong wordsize"); - return (1ULL << 32) - 1; // 0xffffffff; + constexpr uptr max_vm = (1ULL << 32) - 1; // 0xffffffff; # endif + static_assert(max_vm <= SANITIZER_MMAP_RANGE_SIZE, + "Max virtual address must be less than mmap range size."); + return max_vm; } #endif @@ -1180,6 +1275,12 @@ uptr MapDynamicShadow(uptr shadow_size_bytes, uptr shadow_scale, return shadow_start; } +uptr MapDynamicShadowAndAliases(uptr shadow_size, uptr alias_size, + uptr num_aliases, uptr ring_buffer_size) { + CHECK(false && "HWASan aliasing is unimplemented on Mac"); + return 0; +} + uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding, uptr *largest_gap_found, uptr *max_occupied_addr) { diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.h b/libsanitizer/sanitizer_common/sanitizer_mac.h index a2c42b3..96a5986 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.h +++ b/libsanitizer/sanitizer_common/sanitizer_mac.h @@ -84,22 +84,5 @@ void RestrictMemoryToMaxAddress(uptr max_address); } // namespace __sanitizer -extern "C" { -static char __crashreporter_info_buff__[__sanitizer::kErrorMessageBufferSize] = - {}; -static const char *__crashreporter_info__ __attribute__((__used__)) = - &__crashreporter_info_buff__[0]; -asm(".desc ___crashreporter_info__, 0x10"); -} // extern "C" - -namespace __sanitizer { -static BlockingMutex crashreporter_info_mutex(LINKER_INITIALIZED); - -inline void CRAppendCrashLogMessage(const char *msg) { - BlockingMutexLock l(&crashreporter_info_mutex); - internal_strlcat(__crashreporter_info_buff__, msg, - sizeof(__crashreporter_info_buff__)); } -} // namespace __sanitizer - #endif // SANITIZER_MAC #endif // SANITIZER_MAC_H diff --git a/libsanitizer/sanitizer_common/sanitizer_malloc_mac.inc b/libsanitizer/sanitizer_common/sanitizer_malloc_mac.inc index 647bcdf..e3b664f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_malloc_mac.inc +++ b/libsanitizer/sanitizer_common/sanitizer_malloc_mac.inc @@ -120,11 +120,7 @@ INTERCEPTOR(int, malloc_make_nonpurgeable, void *ptr) { INTERCEPTOR(void, malloc_set_zone_name, malloc_zone_t *zone, const char *name) { COMMON_MALLOC_ENTER(); - // Allocate |sizeof(COMMON_MALLOC_ZONE_NAME "-") + internal_strlen(name)| - // bytes. - size_t buflen = - sizeof(COMMON_MALLOC_ZONE_NAME "-") + (name ? internal_strlen(name) : 0); - InternalScopedString new_name(buflen); + InternalScopedString new_name; if (name && zone->introspect == sanitizer_zone.introspect) { new_name.append(COMMON_MALLOC_ZONE_NAME "-%s", name); name = new_name.data(); diff --git a/libsanitizer/sanitizer_common/sanitizer_netbsd.cpp b/libsanitizer/sanitizer_common/sanitizer_netbsd.cpp index 98ac736..ac20f91 100644 --- a/libsanitizer/sanitizer_common/sanitizer_netbsd.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_netbsd.cpp @@ -105,6 +105,12 @@ uptr internal_munmap(void *addr, uptr length) { return _REAL(munmap, addr, length); } +uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags, + void *new_address) { + CHECK(false && "internal_mremap is unimplemented on NetBSD"); + return 0; +} + int internal_mprotect(void *addr, uptr length, int prot) { DEFINE__REAL(int, mprotect, void *a, uptr b, int c); return _REAL(mprotect, addr, length, prot); diff --git a/libsanitizer/sanitizer_common/sanitizer_platform.h b/libsanitizer/sanitizer_common/sanitizer_platform.h index b2372a0..2f64584 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform.h @@ -19,12 +19,25 @@ # error "This operating system is not supported" #endif +// Get __GLIBC__ on a glibc platform. Exclude Android: features.h includes C +// function declarations into a .S file which doesn't compile. +// https://crbug.com/1162741 +#if __has_include(<features.h>) && !defined(__ANDROID__) +#include <features.h> +#endif + #if defined(__linux__) # define SANITIZER_LINUX 1 #else # define SANITIZER_LINUX 0 #endif +#if defined(__GLIBC__) +# define SANITIZER_GLIBC 1 +#else +# define SANITIZER_GLIBC 0 +#endif + #if defined(__FreeBSD__) # define SANITIZER_FREEBSD 1 #else @@ -46,6 +59,11 @@ #if defined(__APPLE__) # define SANITIZER_MAC 1 # include <TargetConditionals.h> +# if TARGET_OS_OSX +# define SANITIZER_OSX 1 +# else +# define SANITIZER_OSX 0 +# endif # if TARGET_OS_IPHONE # define SANITIZER_IOS 1 # else @@ -60,6 +78,7 @@ # define SANITIZER_MAC 0 # define SANITIZER_IOS 0 # define SANITIZER_IOSSIM 0 +# define SANITIZER_OSX 0 #endif #if defined(__APPLE__) && TARGET_OS_IPHONE && TARGET_OS_WATCH @@ -247,8 +266,12 @@ #define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 38) #elif defined(__aarch64__) # if SANITIZER_MAC -// Darwin iOS/ARM64 has a 36-bit VMA, 64GiB VM -# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 36) +# if SANITIZER_OSX || SANITIZER_IOSSIM +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 47) +# else + // Darwin iOS/ARM64 has a 36-bit VMA, 64GiB VM +# define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 36) +# endif # else # define SANITIZER_MMAP_RANGE_SIZE FIRST_32_SECOND_64(1ULL << 32, 1ULL << 48) # endif diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h index 18bab34..731df71 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h @@ -46,6 +46,12 @@ #define SI_LINUX_NOT_ANDROID 0 #endif +#if SANITIZER_GLIBC +#define SI_GLIBC 1 +#else +#define SI_GLIBC 0 +#endif + #if SANITIZER_ANDROID #define SI_ANDROID 1 #else @@ -159,7 +165,7 @@ SANITIZER_INTERCEPT_MEMCMP && \ ((SI_POSIX && _GNU_SOURCE) || SI_NETBSD || SI_FREEBSD) #define SANITIZER_INTERCEPT_STRNDUP SI_POSIX -#define SANITIZER_INTERCEPT___STRNDUP SI_LINUX_NOT_FREEBSD +#define SANITIZER_INTERCEPT___STRNDUP SI_GLIBC #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 #define SI_MAC_DEPLOYMENT_BELOW_10_7 1 @@ -183,8 +189,8 @@ #define SANITIZER_INTERCEPT_FPUTS SI_POSIX #define SANITIZER_INTERCEPT_PUTS SI_POSIX -#define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32 -#define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32 +#define SANITIZER_INTERCEPT_PREAD64 (SI_GLIBC || SI_SOLARIS32) +#define SANITIZER_INTERCEPT_PWRITE64 (SI_GLIBC || SI_SOLARIS32) #define SANITIZER_INTERCEPT_READV SI_POSIX #define SANITIZER_INTERCEPT_WRITEV SI_POSIX @@ -192,8 +198,8 @@ #define SANITIZER_INTERCEPT_PREADV \ (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID) #define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PREADV64 SI_GLIBC +#define SANITIZER_INTERCEPT_PWRITEV64 SI_GLIBC #define SANITIZER_INTERCEPT_PRCTL SI_LINUX @@ -201,16 +207,16 @@ #define SANITIZER_INTERCEPT_STRPTIME SI_POSIX #define SANITIZER_INTERCEPT_SCANF SI_POSIX -#define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_ISOC99_SCANF SI_GLIBC #ifndef SANITIZER_INTERCEPT_PRINTF #define SANITIZER_INTERCEPT_PRINTF SI_POSIX #define SANITIZER_INTERCEPT_PRINTF_L (SI_FREEBSD || SI_NETBSD) -#define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_GLIBC #endif #define SANITIZER_INTERCEPT___PRINTF_CHK \ - (SANITIZER_INTERCEPT_PRINTF && SI_LINUX_NOT_ANDROID) + (SANITIZER_INTERCEPT_PRINTF && SI_GLIBC) #define SANITIZER_INTERCEPT_FREXP SI_NOT_FUCHSIA #define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_POSIX @@ -220,13 +226,11 @@ (SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS) #define SANITIZER_INTERCEPT_GETPWENT \ (SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS) -#define SANITIZER_INTERCEPT_FGETGRENT_R \ - (SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) +#define SANITIZER_INTERCEPT_FGETGRENT_R (SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID || SI_SOLARIS #define SANITIZER_INTERCEPT_GETPWENT_R \ - (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) -#define SANITIZER_INTERCEPT_FGETPWENT_R \ - (SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) + (SI_FREEBSD || SI_NETBSD || SI_GLIBC || SI_SOLARIS) +#define SANITIZER_INTERCEPT_FGETPWENT_R (SI_FREEBSD || SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_SETPWENT \ (SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS) #define SANITIZER_INTERCEPT_CLOCK_GETTIME \ @@ -234,8 +238,8 @@ #define SANITIZER_INTERCEPT_CLOCK_GETCPUCLOCKID SI_LINUX #define SANITIZER_INTERCEPT_GETITIMER SI_POSIX #define SANITIZER_INTERCEPT_TIME SI_POSIX -#define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID || SI_SOLARIS -#define SANITIZER_INTERCEPT_GLOB64 SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_GLOB (SI_GLIBC || SI_SOLARIS) +#define SANITIZER_INTERCEPT_GLOB64 SI_GLIBC #define SANITIZER_INTERCEPT_WAIT SI_POSIX #define SANITIZER_INTERCEPT_INET SI_POSIX #define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM SI_POSIX @@ -250,8 +254,7 @@ (SI_FREEBSD || SI_LINUX_NOT_ANDROID) #define SANITIZER_INTERCEPT_GETHOSTBYADDR_R \ (SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) -#define SANITIZER_INTERCEPT_GETHOSTENT_R \ - (SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) +#define SANITIZER_INTERCEPT_GETHOSTENT_R (SI_FREEBSD || SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_GETSOCKOPT SI_POSIX #define SANITIZER_INTERCEPT_ACCEPT SI_POSIX #define SANITIZER_INTERCEPT_ACCEPT4 (SI_LINUX_NOT_ANDROID || SI_NETBSD) @@ -296,8 +299,7 @@ (SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS) #define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID || SI_SOLARIS #define SANITIZER_INTERCEPT_REALPATH SI_POSIX -#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME \ - (SI_LINUX_NOT_ANDROID || SI_SOLARIS) +#define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME (SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_CONFSTR \ (SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS) #define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID @@ -324,7 +326,7 @@ #define SANITIZER_INTERCEPT_SIGPROCMASK SI_POSIX #define SANITIZER_INTERCEPT_PTHREAD_SIGMASK SI_POSIX #define SANITIZER_INTERCEPT_BACKTRACE \ - (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) + (SI_FREEBSD || SI_NETBSD || SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX #define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATFS \ @@ -342,11 +344,11 @@ #define SANITIZER_INTERCEPT_SHMCTL \ (((SI_FREEBSD || SI_LINUX_NOT_ANDROID) && SANITIZER_WORDSIZE == 64) || \ SI_NETBSD || SI_SOLARIS) // NOLINT -#define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_RANDOM_R SI_GLIBC #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_POSIX #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \ (SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS) -#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_GLIBC #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET_SCHED SI_POSIX #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED \ (SI_POSIX && !SI_NETBSD) @@ -360,7 +362,7 @@ #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED \ (SI_POSIX && !SI_NETBSD) -#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_GLIBC #define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED (SI_POSIX && !SI_NETBSD) #define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK \ (SI_LINUX_NOT_ANDROID || SI_SOLARIS) @@ -368,7 +370,7 @@ (SI_LINUX_NOT_ANDROID && !SI_NETBSD) #define SANITIZER_INTERCEPT_THR_EXIT SI_FREEBSD #define SANITIZER_INTERCEPT_TMPNAM SI_POSIX -#define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID || SI_SOLARIS +#define SANITIZER_INTERCEPT_TMPNAM_R (SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_PTSNAME SI_LINUX #define SANITIZER_INTERCEPT_PTSNAME_R SI_LINUX #define SANITIZER_INTERCEPT_TTYNAME SI_POSIX @@ -381,7 +383,7 @@ #define SANITIZER_INTERCEPT_LGAMMAL (SI_POSIX && !SI_NETBSD) #define SANITIZER_INTERCEPT_LGAMMA_R (SI_FREEBSD || SI_LINUX || SI_SOLARIS) #define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID || SI_SOLARIS -#define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_DRAND48_R SI_GLIBC #define SANITIZER_INTERCEPT_RAND_R \ (SI_FREEBSD || SI_NETBSD || SI_MAC || SI_LINUX_NOT_ANDROID || SI_SOLARIS) #define SANITIZER_INTERCEPT_ICONV \ @@ -396,12 +398,12 @@ (SI_LINUX || SI_FREEBSD || SI_NETBSD || SI_MAC || SI_SOLARIS) #define SANITIZER_INTERCEPT_PTHREAD_MUTEX SI_POSIX -#define SANITIZER_INTERCEPT___PTHREAD_MUTEX SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT___PTHREAD_MUTEX SI_GLIBC #define SANITIZER_INTERCEPT___LIBC_MUTEX SI_NETBSD #define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \ - (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) + (SI_FREEBSD || SI_NETBSD || SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_PTHREAD_GETNAME_NP \ - (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) + (SI_FREEBSD || SI_NETBSD || SI_GLIBC || SI_SOLARIS) #define SANITIZER_INTERCEPT_TLS_GET_ADDR \ (SI_FREEBSD || SI_NETBSD || SI_LINUX_NOT_ANDROID || SI_SOLARIS) @@ -419,19 +421,19 @@ #else #define SANITIZER_INTERCEPT_AEABI_MEM 0 #endif -#define SANITIZER_INTERCEPT___BZERO SI_MAC || SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT___BZERO SI_MAC || SI_GLIBC #define SANITIZER_INTERCEPT_BZERO SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FTIME (!SI_FREEBSD && !SI_NETBSD && SI_POSIX) -#define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID || SI_SOLARIS -#define SANITIZER_INTERCEPT_XDRREC SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_XDR (SI_GLIBC || SI_SOLARIS) +#define SANITIZER_INTERCEPT_XDRREC SI_GLIBC #define SANITIZER_INTERCEPT_TSEARCH \ (SI_LINUX_NOT_ANDROID || SI_MAC || SI_NETBSD || SI_SOLARIS) -#define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_GLIBC #define SANITIZER_INTERCEPT_FOPEN SI_POSIX -#define SANITIZER_INTERCEPT_FOPEN64 SI_LINUX_NOT_ANDROID || SI_SOLARIS32 +#define SANITIZER_INTERCEPT_FOPEN64 (SI_GLIBC || SI_SOLARIS32) #define SANITIZER_INTERCEPT_OPEN_MEMSTREAM \ (SI_LINUX_NOT_ANDROID || SI_NETBSD || SI_SOLARIS) -#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_OBSTACK SI_GLIBC #define SANITIZER_INTERCEPT_FFLUSH SI_POSIX #define SANITIZER_INTERCEPT_FCLOSE SI_POSIX @@ -456,7 +458,7 @@ #define SANITIZER_INTERCEPT_CTERMID_R (SI_MAC || SI_FREEBSD || SI_SOLARIS) #define SANITIZER_INTERCEPTOR_HOOKS \ - (SI_LINUX || SI_MAC || SI_WINDOWS || SI_NETBSD) + (SI_LINUX || SI_MAC || SI_WINDOWS || SI_FREEBSD || SI_NETBSD || SI_SOLARIS) #define SANITIZER_INTERCEPT_RECV_RECVFROM SI_POSIX #define SANITIZER_INTERCEPT_SEND_SENDTO SI_POSIX #define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE SI_LINUX @@ -479,20 +481,12 @@ #define SANITIZER_INTERCEPT_MMAP SI_POSIX #define SANITIZER_INTERCEPT_MMAP64 SI_LINUX_NOT_ANDROID -#define SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO \ - (!SI_FREEBSD && !SI_MAC && !SI_NETBSD && SI_NOT_FUCHSIA && SI_NOT_RTEMS && \ - !SI_SOLARIS) // NOLINT +#define SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO (SI_GLIBC || SI_ANDROID) #define SANITIZER_INTERCEPT_MEMALIGN \ (!SI_FREEBSD && !SI_MAC && !SI_NETBSD && SI_NOT_RTEMS) -#define SANITIZER_INTERCEPT___LIBC_MEMALIGN \ - (!SI_FREEBSD && !SI_MAC && !SI_NETBSD && !SI_OPENBSD && SI_NOT_RTEMS && \ - !SI_ANDROID) // NOLINT -#define SANITIZER_INTERCEPT_PVALLOC \ - (!SI_FREEBSD && !SI_MAC && !SI_NETBSD && SI_NOT_FUCHSIA && SI_NOT_RTEMS && \ - !SI_SOLARIS) // NOLINT -#define SANITIZER_INTERCEPT_CFREE \ - (!SI_FREEBSD && !SI_MAC && !SI_NETBSD && SI_NOT_FUCHSIA && SI_NOT_RTEMS && \ - !SI_SOLARIS && !SANITIZER_ANDROID) // NOLINT +#define SANITIZER_INTERCEPT___LIBC_MEMALIGN SI_GLIBC +#define SANITIZER_INTERCEPT_PVALLOC (SI_GLIBC || SI_ANDROID) +#define SANITIZER_INTERCEPT_CFREE (SI_GLIBC && !SANITIZER_RISCV64) #define SANITIZER_INTERCEPT_REALLOCARRAY SI_POSIX #define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC && SI_NOT_RTEMS) #define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC && !SI_NETBSD) @@ -532,7 +526,7 @@ #define SANITIZER_INTERCEPT_STRMODE (SI_NETBSD || SI_FREEBSD) #define SANITIZER_INTERCEPT_TTYENT SI_NETBSD #define SANITIZER_INTERCEPT_PROTOENT (SI_NETBSD || SI_LINUX) -#define SANITIZER_INTERCEPT_PROTOENT_R (SI_LINUX_NOT_ANDROID) +#define SANITIZER_INTERCEPT_PROTOENT_R SI_GLIBC #define SANITIZER_INTERCEPT_NETENT SI_NETBSD #define SANITIZER_INTERCEPT_SETVBUF \ (SI_NETBSD || SI_FREEBSD || SI_LINUX || SI_MAC) @@ -583,7 +577,7 @@ #define SANITIZER_INTERCEPT_GETENTROPY SI_FREEBSD #define SANITIZER_INTERCEPT_QSORT \ (SI_POSIX && !SI_IOSSIM && !SI_WATCHOS && !SI_TVOS && !SI_ANDROID) -#define SANITIZER_INTERCEPT_QSORT_R (SI_LINUX && !SI_ANDROID) +#define SANITIZER_INTERCEPT_QSORT_R SI_GLIBC // sigaltstack on i386 macOS cannot be intercepted due to setjmp() // calling it and assuming that it does not clobber registers. #define SANITIZER_INTERCEPT_SIGALTSTACK \ @@ -591,4 +585,25 @@ #define SANITIZER_INTERCEPT_UNAME (SI_POSIX && !SI_FREEBSD) #define SANITIZER_INTERCEPT___XUNAME SI_FREEBSD +// This macro gives a way for downstream users to override the above +// interceptor macros irrespective of the platform they are on. They have +// to do two things: +// 1. Build compiler-rt with -DSANITIZER_OVERRIDE_INTERCEPTORS. +// 2. Provide a header file named sanitizer_intercept_overriders.h in the +// include path for their compiler-rt build. +// An example of an overrider for strlen interceptor that one can list in +// sanitizer_intercept_overriders.h is as follows: +// +// #ifdef SANITIZER_INTERCEPT_STRLEN +// #undef SANITIZER_INTERCEPT_STRLEN +// #define SANITIZER_INTERCEPT_STRLEN <value of choice> +// #endif +// +// This "feature" is useful for downstream users who do not want some of +// their libc funtions to be intercepted. They can selectively disable +// interception of those functions. +#ifdef SANITIZER_OVERRIDE_INTERCEPTORS +#include <sanitizer_intercept_overriders.h> +#endif + #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.cpp b/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.cpp index b1c15be..b5a45ae 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_freebsd.cpp @@ -35,7 +35,10 @@ #include <sys/stat.h> #include <sys/statvfs.h> #include <sys/time.h> +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-W#warnings" #include <sys/timeb.h> +#pragma clang diagnostic pop #include <sys/times.h> #include <sys/timespec.h> #include <sys/types.h> diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp index 1427cec..35a690c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cpp @@ -11,19 +11,20 @@ // Sizes and layouts of platform-specific POSIX data structures. //===----------------------------------------------------------------------===// -#include "sanitizer_platform.h" - -#if SANITIZER_LINUX || SANITIZER_MAC +#if defined(__linux__) || defined(__APPLE__) // Tests in this file assume that off_t-dependent data structures match the // libc ABI. For example, struct dirent here is what readdir() function (as // exported from libc) returns, and not the user-facing "dirent", which // depends on _FILE_OFFSET_BITS setting. // To get this "true" dirent definition, we undefine _FILE_OFFSET_BITS below. -#ifdef _FILE_OFFSET_BITS #undef _FILE_OFFSET_BITS #endif // Must go after undef _FILE_OFFSET_BITS. +#include "sanitizer_platform.h" + +#if SANITIZER_LINUX || SANITIZER_MAC +// Must go after undef _FILE_OFFSET_BITS. #include "sanitizer_glibc_version.h" #include <arpa/inet.h> @@ -37,6 +38,7 @@ #include <pwd.h> #include <signal.h> #include <stddef.h> +#include <stdio.h> #include <sys/mman.h> #include <sys/resource.h> #include <sys/socket.h> @@ -58,7 +60,6 @@ #endif #if !SANITIZER_ANDROID -#include <fstab.h> #include <sys/mount.h> #include <sys/timeb.h> #include <utmpx.h> @@ -110,20 +111,31 @@ typedef struct user_fpregs elf_fpregset_t; #include <wordexp.h> #endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID -#include <glob.h> -#include <obstack.h> -#include <mqueue.h> +#if SANITIZER_LINUX +#if SANITIZER_GLIBC +#include <fstab.h> #include <net/if_ppp.h> #include <netax25/ax25.h> #include <netipx/ipx.h> #include <netrom/netrom.h> +#include <obstack.h> #if HAVE_RPC_XDR_H # include <rpc/xdr.h> #endif #include <scsi/scsi.h> -#include <sys/mtio.h> +#else +#include <linux/if_ppp.h> +#include <linux/kd.h> +#include <linux/ppp_defs.h> +#endif // SANITIZER_GLIBC + +#if SANITIZER_ANDROID +#include <linux/mtio.h> +#else +#include <glob.h> +#include <mqueue.h> #include <sys/kd.h> +#include <sys/mtio.h> #include <sys/shm.h> #include <sys/statvfs.h> #include <sys/timex.h> @@ -142,20 +154,14 @@ typedef struct user_fpregs elf_fpregset_t; #include <sys/msg.h> #include <sys/ipc.h> #include <crypt.h> -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID +#endif // SANITIZER_ANDROID -#if SANITIZER_ANDROID -#include <linux/kd.h> -#include <linux/mtio.h> -#include <linux/ppp_defs.h> -#include <linux/if_ppp.h> -#endif - -#if SANITIZER_LINUX #include <link.h> #include <sys/vfs.h> #include <sys/epoll.h> #include <linux/capability.h> +#else +#include <fstab.h> #endif // SANITIZER_LINUX #if SANITIZER_MAC @@ -202,8 +208,11 @@ namespace __sanitizer { unsigned struct_statfs64_sz = sizeof(struct statfs64); #endif // (SANITIZER_MAC && !TARGET_CPU_ARM64) && !SANITIZER_IOS -#if !SANITIZER_ANDROID +#if SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD || SANITIZER_MAC unsigned struct_fstab_sz = sizeof(struct fstab); +#endif // SANITIZER_GLIBC || SANITIZER_FREEBSD || SANITIZER_NETBSD || + // SANITIZER_MAC +#if !SANITIZER_ANDROID unsigned struct_statfs_sz = sizeof(struct statfs); unsigned struct_sockaddr_sz = sizeof(struct sockaddr); unsigned ucontext_t_sz = sizeof(ucontext_t); @@ -299,7 +308,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(ElfW(Phdr)); unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); #endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if SANITIZER_GLIBC int glob_nomatch = GLOB_NOMATCH; int glob_altdirfunc = GLOB_ALTDIRFUNC; #endif @@ -422,7 +431,9 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); unsigned struct_input_id_sz = sizeof(struct input_id); unsigned struct_mtpos_sz = sizeof(struct mtpos); unsigned struct_rtentry_sz = sizeof(struct rtentry); +#if SANITIZER_GLIBC || SANITIZER_ANDROID unsigned struct_termio_sz = sizeof(struct termio); +#endif unsigned struct_vt_consize_sz = sizeof(struct vt_consize); unsigned struct_vt_sizes_sz = sizeof(struct vt_sizes); unsigned struct_vt_stat_sz = sizeof(struct vt_stat); @@ -447,7 +458,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); unsigned struct_vt_mode_sz = sizeof(struct vt_mode); #endif // SANITIZER_LINUX -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if SANITIZER_GLIBC unsigned struct_ax25_parms_struct_sz = sizeof(struct ax25_parms_struct); unsigned struct_cyclades_monitor_sz = sizeof(struct cyclades_monitor); #if EV_VERSION > (0x010000) @@ -470,12 +481,10 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); unsigned struct_sockaddr_ax25_sz = sizeof(struct sockaddr_ax25); unsigned struct_unimapdesc_sz = sizeof(struct unimapdesc); unsigned struct_unimapinit_sz = sizeof(struct unimapinit); -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID -#if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_audio_buf_info_sz = sizeof(struct audio_buf_info); unsigned struct_ppp_stats_sz = sizeof(struct ppp_stats); -#endif // (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID +#endif // SANITIZER_GLIBC #if !SANITIZER_ANDROID && !SANITIZER_MAC unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req); @@ -881,6 +890,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); unsigned IOCTL_PIO_UNIMAP = PIO_UNIMAP; unsigned IOCTL_PIO_UNIMAPCLR = PIO_UNIMAPCLR; unsigned IOCTL_PIO_UNISCRNMAP = PIO_UNISCRNMAP; +#if SANITIZER_GLIBC unsigned IOCTL_SCSI_IOCTL_GET_IDLUN = SCSI_IOCTL_GET_IDLUN; unsigned IOCTL_SCSI_IOCTL_PROBE_HOST = SCSI_IOCTL_PROBE_HOST; unsigned IOCTL_SCSI_IOCTL_TAGGED_DISABLE = SCSI_IOCTL_TAGGED_DISABLE; @@ -899,6 +909,7 @@ unsigned struct_ElfW_Phdr_sz = sizeof(Elf_Phdr); unsigned IOCTL_SIOCNRGETPARMS = SIOCNRGETPARMS; unsigned IOCTL_SIOCNRRTCTL = SIOCNRRTCTL; unsigned IOCTL_SIOCNRSETPARMS = SIOCNRSETPARMS; +#endif unsigned IOCTL_TIOCGSERIAL = TIOCGSERIAL; unsigned IOCTL_TIOCSERGETMULTI = TIOCSERGETMULTI; unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI; @@ -969,7 +980,7 @@ CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phdr); CHECK_SIZE_AND_OFFSET(dl_phdr_info, dlpi_phnum); #endif // SANITIZER_LINUX || SANITIZER_FREEBSD -#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID +#if SANITIZER_GLIBC || SANITIZER_FREEBSD CHECK_TYPE_SIZE(glob_t); CHECK_SIZE_AND_OFFSET(glob_t, gl_pathc); CHECK_SIZE_AND_OFFSET(glob_t, gl_pathv); @@ -980,7 +991,7 @@ CHECK_SIZE_AND_OFFSET(glob_t, gl_readdir); CHECK_SIZE_AND_OFFSET(glob_t, gl_opendir); CHECK_SIZE_AND_OFFSET(glob_t, gl_lstat); CHECK_SIZE_AND_OFFSET(glob_t, gl_stat); -#endif +#endif // SANITIZER_GLIBC || SANITIZER_FREEBSD CHECK_TYPE_SIZE(addrinfo); CHECK_SIZE_AND_OFFSET(addrinfo, ai_flags); @@ -1003,17 +1014,27 @@ CHECK_TYPE_SIZE(iovec); CHECK_SIZE_AND_OFFSET(iovec, iov_base); CHECK_SIZE_AND_OFFSET(iovec, iov_len); +// In POSIX, int msg_iovlen; socklen_t msg_controllen; socklen_t cmsg_len; but +// many implementations don't conform to the standard. Since we pick the +// non-conforming glibc definition, exclude the checks for musl (incompatible +// sizes but compatible offsets). CHECK_TYPE_SIZE(msghdr); CHECK_SIZE_AND_OFFSET(msghdr, msg_name); CHECK_SIZE_AND_OFFSET(msghdr, msg_namelen); CHECK_SIZE_AND_OFFSET(msghdr, msg_iov); +#if SANITIZER_GLIBC || SANITIZER_ANDROID CHECK_SIZE_AND_OFFSET(msghdr, msg_iovlen); +#endif CHECK_SIZE_AND_OFFSET(msghdr, msg_control); +#if SANITIZER_GLIBC || SANITIZER_ANDROID CHECK_SIZE_AND_OFFSET(msghdr, msg_controllen); +#endif CHECK_SIZE_AND_OFFSET(msghdr, msg_flags); CHECK_TYPE_SIZE(cmsghdr); +#if SANITIZER_GLIBC || SANITIZER_ANDROID CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_len); +#endif CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_level); CHECK_SIZE_AND_OFFSET(cmsghdr, cmsg_type); @@ -1121,7 +1142,7 @@ CHECK_SIZE_AND_OFFSET(mntent, mnt_passno); CHECK_TYPE_SIZE(ether_addr); -#if (SANITIZER_LINUX || SANITIZER_FREEBSD) && !SANITIZER_ANDROID +#if SANITIZER_GLIBC || SANITIZER_FREEBSD CHECK_TYPE_SIZE(ipc_perm); # if SANITIZER_FREEBSD CHECK_SIZE_AND_OFFSET(ipc_perm, key); @@ -1183,7 +1204,7 @@ CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_dstaddr); CHECK_SIZE_AND_OFFSET(ifaddrs, ifa_data); #endif -#if SANITIZER_LINUX +#if SANITIZER_GLIBC || SANITIZER_ANDROID COMPILER_CHECK(sizeof(__sanitizer_struct_mallinfo) == sizeof(struct mallinfo)); #endif @@ -1233,7 +1254,7 @@ COMPILER_CHECK(__sanitizer_XDR_DECODE == XDR_DECODE); COMPILER_CHECK(__sanitizer_XDR_FREE == XDR_FREE); #endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID +#if SANITIZER_GLIBC COMPILER_CHECK(sizeof(__sanitizer_FILE) <= sizeof(FILE)); CHECK_SIZE_AND_OFFSET(FILE, _flags); CHECK_SIZE_AND_OFFSET(FILE, _IO_read_ptr); @@ -1250,9 +1271,7 @@ CHECK_SIZE_AND_OFFSET(FILE, _IO_save_end); CHECK_SIZE_AND_OFFSET(FILE, _markers); CHECK_SIZE_AND_OFFSET(FILE, _chain); CHECK_SIZE_AND_OFFSET(FILE, _fileno); -#endif -#if SANITIZER_LINUX && !SANITIZER_ANDROID COMPILER_CHECK(sizeof(__sanitizer__obstack_chunk) <= sizeof(_obstack_chunk)); CHECK_SIZE_AND_OFFSET(_obstack_chunk, limit); CHECK_SIZE_AND_OFFSET(_obstack_chunk, prev); @@ -1267,7 +1286,7 @@ CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, read); CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, write); CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, seek); CHECK_SIZE_AND_OFFSET(cookie_io_functions_t, close); -#endif +#endif // SANITIZER_GLIBC #if SANITIZER_LINUX || SANITIZER_FREEBSD CHECK_TYPE_SIZE(sem_t); diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h index 0812039..ad358ee 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h @@ -443,6 +443,8 @@ struct __sanitizer_cmsghdr { int cmsg_type; }; #else +// In POSIX, int msg_iovlen; socklen_t msg_controllen; socklen_t cmsg_len; but +// many implementations don't conform to the standard. struct __sanitizer_msghdr { void *msg_name; unsigned msg_namelen; diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.cpp b/libsanitizer/sanitizer_common/sanitizer_posix.cpp index 2e08009..f8457a6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_posix.cpp @@ -275,8 +275,8 @@ void ReportFile::Write(const char *buffer, uptr length) { bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { MemoryMappingLayout proc_maps(/*cache_enabled*/false); - InternalScopedString buff(kMaxPathLength); - MemoryMappedSegment segment(buff.data(), kMaxPathLength); + InternalMmapVector<char> buff(kMaxPathLength); + MemoryMappedSegment segment(buff.data(), buff.size()); while (proc_maps.Next(&segment)) { if (segment.IsExecutable() && internal_strcmp(module, segment.filename) == 0) { diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.h b/libsanitizer/sanitizer_common/sanitizer_posix.h index e1a2b48..b65dae6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix.h +++ b/libsanitizer/sanitizer_common/sanitizer_posix.h @@ -40,6 +40,10 @@ uptr internal_write(fd_t fd, const void *buf, uptr count); uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, u64 offset); uptr internal_munmap(void *addr, uptr length); +#if SANITIZER_LINUX +uptr internal_mremap(void *old_address, uptr old_size, uptr new_size, int flags, + void *new_address); +#endif int internal_mprotect(void *addr, uptr length, int prot); int internal_madvise(uptr addr, uptr length, int advice); diff --git a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp index 7ff48c3..d1d8e50 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cpp @@ -143,7 +143,7 @@ void Abort() { if (GetHandleSignalMode(SIGABRT) != kHandleSignalNo) { struct sigaction sigact; internal_memset(&sigact, 0, sizeof(sigact)); - sigact.sa_sigaction = (sa_sigaction_t)SIG_DFL; + sigact.sa_handler = SIG_DFL; internal_sigaction(SIGABRT, &sigact, nullptr); } #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cpp b/libsanitizer/sanitizer_common/sanitizer_printf.cpp index a032787..5d16dfd 100644 --- a/libsanitizer/sanitizer_common/sanitizer_printf.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cpp @@ -249,26 +249,21 @@ static void NOINLINE SharedPrintfCodeNoBuffer(bool append_pid, va_list args) { va_list args2; va_copy(args2, args); - const int kLen = 16 * 1024; - int needed_length; + InternalMmapVector<char> v; + int needed_length = 0; char *buffer = local_buffer; // First try to print a message using a local buffer, and then fall back to // mmaped buffer. - for (int use_mmap = 0; use_mmap < 2; use_mmap++) { + for (int use_mmap = 0;; use_mmap++) { if (use_mmap) { va_end(args); va_copy(args, args2); - buffer = (char*)MmapOrDie(kLen, "Report"); - buffer_size = kLen; + v.resize(needed_length + 1); + buffer_size = v.capacity(); + v.resize(buffer_size); + buffer = &v[0]; } needed_length = 0; - // Check that data fits into the current buffer. -# define CHECK_NEEDED_LENGTH \ - if (needed_length >= buffer_size) { \ - if (!use_mmap) continue; \ - RAW_CHECK_MSG(needed_length < kLen, \ - "Buffer in Report is too short!\n"); \ - } // Fuchsia's logging infrastructure always keeps track of the logging // process, thread, and timestamp, so never prepend such information. if (!SANITIZER_FUCHSIA && append_pid) { @@ -277,18 +272,20 @@ static void NOINLINE SharedPrintfCodeNoBuffer(bool append_pid, if (common_flags()->log_exe_name && exe_name) { needed_length += internal_snprintf(buffer, buffer_size, "==%s", exe_name); - CHECK_NEEDED_LENGTH + if (needed_length >= buffer_size) + continue; } needed_length += internal_snprintf( buffer + needed_length, buffer_size - needed_length, "==%d==", pid); - CHECK_NEEDED_LENGTH + if (needed_length >= buffer_size) + continue; } needed_length += VSNPrintf(buffer + needed_length, buffer_size - needed_length, format, args); - CHECK_NEEDED_LENGTH + if (needed_length >= buffer_size) + continue; // If the message fit into the buffer, print it and exit. break; -# undef CHECK_NEEDED_LENGTH } RawWrite(buffer); @@ -297,9 +294,6 @@ static void NOINLINE SharedPrintfCodeNoBuffer(bool append_pid, CallPrintfAndReportCallback(buffer); LogMessageOnPrintf(buffer); - // If we had mapped any memory, clean up. - if (buffer != local_buffer) - UnmapOrDie((void *)buffer, buffer_size); va_end(args2); } @@ -346,13 +340,24 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...) { FORMAT(2, 3) void InternalScopedString::append(const char *format, ...) { - CHECK_LT(length_, size()); - va_list args; - va_start(args, format); - VSNPrintf(data() + length_, size() - length_, format, args); - va_end(args); - length_ += internal_strlen(data() + length_); - CHECK_LT(length_, size()); + uptr prev_len = length(); + + while (true) { + buffer_.resize(buffer_.capacity()); + + va_list args; + va_start(args, format); + uptr sz = VSNPrintf(buffer_.data() + prev_len, buffer_.size() - prev_len, + format, args); + va_end(args); + if (sz < buffer_.size() - prev_len) { + buffer_.resize(prev_len + sz + 1); + break; + } + + buffer_.reserve(buffer_.capacity() * 2); + } + CHECK_EQ(buffer_[length()], '\0'); } } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cpp index f2cfcff..1b7dd46 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_common.cpp @@ -120,7 +120,7 @@ void MemoryMappingLayout::LoadFromCache() { void MemoryMappingLayout::DumpListOfModules( InternalMmapVectorNoCtor<LoadedModule> *modules) { Reset(); - InternalScopedString module_name(kMaxPathLength); + InternalMmapVector<char> module_name(kMaxPathLength); MemoryMappedSegment segment(module_name.data(), module_name.size()); for (uptr i = 0; Next(&segment); i++) { const char *cur_name = segment.filename; diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp index d02afcf..1f53e3e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp @@ -354,8 +354,8 @@ bool MemoryMappingLayout::Next(MemoryMappedSegment *segment) { void MemoryMappingLayout::DumpListOfModules( InternalMmapVectorNoCtor<LoadedModule> *modules) { Reset(); - InternalScopedString module_name(kMaxPathLength); - MemoryMappedSegment segment(module_name.data(), kMaxPathLength); + InternalMmapVector<char> module_name(kMaxPathLength); + MemoryMappedSegment segment(module_name.data(), module_name.size()); MemoryMappedSegmentData data; segment.data_ = &data; while (Next(&segment)) { diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_solaris.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_solaris.cpp index 4063ec8..bf813f23 100644 --- a/libsanitizer/sanitizer_common/sanitizer_procmaps_solaris.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_solaris.cpp @@ -9,13 +9,13 @@ // Information about the process mappings (Solaris-specific parts). //===----------------------------------------------------------------------===// +// Before Solaris 11.4, <procfs.h> doesn't work in a largefile environment. +#undef _FILE_OFFSET_BITS #include "sanitizer_platform.h" #if SANITIZER_SOLARIS #include "sanitizer_common.h" #include "sanitizer_procmaps.h" -// Before Solaris 11.4, <procfs.h> doesn't work in a largefile environment. -#undef _FILE_OFFSET_BITS #include <procfs.h> #include <limits.h> diff --git a/libsanitizer/sanitizer_common/sanitizer_ptrauth.h b/libsanitizer/sanitizer_common/sanitizer_ptrauth.h index a288068..5200354 100644 --- a/libsanitizer/sanitizer_common/sanitizer_ptrauth.h +++ b/libsanitizer/sanitizer_common/sanitizer_ptrauth.h @@ -11,6 +11,24 @@ #if __has_feature(ptrauth_calls) #include <ptrauth.h> +#elif defined(__ARM_FEATURE_PAC_DEFAULT) && !defined(__APPLE__) +inline unsigned long ptrauth_strip(void* __value, unsigned int __key) { + // On the stack the link register is protected with Pointer + // Authentication Code when compiled with -mbranch-protection. + // Let's stripping the PAC unconditionally because xpaclri is in + // the NOP space so will do nothing when it is not enabled or not available. + unsigned long ret; + asm volatile( + "mov x30, %1\n\t" + "hint #7\n\t" // xpaclri + "mov %0, x30\n\t" + : "=r"(ret) + : "r"(__value) + : "x30"); + return ret; +} +#define ptrauth_auth_data(__value, __old_key, __old_data) __value +#define ptrauth_string_discriminator(__string) ((int)0) #else // Copied from <ptrauth.h> #define ptrauth_strip(__value, __key) __value @@ -18,6 +36,6 @@ #define ptrauth_string_discriminator(__string) ((int)0) #endif -#define STRIP_PC(pc) ((uptr)ptrauth_strip(pc, 0)) +#define STRIP_PAC_PC(pc) ((uptr)ptrauth_strip(pc, 0)) #endif // SANITIZER_PTRAUTH_H diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cpp b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cpp index 4692f50..44a9521 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cpp @@ -145,8 +145,7 @@ StackTrace StackDepotReverseMap::Get(u32 id) { if (!map_.size()) return StackTrace(); IdDescPair pair = {id, nullptr}; - uptr idx = - InternalLowerBound(map_, 0, map_.size(), pair, IdDescPair::IdComparator); + uptr idx = InternalLowerBound(map_, pair, IdDescPair::IdComparator); if (idx > map_.size() || map_[idx].id != id) return StackTrace(); return map_[idx].desc->load(); diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp index b28fc1c..515dedd 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cpp @@ -15,6 +15,7 @@ #include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_platform.h" +#include "sanitizer_ptrauth.h" namespace __sanitizer { @@ -129,7 +130,7 @@ void BufferedStackTrace::UnwindFast(uptr pc, uptr bp, uptr stack_top, // frame[-1] contains the return address uhwptr pc1 = frame[-1]; #else - uhwptr pc1 = frame[1]; + uhwptr pc1 = STRIP_PAC_PC((void *)frame[1]); #endif // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and // x86_64) is invalid and stop unwinding here. If we're adding support for diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h index 0350fe8..15616f8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h @@ -67,8 +67,6 @@ struct StackTrace { static uptr GetCurrentPc(); static inline uptr GetPreviousInstructionPc(uptr pc); static uptr GetNextInstructionPc(uptr pc); - typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer, - int out_size); }; // Performance-critical, must be in the header. diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp index 7808ba9..7386332 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cpp @@ -23,8 +23,8 @@ void StackTrace::Print() const { Printf(" <empty stack>\n\n"); return; } - InternalScopedString frame_desc(GetPageSizeCached() * 2); - InternalScopedString dedup_token(GetPageSizeCached()); + InternalScopedString frame_desc; + InternalScopedString dedup_token; int dedup_frames = common_flags()->dedup_token_length; bool symbolize = RenderNeedsSymbolization(common_flags()->stack_trace_format); uptr frame_num = 0; @@ -125,7 +125,7 @@ void __sanitizer_symbolize_pc(uptr pc, const char *fmt, char *out_buf, out_buf[out_buf_size - 1] = 0; return; } - InternalScopedString frame_desc(GetPageSizeCached()); + InternalScopedString frame_desc; uptr frame_num = 0; // Reserve one byte for the final 0. char *out_end = out_buf + out_buf_size - 1; @@ -156,7 +156,7 @@ void __sanitizer_symbolize_global(uptr data_addr, const char *fmt, out_buf[0] = 0; DataInfo DI; if (!Symbolizer::GetOrInit()->SymbolizeData(data_addr, &DI)) return; - InternalScopedString data_desc(GetPageSizeCached()); + InternalScopedString data_desc; RenderData(&data_desc, fmt, &DI, common_flags()->strip_path_prefix); internal_strncpy(out_buf, data_desc.data(), out_buf_size); out_buf[out_buf_size - 1] = 0; diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp index 0f1cadf..53cfddc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cpp @@ -490,6 +490,9 @@ typedef user_regs_struct regs_struct; #ifndef NT_X86_XSTATE #define NT_X86_XSTATE 0x202 #endif +#ifndef PTRACE_GETREGSET +#define PTRACE_GETREGSET 0x4204 +#endif // Compiler may use FP registers to store pointers. static constexpr uptr kExtraRegs[] = {NT_X86_XSTATE, NT_FPREGSET}; @@ -513,6 +516,8 @@ static constexpr uptr kExtraRegs[] = {0}; #elif SANITIZER_RISCV64 typedef struct user_regs_struct regs_struct; +// sys/ucontext.h already defines REG_SP as 2. Undefine it first. +#undef REG_SP #define REG_SP sp static constexpr uptr kExtraRegs[] = {0}; #define ARCH_IOVEC_FOR_GETREGSET diff --git a/libsanitizer/sanitizer_common/sanitizer_suppressions.cpp b/libsanitizer/sanitizer_common/sanitizer_suppressions.cpp index 44c83a6..a674034 100644 --- a/libsanitizer/sanitizer_common/sanitizer_suppressions.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_suppressions.cpp @@ -34,7 +34,7 @@ SuppressionContext::SuppressionContext(const char *suppression_types[], static bool GetPathAssumingFileIsRelativeToExec(const char *file_path, /*out*/char *new_file_path, uptr new_file_path_size) { - InternalScopedString exec(kMaxPathLength); + InternalMmapVector<char> exec(kMaxPathLength); if (ReadBinaryNameCached(exec.data(), exec.size())) { const char *file_name_pos = StripModuleName(exec.data()); uptr path_to_exec_len = file_name_pos - exec.data(); @@ -69,7 +69,7 @@ void SuppressionContext::ParseFromFile(const char *filename) { if (filename[0] == '\0') return; - InternalScopedString new_file_path(kMaxPathLength); + InternalMmapVector<char> new_file_path(kMaxPathLength); filename = FindFile(filename, new_file_path.data(), new_file_path.size()); // Read the file. diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp index 710da4c..98418b4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cpp @@ -356,7 +356,7 @@ void ParseSymbolizePCOutput(const char *str, SymbolizedStack *res) { InternalFree(info->function); info->function = 0; } - if (0 == internal_strcmp(info->file, "??")) { + if (info->file && 0 == internal_strcmp(info->file, "??")) { InternalFree(info->file); info->file = 0; } diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.cpp index 30cba08..01edef9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_markup.cpp @@ -54,6 +54,10 @@ bool Symbolizer::GetModuleNameAndOffsetForPC(uptr pc, const char **module_name, return false; } +// This is mainly used by hwasan for online symbolization. This isn't needed +// since hwasan can always just dump stack frames for offline symbolization. +bool Symbolizer::SymbolizeFrame(uptr addr, FrameInfo *info) { return false; } + // This is used in some places for suppression checking, which we // don't really support for Fuchsia. It's also used in UBSan to // identify a PC location to a function name, so we always fill in diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp index 4dd5cc3..4cd4b46 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cpp @@ -400,11 +400,20 @@ const char *Symbolizer::PlatformDemangle(const char *name) { static SymbolizerTool *ChooseExternalSymbolizer(LowLevelAllocator *allocator) { const char *path = common_flags()->external_symbolizer_path; + + if (path && internal_strchr(path, '%')) { + char *new_path = (char *)InternalAlloc(kMaxPathLength); + SubstituteForFlagValue(path, new_path, kMaxPathLength); + path = new_path; + } + const char *binary_name = path ? StripModuleName(path) : ""; + static const char kLLVMSymbolizerPrefix[] = "llvm-symbolizer"; if (path && path[0] == '\0') { VReport(2, "External symbolizer is explicitly disabled.\n"); return nullptr; - } else if (!internal_strcmp(binary_name, "llvm-symbolizer")) { + } else if (!internal_strncmp(binary_name, kLLVMSymbolizerPrefix, + internal_strlen(kLLVMSymbolizerPrefix))) { VReport(2, "Using llvm-symbolizer at user-specified path: %s\n", path); return new(*allocator) LLVMSymbolizer(path, allocator); } else if (!internal_strcmp(binary_name, "atos")) { diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cpp index 06301b8..9287993 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cpp @@ -31,7 +31,7 @@ namespace __sanitizer { void ReportErrorSummary(const char *error_type, const AddressInfo &info, const char *alt_tool_name) { if (!common_flags()->print_summary) return; - InternalScopedString buff(kMaxSummaryLength); + InternalScopedString buff; buff.append("%s ", error_type); RenderFrame(&buff, "%L %F", 0, info.address, &info, common_flags()->symbolize_vs_style, @@ -150,7 +150,7 @@ static void PrintMemoryByte(InternalScopedString *str, const char *before, static void MaybeDumpInstructionBytes(uptr pc) { if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) return; - InternalScopedString str(1024); + InternalScopedString str; str.append("First 16 instruction bytes at pc: "); if (IsAccessibleMemoryRange(pc, 16)) { for (int i = 0; i < 16; ++i) { @@ -211,7 +211,7 @@ static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid, Report("The signal is caused by a %s memory access.\n", access_type); if (!sig.is_true_faulting_addr) Report("Hint: this fault was caused by a dereference of a high value " - "address (see register values below). Dissassemble the provided " + "address (see register values below). Disassemble the provided " "pc to learn which register was used.\n"); else if (sig.addr < GetPageSizeCached()) Report("Hint: address points to the zero page.\n"); diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cpp b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cpp index 48fa2d1..702d901 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cpp @@ -136,9 +136,10 @@ void InitializeDbgHelpIfNeeded() { bool WinSymbolizerTool::SymbolizePC(uptr addr, SymbolizedStack *frame) { InitializeDbgHelpIfNeeded(); - // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx - char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)]; - PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer; + // See https://docs.microsoft.com/en-us/windows/win32/debug/retrieving-symbol-information-by-address + InternalMmapVector<char> buffer(sizeof(SYMBOL_INFO) + + MAX_SYM_NAME * sizeof(CHAR)); + PSYMBOL_INFO symbol = (PSYMBOL_INFO)&buffer[0]; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; DWORD64 offset = 0; @@ -223,7 +224,7 @@ bool SymbolizerProcess::StartSymbolizerSubprocess() { // Compute the command line. Wrap double quotes around everything. const char *argv[kArgVMax]; GetArgV(path_, argv); - InternalScopedString command_line(kMaxPathLength * 3); + InternalScopedString command_line; for (int i = 0; argv[i]; i++) { const char *arg = argv[i]; int arglen = internal_strlen(arg); @@ -281,8 +282,15 @@ static void ChooseSymbolizerTools(IntrusiveList<SymbolizerTool> *list, return; } - // Add llvm-symbolizer in case the binary has dwarf. + // Add llvm-symbolizer. const char *user_path = common_flags()->external_symbolizer_path; + + if (user_path && internal_strchr(user_path, '%')) { + char *new_path = (char *)InternalAlloc(kMaxPathLength); + SubstituteForFlagValue(user_path, new_path, kMaxPathLength); + user_path = new_path; + } + const char *path = user_path ? user_path : FindPathToBinary("llvm-symbolizer.exe"); if (path) { diff --git a/libsanitizer/sanitizer_common/sanitizer_termination.cpp b/libsanitizer/sanitizer_common/sanitizer_termination.cpp index 84be6fc..6a54734 100644 --- a/libsanitizer/sanitizer_common/sanitizer_termination.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_termination.cpp @@ -59,26 +59,31 @@ void NORETURN Die() { internal__exit(common_flags()->exitcode); } -static CheckFailedCallbackType CheckFailedCallback; -void SetCheckFailedCallback(CheckFailedCallbackType callback) { - CheckFailedCallback = callback; +static void (*CheckUnwindCallback)(); +void SetCheckUnwindCallback(void (*callback)()) { + CheckUnwindCallback = callback; } -const int kSecondsToSleepWhenRecursiveCheckFailed = 2; - void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2) { - static atomic_uint32_t num_calls; - if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) > 10) { - SleepForSeconds(kSecondsToSleepWhenRecursiveCheckFailed); + u32 tid = GetTid(); + Printf("%s: CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx) (tid=%u)\n", + SanitizerToolName, StripModuleName(file), line, cond, (uptr)v1, + (uptr)v2, tid); + static atomic_uint32_t first_tid; + u32 cmp = 0; + if (!atomic_compare_exchange_strong(&first_tid, &cmp, tid, + memory_order_relaxed)) { + if (cmp == tid) { + // Recursing into CheckFailed. + } else { + // Another thread fails already, let it print the stack and terminate. + SleepForSeconds(2); + } Trap(); } - - if (CheckFailedCallback) { - CheckFailedCallback(file, line, cond, v1, v2); - } - Report("Sanitizer CHECK failed: %s:%d %s (%lld, %lld)\n", file, line, cond, - v1, v2); + if (CheckUnwindCallback) + CheckUnwindCallback(); Die(); } diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.cpp b/libsanitizer/sanitizer_common/sanitizer_thread_registry.cpp index f2c6f27..3273da3 100644 --- a/libsanitizer/sanitizer_common/sanitizer_thread_registry.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.cpp @@ -85,7 +85,7 @@ void ThreadContextBase::SetCreated(uptr _user_id, u64 _unique_id, unique_id = _unique_id; detached = _detached; // Parent tid makes no sense for the main thread. - if (tid != 0) + if (tid != kMainTid) parent_tid = _parent_tid; OnCreated(arg); } @@ -99,8 +99,6 @@ void ThreadContextBase::Reset() { // ThreadRegistry implementation. -const u32 ThreadRegistry::kUnknownTid = ~0U; - ThreadRegistry::ThreadRegistry(ThreadContextFactory factory, u32 max_threads, u32 thread_quarantine_size, u32 max_reuse) : context_factory_(factory), @@ -135,7 +133,7 @@ uptr ThreadRegistry::GetMaxAliveThreads() { u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, void *arg) { BlockingMutexLock l(&mtx_); - u32 tid = kUnknownTid; + u32 tid = kInvalidTid; ThreadContextBase *tctx = QuarantinePop(); if (tctx) { tid = tctx->tid; @@ -155,7 +153,7 @@ u32 ThreadRegistry::CreateThread(uptr user_id, bool detached, u32 parent_tid, Die(); } CHECK_NE(tctx, 0); - CHECK_NE(tid, kUnknownTid); + CHECK_NE(tid, kInvalidTid); CHECK_LT(tid, max_threads_); CHECK_EQ(tctx->status, ThreadStatusInvalid); alive_threads_++; @@ -186,7 +184,7 @@ u32 ThreadRegistry::FindThread(FindThreadCallback cb, void *arg) { if (tctx != 0 && cb(tctx, arg)) return tctx->tid; } - return kUnknownTid; + return kInvalidTid; } ThreadContextBase * @@ -278,7 +276,7 @@ void ThreadRegistry::JoinThread(u32 tid, void *arg) { // really started. We just did CreateThread for a prospective new // thread before trying to create it, and then failed to actually // create it, and so never called StartThread. -void ThreadRegistry::FinishThread(u32 tid) { +ThreadStatus ThreadRegistry::FinishThread(u32 tid) { BlockingMutexLock l(&mtx_); CHECK_GT(alive_threads_, 0); alive_threads_--; @@ -286,6 +284,7 @@ void ThreadRegistry::FinishThread(u32 tid) { ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); bool dead = tctx->detached; + ThreadStatus prev_status = tctx->status; if (tctx->status == ThreadStatusRunning) { CHECK_GT(running_threads_, 0); running_threads_--; @@ -300,6 +299,7 @@ void ThreadRegistry::FinishThread(u32 tid) { QuarantinePush(tctx); } tctx->SetDestroyed(); + return prev_status; } void ThreadRegistry::StartThread(u32 tid, tid_t os_id, ThreadType thread_type, diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h index 85c522a..dcd445c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h +++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h @@ -87,8 +87,6 @@ typedef ThreadContextBase* (*ThreadContextFactory)(u32 tid); class ThreadRegistry { public: - static const u32 kUnknownTid; - ThreadRegistry(ThreadContextFactory factory, u32 max_threads, u32 thread_quarantine_size, u32 max_reuse = 0); void GetNumberOfThreads(uptr *total = nullptr, uptr *running = nullptr, @@ -113,7 +111,7 @@ class ThreadRegistry { void RunCallbackForEachThreadLocked(ThreadCallback cb, void *arg); typedef bool (*FindThreadCallback)(ThreadContextBase *tctx, void *arg); - // Finds a thread using the provided callback. Returns kUnknownTid if no + // Finds a thread using the provided callback. Returns kInvalidTid if no // thread is found. u32 FindThread(FindThreadCallback cb, void *arg); // Should be guarded by ThreadRegistryLock. Return 0 if no thread @@ -126,7 +124,8 @@ class ThreadRegistry { void SetThreadNameByUserId(uptr user_id, const char *name); void DetachThread(u32 tid, void *arg); void JoinThread(u32 tid, void *arg); - void FinishThread(u32 tid); + // Finishes thread and returns previous status. + ThreadStatus FinishThread(u32 tid); void StartThread(u32 tid, tid_t os_id, ThreadType thread_type, void *arg); void SetThreadUserId(u32 tid, uptr user_id); diff --git a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cpp b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cpp index 10748f9..1f664b6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.cpp @@ -12,6 +12,7 @@ #include "sanitizer_tls_get_addr.h" +#include "sanitizer_atomic.h" #include "sanitizer_flags.h" #include "sanitizer_platform_interceptors.h" @@ -42,39 +43,54 @@ static atomic_uintptr_t number_of_live_dtls; static const uptr kDestroyedThread = -1; -static inline void DTLS_Deallocate(DTLS::DTV *dtv, uptr size) { - if (!size) return; - VReport(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", dtv, size); - UnmapOrDie(dtv, size * sizeof(DTLS::DTV)); +static void DTLS_Deallocate(DTLS::DTVBlock *block) { + VReport(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", block); + UnmapOrDie(block, sizeof(DTLS::DTVBlock)); atomic_fetch_sub(&number_of_live_dtls, 1, memory_order_relaxed); } -static inline void DTLS_Resize(uptr new_size) { - if (dtls.dtv_size >= new_size) return; - new_size = RoundUpToPowerOfTwo(new_size); - new_size = Max(new_size, 4096UL / sizeof(DTLS::DTV)); - DTLS::DTV *new_dtv = - (DTLS::DTV *)MmapOrDie(new_size * sizeof(DTLS::DTV), "DTLS_Resize"); +static DTLS::DTVBlock *DTLS_NextBlock(atomic_uintptr_t *cur) { + uptr v = atomic_load(cur, memory_order_acquire); + if (v == kDestroyedThread) + return nullptr; + DTLS::DTVBlock *next = (DTLS::DTVBlock *)v; + if (next) + return next; + DTLS::DTVBlock *new_dtv = + (DTLS::DTVBlock *)MmapOrDie(sizeof(DTLS::DTVBlock), "DTLS_NextBlock"); + uptr prev = 0; + if (!atomic_compare_exchange_strong(cur, &prev, (uptr)new_dtv, + memory_order_seq_cst)) { + UnmapOrDie(new_dtv, sizeof(DTLS::DTVBlock)); + return (DTLS::DTVBlock *)prev; + } uptr num_live_dtls = atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed); - VReport(2, "__tls_get_addr: DTLS_Resize %p %zd\n", &dtls, num_live_dtls); - CHECK_LT(num_live_dtls, 1 << 20); - uptr old_dtv_size = dtls.dtv_size; - DTLS::DTV *old_dtv = dtls.dtv; - if (old_dtv_size) - internal_memcpy(new_dtv, dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV)); - dtls.dtv = new_dtv; - dtls.dtv_size = new_size; - if (old_dtv_size) - DTLS_Deallocate(old_dtv, old_dtv_size); + VReport(2, "__tls_get_addr: DTLS_NextBlock %p %zd\n", &dtls, num_live_dtls); + return new_dtv; +} + +static DTLS::DTV *DTLS_Find(uptr id) { + VReport(2, "__tls_get_addr: DTLS_Find %p %zd\n", &dtls, id); + static constexpr uptr kPerBlock = ARRAY_SIZE(DTLS::DTVBlock::dtvs); + DTLS::DTVBlock *cur = DTLS_NextBlock(&dtls.dtv_block); + if (!cur) + return nullptr; + for (; id >= kPerBlock; id -= kPerBlock) cur = DTLS_NextBlock(&cur->next); + return cur->dtvs + id; } void DTLS_Destroy() { if (!common_flags()->intercept_tls_get_addr) return; - VReport(2, "__tls_get_addr: DTLS_Destroy %p %zd\n", &dtls, dtls.dtv_size); - uptr s = dtls.dtv_size; - dtls.dtv_size = kDestroyedThread; // Do this before unmap for AS-safety. - DTLS_Deallocate(dtls.dtv, s); + VReport(2, "__tls_get_addr: DTLS_Destroy %p\n", &dtls); + DTLS::DTVBlock *block = (DTLS::DTVBlock *)atomic_exchange( + &dtls.dtv_block, kDestroyedThread, memory_order_release); + while (block) { + DTLS::DTVBlock *next = + (DTLS::DTVBlock *)atomic_load(&block->next, memory_order_acquire); + DTLS_Deallocate(block); + block = next; + } } #if defined(__powerpc64__) || defined(__mips__) @@ -96,9 +112,9 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, if (!common_flags()->intercept_tls_get_addr) return 0; TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void); uptr dso_id = arg->dso_id; - if (dtls.dtv_size == kDestroyedThread) return 0; - DTLS_Resize(dso_id + 1); - if (dtls.dtv[dso_id].beg) return 0; + DTLS::DTV *dtv = DTLS_Find(dso_id); + if (!dtv || dtv->beg) + return 0; uptr tls_size = 0; uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset; VReport(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p " @@ -126,9 +142,9 @@ DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, // This may happen inside the DTOR of main thread, so just ignore it. tls_size = 0; } - dtls.dtv[dso_id].beg = tls_beg; - dtls.dtv[dso_id].size = tls_size; - return dtls.dtv + dso_id; + dtv->beg = tls_beg; + dtv->size = tls_size; + return dtv; } void DTLS_on_libc_memalign(void *ptr, uptr size) { @@ -141,7 +157,8 @@ void DTLS_on_libc_memalign(void *ptr, uptr size) { DTLS *DTLS_Get() { return &dtls; } bool DTLSInDestruction(DTLS *dtls) { - return dtls->dtv_size == kDestroyedThread; + return atomic_load(&dtls->dtv_block, memory_order_relaxed) == + kDestroyedThread; } #else diff --git a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h index c7cd5a8..a599c0b 100644 --- a/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h +++ b/libsanitizer/sanitizer_common/sanitizer_tls_get_addr.h @@ -28,6 +28,7 @@ #ifndef SANITIZER_TLS_GET_ADDR_H #define SANITIZER_TLS_GET_ADDR_H +#include "sanitizer_atomic.h" #include "sanitizer_common.h" namespace __sanitizer { @@ -38,15 +39,31 @@ struct DTLS { struct DTV { uptr beg, size; }; + struct DTVBlock { + atomic_uintptr_t next; + DTV dtvs[(4096UL - sizeof(next)) / sizeof(DTLS::DTV)]; + }; + + static_assert(sizeof(DTVBlock) <= 4096UL, "Unexpected block size"); - uptr dtv_size; - DTV *dtv; // dtv_size elements, allocated by MmapOrDie. + atomic_uintptr_t dtv_block; // Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cpp uptr last_memalign_size; uptr last_memalign_ptr; }; +template <typename Fn> +void ForEachDVT(DTLS *dtls, const Fn &fn) { + DTLS::DTVBlock *block = + (DTLS::DTVBlock *)atomic_load(&dtls->dtv_block, memory_order_acquire); + while (block) { + int id = 0; + for (auto &d : block->dtvs) fn(d, id++); + block = (DTLS::DTVBlock *)atomic_load(&block->next, memory_order_acquire); + } +} + // Returns pointer and size of a linker-allocated TLS block. // Each block is returned exactly once. DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, uptr static_tls_begin, diff --git a/libsanitizer/sanitizer_common/sanitizer_unwind_win.cpp b/libsanitizer/sanitizer_common/sanitizer_unwind_win.cpp index e2edf42..7e01c81 100644 --- a/libsanitizer/sanitizer_common/sanitizer_unwind_win.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_unwind_win.cpp @@ -43,6 +43,10 @@ void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) { trace_buffer[0] = pc; } +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wframe-larger-than=" +#endif void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { CHECK(context); CHECK_GE(max_depth, 2); @@ -74,6 +78,9 @@ void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset; } } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif #endif // #if !SANITIZER_GO #endif // SANITIZER_WINDOWS diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cpp b/libsanitizer/sanitizer_common/sanitizer_win.cpp index 85ac263..f383e13 100644 --- a/libsanitizer/sanitizer_common/sanitizer_win.cpp +++ b/libsanitizer/sanitizer_common/sanitizer_win.cpp @@ -334,8 +334,12 @@ bool MprotectNoAccess(uptr addr, uptr size) { } void ReleaseMemoryPagesToOS(uptr beg, uptr end) { - // This is almost useless on 32-bits. - // FIXME: add madvise-analog when we move to 64-bits. + uptr beg_aligned = RoundDownTo(beg, GetPageSizeCached()), + end_aligned = RoundDownTo(end, GetPageSizeCached()); + CHECK(beg < end); // make sure the region is sane + if (beg_aligned == end_aligned) // make sure we're freeing at least 1 page; + return; + UnmapOrDie((void *)beg, end_aligned - beg_aligned); } void SetShadowRegionHugePageMode(uptr addr, uptr size) { @@ -386,6 +390,12 @@ uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding, return 0; } +uptr MapDynamicShadowAndAliases(uptr shadow_size, uptr alias_size, + uptr num_aliases, uptr ring_buffer_size) { + CHECK(false && "HWASan aliasing is unimplemented on Windows"); + return 0; +} + bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { MEMORY_BASIC_INFORMATION mbi; CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi))); @@ -564,7 +574,7 @@ void Abort() { // load the image at this address. Therefore, we call it the preferred base. Any // addresses in the DWARF typically assume that the object has been loaded at // this address. -static uptr GetPreferredBase(const char *modname) { +static uptr GetPreferredBase(const char *modname, char *buf, size_t buf_size) { fd_t fd = OpenFile(modname, RdOnly, nullptr); if (fd == kInvalidFd) return 0; @@ -586,12 +596,10 @@ static uptr GetPreferredBase(const char *modname) { // IMAGE_FILE_HEADER // IMAGE_OPTIONAL_HEADER // Seek to e_lfanew and read all that data. - char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)]; if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0; - if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) || - bytes_read != sizeof(buf)) + if (!ReadFromFile(fd, buf, buf_size, &bytes_read) || bytes_read != buf_size) return 0; // Check for "PE\0\0" before the PE header. @@ -633,6 +641,10 @@ void ListOfModules::init() { } } + InternalMmapVector<char> buf(4 + sizeof(IMAGE_FILE_HEADER) + + sizeof(IMAGE_OPTIONAL_HEADER)); + InternalMmapVector<wchar_t> modname_utf16(kMaxPathLength); + InternalMmapVector<char> module_name(kMaxPathLength); // |num_modules| is the number of modules actually present, size_t num_modules = bytes_required / sizeof(HMODULE); for (size_t i = 0; i < num_modules; ++i) { @@ -642,15 +654,13 @@ void ListOfModules::init() { continue; // Get the UTF-16 path and convert to UTF-8. - wchar_t modname_utf16[kMaxPathLength]; int modname_utf16_len = - GetModuleFileNameW(handle, modname_utf16, kMaxPathLength); + GetModuleFileNameW(handle, &modname_utf16[0], kMaxPathLength); if (modname_utf16_len == 0) modname_utf16[0] = '\0'; - char module_name[kMaxPathLength]; - int module_name_len = - ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1, - &module_name[0], kMaxPathLength, NULL, NULL); + int module_name_len = ::WideCharToMultiByte( + CP_UTF8, 0, &modname_utf16[0], modname_utf16_len + 1, &module_name[0], + kMaxPathLength, NULL, NULL); module_name[module_name_len] = '\0'; uptr base_address = (uptr)mi.lpBaseOfDll; @@ -660,15 +670,16 @@ void ListOfModules::init() { // RVA when computing the module offset. This helps llvm-symbolizer find the // right DWARF CU. In the common case that the image is loaded at it's // preferred address, we will now print normal virtual addresses. - uptr preferred_base = GetPreferredBase(&module_name[0]); + uptr preferred_base = + GetPreferredBase(&module_name[0], &buf[0], buf.size()); uptr adjusted_base = base_address - preferred_base; - LoadedModule cur_module; - cur_module.set(module_name, adjusted_base); + modules_.push_back(LoadedModule()); + LoadedModule &cur_module = modules_.back(); + cur_module.set(&module_name[0], adjusted_base); // We add the whole module as one single address range. cur_module.addAddressRange(base_address, end_address, /*executable*/ true, /*writable*/ true); - modules_.push_back(cur_module); } UnmapOrDie(hmodules, modules_buffer_size); } @@ -956,22 +967,27 @@ void SignalContext::InitPcSpBp() { uptr SignalContext::GetAddress() const { EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo; - return exception_record->ExceptionInformation[1]; + if (exception_record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) + return exception_record->ExceptionInformation[1]; + return (uptr)exception_record->ExceptionAddress; } bool SignalContext::IsMemoryAccess() const { - return GetWriteFlag() != SignalContext::UNKNOWN; + return ((EXCEPTION_RECORD *)siginfo)->ExceptionCode == + EXCEPTION_ACCESS_VIOLATION; } -bool SignalContext::IsTrueFaultingAddress() const { - // FIXME: Provide real implementation for this. See Linux and Mac variants. - return IsMemoryAccess(); -} +bool SignalContext::IsTrueFaultingAddress() const { return true; } SignalContext::WriteFlag SignalContext::GetWriteFlag() const { EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo; + + // The write flag is only available for access violation exceptions. + if (exception_record->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) + return SignalContext::UNKNOWN; + // The contents of this array are documented at - // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx + // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record // The first element indicates read as 0, write as 1, or execute as 8. The // second element is the faulting address. switch (exception_record->ExceptionInformation[0]) { @@ -1037,10 +1053,24 @@ const char *SignalContext::Describe() const { } uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { - // FIXME: Actually implement this function. - CHECK_GT(buf_len, 0); - buf[0] = 0; - return 0; + if (buf_len == 0) + return 0; + + // Get the UTF-16 path and convert to UTF-8. + InternalMmapVector<wchar_t> binname_utf16(kMaxPathLength); + int binname_utf16_len = + GetModuleFileNameW(NULL, &binname_utf16[0], kMaxPathLength); + if (binname_utf16_len == 0) { + buf[0] = '\0'; + return 0; + } + int binary_name_len = + ::WideCharToMultiByte(CP_UTF8, 0, &binname_utf16[0], binname_utf16_len, + buf, buf_len, NULL, NULL); + if ((unsigned)binary_name_len == buf_len) + --binary_name_len; + buf[binary_name_len] = '\0'; + return binary_name_len; } uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { diff --git a/libsanitizer/tsan/tsan_clock.cpp b/libsanitizer/tsan/tsan_clock.cpp index c91b29c..8e51883 100644 --- a/libsanitizer/tsan/tsan_clock.cpp +++ b/libsanitizer/tsan/tsan_clock.cpp @@ -150,7 +150,7 @@ void ThreadClock::acquire(ClockCache *c, SyncClock *src) { bool acquired = false; for (unsigned i = 0; i < kDirtyTids; i++) { SyncClock::Dirty dirty = src->dirty_[i]; - unsigned tid = dirty.tid; + unsigned tid = dirty.tid(); if (tid != kInvalidTid) { if (clk_[tid] < dirty.epoch) { clk_[tid] = dirty.epoch; @@ -299,10 +299,10 @@ void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) { dst->tab_idx_ = cached_idx_; dst->size_ = cached_size_; dst->blocks_ = cached_blocks_; - CHECK_EQ(dst->dirty_[0].tid, kInvalidTid); + CHECK_EQ(dst->dirty_[0].tid(), kInvalidTid); // The cached clock is shared (immutable), // so this is where we store the current clock. - dst->dirty_[0].tid = tid_; + dst->dirty_[0].set_tid(tid_); dst->dirty_[0].epoch = clk_[tid_]; dst->release_store_tid_ = tid_; dst->release_store_reused_ = reused_; @@ -336,8 +336,7 @@ void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) { ce.reused = 0; i++; } - for (uptr i = 0; i < kDirtyTids; i++) - dst->dirty_[i].tid = kInvalidTid; + for (uptr i = 0; i < kDirtyTids; i++) dst->dirty_[i].set_tid(kInvalidTid); dst->release_store_tid_ = tid_; dst->release_store_reused_ = reused_; // Rememeber that we don't need to acquire it in future. @@ -369,10 +368,10 @@ void ThreadClock::UpdateCurrentThread(ClockCache *c, SyncClock *dst) const { // Update the threads time, but preserve 'acquired' flag. for (unsigned i = 0; i < kDirtyTids; i++) { SyncClock::Dirty *dirty = &dst->dirty_[i]; - const unsigned tid = dirty->tid; + const unsigned tid = dirty->tid(); if (tid == tid_ || tid == kInvalidTid) { CPP_STAT_INC(StatClockReleaseFast); - dirty->tid = tid_; + dirty->set_tid(tid_); dirty->epoch = clk_[tid_]; return; } @@ -393,8 +392,8 @@ bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const { return false; for (unsigned i = 0; i < kDirtyTids; i++) { SyncClock::Dirty dirty = src->dirty_[i]; - if (dirty.tid != kInvalidTid) { - if (clk_[dirty.tid] < dirty.epoch) + if (dirty.tid() != kInvalidTid) { + if (clk_[dirty.tid()] < dirty.epoch) return false; } } @@ -453,8 +452,7 @@ void SyncClock::ResetImpl() { blocks_ = 0; release_store_tid_ = kInvalidTid; release_store_reused_ = 0; - for (uptr i = 0; i < kDirtyTids; i++) - dirty_[i].tid = kInvalidTid; + for (uptr i = 0; i < kDirtyTids; i++) dirty_[i].set_tid(kInvalidTid); } void SyncClock::Resize(ClockCache *c, uptr nclk) { @@ -503,10 +501,10 @@ void SyncClock::Resize(ClockCache *c, uptr nclk) { void SyncClock::FlushDirty() { for (unsigned i = 0; i < kDirtyTids; i++) { Dirty *dirty = &dirty_[i]; - if (dirty->tid != kInvalidTid) { - CHECK_LT(dirty->tid, size_); - elem(dirty->tid).epoch = dirty->epoch; - dirty->tid = kInvalidTid; + if (dirty->tid() != kInvalidTid) { + CHECK_LT(dirty->tid(), size_); + elem(dirty->tid()).epoch = dirty->epoch; + dirty->set_tid(kInvalidTid); } } } @@ -559,7 +557,7 @@ ALWAYS_INLINE bool SyncClock::Cachable() const { if (size_ == 0) return false; for (unsigned i = 0; i < kDirtyTids; i++) { - if (dirty_[i].tid != kInvalidTid) + if (dirty_[i].tid() != kInvalidTid) return false; } return atomic_load_relaxed(ref_ptr(tab_)) == 1; @@ -606,7 +604,7 @@ ALWAYS_INLINE void SyncClock::append_block(u32 idx) { u64 SyncClock::get(unsigned tid) const { for (unsigned i = 0; i < kDirtyTids; i++) { Dirty dirty = dirty_[i]; - if (dirty.tid == tid) + if (dirty.tid() == tid) return dirty.epoch; } return elem(tid).epoch; @@ -625,9 +623,8 @@ void SyncClock::DebugDump(int(*printf)(const char *s, ...)) { for (uptr i = 0; i < size_; i++) printf("%s%llu", i == 0 ? "" : ",", elem(i).reused); printf("] release_store_tid=%d/%d dirty_tids=%d[%llu]/%d[%llu]", - release_store_tid_, release_store_reused_, - dirty_[0].tid, dirty_[0].epoch, - dirty_[1].tid, dirty_[1].epoch); + release_store_tid_, release_store_reused_, dirty_[0].tid(), + dirty_[0].epoch, dirty_[1].tid(), dirty_[1].epoch); } void SyncClock::Iter::Next() { diff --git a/libsanitizer/tsan/tsan_clock.h b/libsanitizer/tsan/tsan_clock.h index 736cdae..31376a1 100644 --- a/libsanitizer/tsan/tsan_clock.h +++ b/libsanitizer/tsan/tsan_clock.h @@ -17,7 +17,7 @@ namespace __tsan { -typedef DenseSlabAlloc<ClockBlock, 1<<16, 1<<10> ClockAlloc; +typedef DenseSlabAlloc<ClockBlock, 1 << 22, 1 << 10> ClockAlloc; typedef DenseSlabAllocCache ClockCache; // The clock that lives in sync variables (mutexes, atomics, etc). @@ -65,10 +65,20 @@ class SyncClock { static const uptr kDirtyTids = 2; struct Dirty { - u64 epoch : kClkBits; - u64 tid : 64 - kClkBits; // kInvalidId if not active + u32 tid() const { return tid_ == kShortInvalidTid ? kInvalidTid : tid_; } + void set_tid(u32 tid) { + tid_ = tid == kInvalidTid ? kShortInvalidTid : tid; + } + u64 epoch : kClkBits; + + private: + // Full kInvalidTid won't fit into Dirty::tid. + static const u64 kShortInvalidTid = (1ull << (64 - kClkBits)) - 1; + u64 tid_ : 64 - kClkBits; // kInvalidId if not active }; + static_assert(sizeof(Dirty) == 8, "Dirty is not 64bit"); + unsigned release_store_tid_; unsigned release_store_reused_; Dirty dirty_[kDirtyTids]; diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h index 293d7de..f53787a 100644 --- a/libsanitizer/tsan/tsan_defs.h +++ b/libsanitizer/tsan/tsan_defs.h @@ -98,8 +98,6 @@ const bool kCollectHistory = false; const bool kCollectHistory = true; #endif -const u16 kInvalidTid = kMaxTid + 1; - // The following "build consistency" machinery ensures that all source files // are built in the same configuration. Inconsistent builds lead to // hard to debug crashes. diff --git a/libsanitizer/tsan/tsan_dense_alloc.h b/libsanitizer/tsan/tsan_dense_alloc.h index 64fc50e..6c89e40 100644 --- a/libsanitizer/tsan/tsan_dense_alloc.h +++ b/libsanitizer/tsan/tsan_dense_alloc.h @@ -29,28 +29,40 @@ class DenseSlabAllocCache { typedef u32 IndexT; uptr pos; IndexT cache[kSize]; - template<typename T, uptr kL1Size, uptr kL2Size> friend class DenseSlabAlloc; + template <typename, uptr, uptr, u64> + friend class DenseSlabAlloc; }; -template<typename T, uptr kL1Size, uptr kL2Size> +template <typename T, uptr kL1Size, uptr kL2Size, u64 kReserved = 0> class DenseSlabAlloc { public: typedef DenseSlabAllocCache Cache; typedef typename Cache::IndexT IndexT; - explicit DenseSlabAlloc(const char *name) { - // Check that kL1Size and kL2Size are sane. - CHECK_EQ(kL1Size & (kL1Size - 1), 0); - CHECK_EQ(kL2Size & (kL2Size - 1), 0); - CHECK_GE(1ull << (sizeof(IndexT) * 8), kL1Size * kL2Size); - // Check that it makes sense to use the dense alloc. - CHECK_GE(sizeof(T), sizeof(IndexT)); - internal_memset(map_, 0, sizeof(map_)); + static_assert((kL1Size & (kL1Size - 1)) == 0, + "kL1Size must be a power-of-two"); + static_assert((kL2Size & (kL2Size - 1)) == 0, + "kL2Size must be a power-of-two"); + static_assert((kL1Size * kL2Size) <= (1ull << (sizeof(IndexT) * 8)), + "kL1Size/kL2Size are too large"); + static_assert(((kL1Size * kL2Size - 1) & kReserved) == 0, + "reserved bits don't fit"); + static_assert(sizeof(T) > sizeof(IndexT), + "it doesn't make sense to use dense alloc"); + + explicit DenseSlabAlloc(LinkerInitialized, const char *name) { freelist_ = 0; fillpos_ = 0; name_ = name; } + explicit DenseSlabAlloc(const char *name) + : DenseSlabAlloc(LINKER_INITIALIZED, name) { + // It can be very large. + // Don't page it in for linker initialized objects. + internal_memset(map_, 0, sizeof(map_)); + } + ~DenseSlabAlloc() { for (uptr i = 0; i < kL1Size; i++) { if (map_[i] != 0) diff --git a/libsanitizer/tsan/tsan_external.cpp b/libsanitizer/tsan/tsan_external.cpp index 466b2bf..a87e12f 100644 --- a/libsanitizer/tsan/tsan_external.cpp +++ b/libsanitizer/tsan/tsan_external.cpp @@ -111,12 +111,12 @@ void __tsan_external_assign_tag(void *addr, void *tag) { SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_read(void *addr, void *caller_pc, void *tag) { - ExternalAccess(addr, STRIP_PC(caller_pc), tag, MemoryRead); + ExternalAccess(addr, STRIP_PAC_PC(caller_pc), tag, MemoryRead); } SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_write(void *addr, void *caller_pc, void *tag) { - ExternalAccess(addr, STRIP_PC(caller_pc), tag, MemoryWrite); + ExternalAccess(addr, STRIP_PAC_PC(caller_pc), tag, MemoryWrite); } } // extern "C" diff --git a/libsanitizer/tsan/tsan_interceptors_mac.cpp b/libsanitizer/tsan/tsan_interceptors_mac.cpp index aa29536..ed10fcc 100644 --- a/libsanitizer/tsan/tsan_interceptors_mac.cpp +++ b/libsanitizer/tsan/tsan_interceptors_mac.cpp @@ -438,6 +438,7 @@ struct fake_shared_weak_count { virtual void on_zero_shared() = 0; virtual void _unused_0x18() = 0; virtual void on_zero_shared_weak() = 0; + virtual ~fake_shared_weak_count() = 0; // suppress -Wnon-virtual-dtor }; } // namespace diff --git a/libsanitizer/tsan/tsan_interceptors_posix.cpp b/libsanitizer/tsan/tsan_interceptors_posix.cpp index aa04d8d..2651e22 100644 --- a/libsanitizer/tsan/tsan_interceptors_posix.cpp +++ b/libsanitizer/tsan/tsan_interceptors_posix.cpp @@ -54,10 +54,6 @@ using namespace __tsan; #define vfork __vfork14 #endif -#if SANITIZER_ANDROID -#define mallopt(a, b) -#endif - #ifdef __mips__ const int kSigCount = 129; #else @@ -85,6 +81,8 @@ extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); +extern "C" int pthread_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void)); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *) @@ -97,7 +95,7 @@ extern "C" void _exit(int status); extern "C" int fileno_unlocked(void *stream); extern "C" int dirfd(void *dirp); #endif -#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_NETBSD +#if SANITIZER_GLIBC extern "C" int mallopt(int param, int value); #endif #if SANITIZER_NETBSD @@ -659,8 +657,11 @@ TSAN_INTERCEPTOR(void*, malloc, uptr size) { return p; } +// In glibc<2.25, dynamic TLS blocks are allocated by __libc_memalign. Intercept +// __libc_memalign so that (1) we can detect races (2) free will not be called +// on libc internally allocated blocks. TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz); + SCOPED_INTERCEPTOR_RAW(__libc_memalign, align, sz); return user_memalign(thr, pc, align, sz); } @@ -773,6 +774,11 @@ static void *mmap_interceptor(ThreadState *thr, uptr pc, Mmap real_mmap, if (!fix_mmap_addr(&addr, sz, flags)) return MAP_FAILED; void *res = real_mmap(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { + if (!IsAppMem((uptr)res) || !IsAppMem((uptr)res + sz - 1)) { + Report("ThreadSanitizer: mmap at bad address: addr=%p size=%p res=%p\n", + addr, (void*)sz, res); + Die(); + } if (fd > 0) FdAccess(thr, pc, fd); MemoryRangeImitateWriteOrResetRange(thr, pc, (uptr)res, sz); } @@ -1122,27 +1128,37 @@ static void *init_cond(void *c, bool force = false) { return (void*)cond; } +namespace { + +template <class Fn> struct CondMutexUnlockCtx { ScopedInterceptor *si; ThreadState *thr; uptr pc; void *m; + void *c; + const Fn &fn; + + int Cancel() const { return fn(); } + void Unlock() const; }; -static void cond_mutex_unlock(CondMutexUnlockCtx *arg) { +template <class Fn> +void CondMutexUnlockCtx<Fn>::Unlock() const { // pthread_cond_wait interceptor has enabled async signal delivery // (see BlockingCall below). Disable async signals since we are running // tsan code. Also ScopedInterceptor and BlockingCall destructors won't run // since the thread is cancelled, so we have to manually execute them // (the thread still can run some user code due to pthread_cleanup_push). - ThreadSignalContext *ctx = SigCtx(arg->thr); + ThreadSignalContext *ctx = SigCtx(thr); CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1); atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed); - MutexPostLock(arg->thr, arg->pc, (uptr)arg->m, MutexFlagDoPreLockOnPostLock); + MutexPostLock(thr, pc, (uptr)m, MutexFlagDoPreLockOnPostLock); // Undo BlockingCall ctor effects. - arg->thr->ignore_interceptors--; - arg->si->~ScopedInterceptor(); + thr->ignore_interceptors--; + si->~ScopedInterceptor(); } +} // namespace INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { void *cond = init_cond(c, true); @@ -1151,20 +1167,24 @@ INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { return REAL(pthread_cond_init)(cond, a); } -static int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si, - int (*fn)(void *c, void *m, void *abstime), void *c, - void *m, void *t) { +template <class Fn> +int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si, const Fn &fn, + void *c, void *m) { MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false); MutexUnlock(thr, pc, (uptr)m); - CondMutexUnlockCtx arg = {si, thr, pc, m}; int res = 0; // This ensures that we handle mutex lock even in case of pthread_cancel. // See test/tsan/cond_cancel.cpp. { // Enable signal delivery while the thread is blocked. BlockingCall bc(thr); + CondMutexUnlockCtx<Fn> arg = {si, thr, pc, m, c, fn}; res = call_pthread_cancel_with_cleanup( - fn, c, m, t, (void (*)(void *arg))cond_mutex_unlock, &arg); + [](void *arg) -> int { + return ((const CondMutexUnlockCtx<Fn> *)arg)->Cancel(); + }, + [](void *arg) { ((const CondMutexUnlockCtx<Fn> *)arg)->Unlock(); }, + &arg); } if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); MutexPostLock(thr, pc, (uptr)m, MutexFlagDoPreLockOnPostLock); @@ -1174,25 +1194,46 @@ static int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si, INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m); - return cond_wait(thr, pc, &si, (int (*)(void *c, void *m, void *abstime))REAL( - pthread_cond_wait), - cond, m, 0); + return cond_wait( + thr, pc, &si, [=]() { return REAL(pthread_cond_wait)(cond, m); }, cond, + m); } INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime); - return cond_wait(thr, pc, &si, REAL(pthread_cond_timedwait), cond, m, - abstime); + return cond_wait( + thr, pc, &si, + [=]() { return REAL(pthread_cond_timedwait)(cond, m, abstime); }, cond, + m); } +#if SANITIZER_LINUX +INTERCEPTOR(int, pthread_cond_clockwait, void *c, void *m, + __sanitizer_clockid_t clock, void *abstime) { + void *cond = init_cond(c); + SCOPED_TSAN_INTERCEPTOR(pthread_cond_clockwait, cond, m, clock, abstime); + return cond_wait( + thr, pc, &si, + [=]() { return REAL(pthread_cond_clockwait)(cond, m, clock, abstime); }, + cond, m); +} +#define TSAN_MAYBE_PTHREAD_COND_CLOCKWAIT TSAN_INTERCEPT(pthread_cond_clockwait) +#else +#define TSAN_MAYBE_PTHREAD_COND_CLOCKWAIT +#endif + #if SANITIZER_MAC INTERCEPTOR(int, pthread_cond_timedwait_relative_np, void *c, void *m, void *reltime) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait_relative_np, cond, m, reltime); - return cond_wait(thr, pc, &si, REAL(pthread_cond_timedwait_relative_np), cond, - m, reltime); + return cond_wait( + thr, pc, &si, + [=]() { + return REAL(pthread_cond_timedwait_relative_np)(cond, m, reltime); + }, + cond, m); } #endif @@ -1937,7 +1978,8 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, // because in async signal processing case (when handler is called directly // from rtl_generic_sighandler) we have not yet received the reraised // signal; and it looks too fragile to intercept all ways to reraise a signal. - if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) { + if (ShouldReport(thr, ReportTypeErrnoInSignal) && !sync && sig != SIGTERM && + errno != 99) { VarSizeStackTrace stack; // StackTrace::GetNestInstructionPc(pc) is used because return address is // expected, OutputReport() will undo this. @@ -2107,26 +2149,32 @@ TSAN_INTERCEPTOR(int, fork, int fake) { if (in_symbolizer()) return REAL(fork)(fake); SCOPED_INTERCEPTOR_RAW(fork, fake); + return REAL(fork)(fake); +} + +void atfork_prepare() { + if (in_symbolizer()) + return; + ThreadState *thr = cur_thread(); + const uptr pc = StackTrace::GetCurrentPc(); ForkBefore(thr, pc); - int pid; - { - // On OS X, REAL(fork) can call intercepted functions (OSSpinLockLock), and - // we'll assert in CheckNoLocks() unless we ignore interceptors. - ScopedIgnoreInterceptors ignore; - pid = REAL(fork)(fake); - } - if (pid == 0) { - // child - ForkChildAfter(thr, pc); - FdOnFork(thr, pc); - } else if (pid > 0) { - // parent - ForkParentAfter(thr, pc); - } else { - // error - ForkParentAfter(thr, pc); - } - return pid; +} + +void atfork_parent() { + if (in_symbolizer()) + return; + ThreadState *thr = cur_thread(); + const uptr pc = StackTrace::GetCurrentPc(); + ForkParentAfter(thr, pc); +} + +void atfork_child() { + if (in_symbolizer()) + return; + ThreadState *thr = cur_thread(); + const uptr pc = StackTrace::GetCurrentPc(); + ForkChildAfter(thr, pc); + FdOnFork(thr, pc); } TSAN_INTERCEPTOR(int, vfork, int fake) { @@ -2479,13 +2527,10 @@ static USED void syscall_fd_release(uptr pc, int fd) { FdRelease(thr, pc, fd); } -static void syscall_pre_fork(uptr pc) { - TSAN_SYSCALL(); - ForkBefore(thr, pc); -} +static void syscall_pre_fork(uptr pc) { ForkBefore(cur_thread(), pc); } static void syscall_post_fork(uptr pc, int pid) { - TSAN_SYSCALL(); + ThreadState *thr = cur_thread(); if (pid == 0) { // child ForkChildAfter(thr, pc); @@ -2635,7 +2680,7 @@ void InitializeInterceptors() { #endif // Instruct libc malloc to consume less memory. -#if SANITIZER_LINUX +#if SANITIZER_GLIBC mallopt(1, 0); // M_MXFAST mallopt(-3, 32*1024); // M_MMAP_THRESHOLD #endif @@ -2698,6 +2743,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT_VER(pthread_cond_timedwait, PTHREAD_ABI_BASE); TSAN_INTERCEPT_VER(pthread_cond_destroy, PTHREAD_ABI_BASE); + TSAN_MAYBE_PTHREAD_COND_CLOCKWAIT; + TSAN_INTERCEPT(pthread_mutex_init); TSAN_INTERCEPT(pthread_mutex_destroy); TSAN_INTERCEPT(pthread_mutex_trylock); @@ -2799,6 +2846,10 @@ void InitializeInterceptors() { Printf("ThreadSanitizer: failed to setup atexit callback\n"); Die(); } + if (pthread_atfork(atfork_prepare, atfork_parent, atfork_child)) { + Printf("ThreadSanitizer: failed to setup atfork callbacks\n"); + Die(); + } #if !SANITIZER_MAC && !SANITIZER_NETBSD && !SANITIZER_FREEBSD if (pthread_key_create(&interceptor_ctx()->finalize_key, &thread_finalize)) { diff --git a/libsanitizer/tsan/tsan_interface.cpp b/libsanitizer/tsan/tsan_interface.cpp index 55f1c98..9bd0e85 100644 --- a/libsanitizer/tsan/tsan_interface.cpp +++ b/libsanitizer/tsan/tsan_interface.cpp @@ -40,13 +40,13 @@ void __tsan_write16(void *addr) { } void __tsan_read16_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr + 8, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr + 8, kSizeLog8); } void __tsan_write16_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr + 8, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr + 8, kSizeLog8); } // __tsan_unaligned_read/write calls are emitted by compiler. diff --git a/libsanitizer/tsan/tsan_interface.h b/libsanitizer/tsan/tsan_interface.h index 6d7286c..6e022b5 100644 --- a/libsanitizer/tsan/tsan_interface.h +++ b/libsanitizer/tsan/tsan_interface.h @@ -204,7 +204,7 @@ __extension__ typedef __int128 a128; #endif // Part of ABI, do not change. -// https://github.com/llvm/llvm-project/blob/master/libcxx/include/atomic +// https://github.com/llvm/llvm-project/blob/main/libcxx/include/atomic typedef enum { mo_relaxed, mo_consume, @@ -415,6 +415,13 @@ void __tsan_go_atomic32_compare_exchange(ThreadState *thr, uptr cpc, uptr pc, SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a); + +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_on_initialize(); + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_on_finalize(int failed); + } // extern "C" } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_interface_inl.h b/libsanitizer/tsan/tsan_interface_inl.h index f5d743c..5e77d4d 100644 --- a/libsanitizer/tsan/tsan_interface_inl.h +++ b/libsanitizer/tsan/tsan_interface_inl.h @@ -51,35 +51,35 @@ void __tsan_write8(void *addr) { } void __tsan_read1_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog1); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog1); } void __tsan_read2_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog2); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog2); } void __tsan_read4_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog4); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog4); } void __tsan_read8_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8); } void __tsan_write1_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog1); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog1); } void __tsan_write2_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog2); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog2); } void __tsan_write4_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog4); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog4); } void __tsan_write8_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8); } void __tsan_vptr_update(void **vptr_p, void *new_val) { @@ -101,7 +101,7 @@ void __tsan_vptr_read(void **vptr_p) { } void __tsan_func_entry(void *pc) { - FuncEntry(cur_thread(), STRIP_PC(pc)); + FuncEntry(cur_thread(), STRIP_PAC_PC(pc)); } void __tsan_func_exit() { @@ -125,9 +125,9 @@ void __tsan_write_range(void *addr, uptr size) { } void __tsan_read_range_pc(void *addr, uptr size, void *pc) { - MemoryAccessRange(cur_thread(), STRIP_PC(pc), (uptr)addr, size, false); + MemoryAccessRange(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, size, false); } void __tsan_write_range_pc(void *addr, uptr size, void *pc) { - MemoryAccessRange(cur_thread(), STRIP_PC(pc), (uptr)addr, size, true); + MemoryAccessRange(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, size, true); } diff --git a/libsanitizer/tsan/tsan_mman.cpp b/libsanitizer/tsan/tsan_mman.cpp index 743e67bf..45a39f0 100644 --- a/libsanitizer/tsan/tsan_mman.cpp +++ b/libsanitizer/tsan/tsan_mman.cpp @@ -145,7 +145,7 @@ void AllocatorPrintStats() { static void SignalUnsafeCall(ThreadState *thr, uptr pc) { if (atomic_load_relaxed(&thr->in_signal_handler) == 0 || - !flags()->report_signal_unsafe) + !ShouldReport(thr, ReportTypeSignalUnsafe)) return; VarSizeStackTrace stack; ObtainCurrentStack(thr, pc, &stack); diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h index 16169ca..101522d 100644 --- a/libsanitizer/tsan/tsan_platform.h +++ b/libsanitizer/tsan/tsan_platform.h @@ -23,9 +23,21 @@ namespace __tsan { +#if defined(__x86_64__) +#define HAS_48_BIT_ADDRESS_SPACE 1 +#elif SANITIZER_IOSSIM // arm64 iOS simulators (order of #if matters) +#define HAS_48_BIT_ADDRESS_SPACE 1 +#elif SANITIZER_IOS // arm64 iOS devices (order of #if matters) +#define HAS_48_BIT_ADDRESS_SPACE 0 +#elif SANITIZER_MAC // arm64 macOS (order of #if matters) +#define HAS_48_BIT_ADDRESS_SPACE 1 +#else +#define HAS_48_BIT_ADDRESS_SPACE 0 +#endif + #if !SANITIZER_GO -#if defined(__x86_64__) +#if HAS_48_BIT_ADDRESS_SPACE /* C/C++ on linux/x86_64 and freebsd/x86_64 0000 0000 1000 - 0080 0000 0000: main binary and/or MAP_32BIT mappings (512GB) @@ -93,7 +105,7 @@ fe00 0000 00 - ff00 0000 00: heap (4 GB) ff00 0000 00 - ff80 0000 00: - (2 GB) ff80 0000 00 - ffff ffff ff: modules and main thread stack (<2 GB) */ -struct Mapping { +struct Mapping40 { static const uptr kMetaShadowBeg = 0x4000000000ull; static const uptr kMetaShadowEnd = 0x5000000000ull; static const uptr kTraceMemBeg = 0xb000000000ull; @@ -114,6 +126,7 @@ struct Mapping { }; #define TSAN_MID_APP_RANGE 1 +#define TSAN_RUNTIME_VMA 1 #elif defined(__aarch64__) && defined(__APPLE__) /* C/C++ on Darwin/iOS/ARM64 (36-bit VMA, 64 GB VM) @@ -146,7 +159,7 @@ struct Mapping { static const uptr kVdsoBeg = 0x7000000000000000ull; }; -#elif defined(__aarch64__) +#elif defined(__aarch64__) && !defined(__APPLE__) // AArch64 supports multiple VMA which leads to multiple address transformation // functions. To support these multiple VMAS transformations and mappings TSAN // runtime for AArch64 uses an external memory read (vmaSize) to select which @@ -354,7 +367,7 @@ struct Mapping47 { #define TSAN_RUNTIME_VMA 1 #endif -#elif SANITIZER_GO && !SANITIZER_WINDOWS && defined(__x86_64__) +#elif SANITIZER_GO && !SANITIZER_WINDOWS && HAS_48_BIT_ADDRESS_SPACE /* Go on linux, darwin and freebsd on x86_64 0000 0000 1000 - 0000 1000 0000: executable @@ -502,7 +515,7 @@ Go on linux/mips64 (47-bit VMA) 6000 0000 0000 - 6200 0000 0000: traces 6200 0000 0000 - 8000 0000 0000: - */ -struct Mapping { +struct Mapping47 { static const uptr kMetaShadowBeg = 0x300000000000ull; static const uptr kMetaShadowEnd = 0x400000000000ull; static const uptr kTraceMemBeg = 0x600000000000ull; @@ -512,6 +525,9 @@ struct Mapping { static const uptr kAppMemBeg = 0x000000001000ull; static const uptr kAppMemEnd = 0x00e000000000ull; }; + +#define TSAN_RUNTIME_VMA 1 + #else # error "Unknown platform" #endif @@ -592,6 +608,16 @@ uptr MappingArchImpl(void) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return MappingImpl<Mapping40, Type>(); +#else + case 47: return MappingImpl<Mapping47, Type>(); +#endif + } + DCHECK(0); + return 0; #else return MappingImpl<Mapping, Type>(); #endif @@ -749,6 +775,16 @@ bool IsAppMem(uptr mem) { } DCHECK(0); return false; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return IsAppMemImpl<Mapping40>(mem); +#else + case 47: return IsAppMemImpl<Mapping47>(mem); +#endif + } + DCHECK(0); + return false; #else return IsAppMemImpl<Mapping>(mem); #endif @@ -780,6 +816,16 @@ bool IsShadowMem(uptr mem) { } DCHECK(0); return false; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return IsShadowMemImpl<Mapping40>(mem); +#else + case 47: return IsShadowMemImpl<Mapping47>(mem); +#endif + } + DCHECK(0); + return false; #else return IsShadowMemImpl<Mapping>(mem); #endif @@ -811,6 +857,16 @@ bool IsMetaMem(uptr mem) { } DCHECK(0); return false; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return IsMetaMemImpl<Mapping40>(mem); +#else + case 47: return IsMetaMemImpl<Mapping47>(mem); +#endif + } + DCHECK(0); + return false; #else return IsMetaMemImpl<Mapping>(mem); #endif @@ -852,6 +908,16 @@ uptr MemToShadow(uptr x) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return MemToShadowImpl<Mapping40>(x); +#else + case 47: return MemToShadowImpl<Mapping47>(x); +#endif + } + DCHECK(0); + return 0; #else return MemToShadowImpl<Mapping>(x); #endif @@ -895,6 +961,16 @@ u32 *MemToMeta(uptr x) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return MemToMetaImpl<Mapping40>(x); +#else + case 47: return MemToMetaImpl<Mapping47>(x); +#endif + } + DCHECK(0); + return 0; #else return MemToMetaImpl<Mapping>(x); #endif @@ -951,6 +1027,16 @@ uptr ShadowToMem(uptr s) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return ShadowToMemImpl<Mapping40>(s); +#else + case 47: return ShadowToMemImpl<Mapping47>(s); +#endif + } + DCHECK(0); + return 0; #else return ShadowToMemImpl<Mapping>(s); #endif @@ -990,6 +1076,16 @@ uptr GetThreadTrace(int tid) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return GetThreadTraceImpl<Mapping40>(tid); +#else + case 47: return GetThreadTraceImpl<Mapping47>(tid); +#endif + } + DCHECK(0); + return 0; #else return GetThreadTraceImpl<Mapping>(tid); #endif @@ -1024,6 +1120,16 @@ uptr GetThreadTraceHeader(int tid) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return GetThreadTraceHeaderImpl<Mapping40>(tid); +#else + case 47: return GetThreadTraceHeaderImpl<Mapping47>(tid); +#endif + } + DCHECK(0); + return 0; #else return GetThreadTraceHeaderImpl<Mapping>(tid); #endif @@ -1040,9 +1146,8 @@ int ExtractRecvmsgFDs(void *msg, int *fds, int nfd); uptr ExtractLongJmpSp(uptr *env); void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size); -int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, - void *abstime), void *c, void *m, void *abstime, - void(*cleanup)(void *arg), void *arg); +int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), + void (*cleanup)(void *arg), void *arg); void DestroyThreadState(); void PlatformCleanUpThreadState(ThreadState *thr); diff --git a/libsanitizer/tsan/tsan_platform_linux.cpp b/libsanitizer/tsan/tsan_platform_linux.cpp index d136dcb..e5b6690 100644 --- a/libsanitizer/tsan/tsan_platform_linux.cpp +++ b/libsanitizer/tsan/tsan_platform_linux.cpp @@ -250,6 +250,20 @@ void InitializePlatformEarly() { Die(); } # endif +#elif defined(__mips64) +# if !SANITIZER_GO + if (vmaSize != 40) { + Printf("FATAL: ThreadSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %zd - Supported 40\n", vmaSize); + Die(); + } +# else + if (vmaSize != 47) { + Printf("FATAL: ThreadSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %zd - Supported 47\n", vmaSize); + Die(); + } +# endif #endif #endif } @@ -443,14 +457,13 @@ void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) { // Note: this function runs with async signals enabled, // so it must not touch any tsan state. -int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, - void *abstime), void *c, void *m, void *abstime, - void(*cleanup)(void *arg), void *arg) { +int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), + void (*cleanup)(void *arg), void *arg) { // pthread_cleanup_push/pop are hardcore macros mess. // We can't intercept nor call them w/o including pthread.h. int res; pthread_cleanup_push(cleanup, arg); - res = fn(c, m, abstime); + res = fn(arg); pthread_cleanup_pop(0); return res; } @@ -484,7 +497,7 @@ ThreadState *cur_thread() { dead_thread_state->fast_state.SetIgnoreBit(); dead_thread_state->ignore_interceptors = 1; dead_thread_state->is_dead = true; - *const_cast<int*>(&dead_thread_state->tid) = -1; + *const_cast<u32*>(&dead_thread_state->tid) = -1; CHECK_EQ(0, internal_mprotect(dead_thread_state, sizeof(ThreadState), PROT_READ)); } diff --git a/libsanitizer/tsan/tsan_platform_mac.cpp b/libsanitizer/tsan/tsan_platform_mac.cpp index ec2c5fb..d9719a1 100644 --- a/libsanitizer/tsan/tsan_platform_mac.cpp +++ b/libsanitizer/tsan/tsan_platform_mac.cpp @@ -234,7 +234,7 @@ static void my_pthread_introspection_hook(unsigned int event, pthread_t thread, #endif void InitializePlatformEarly() { -#if !SANITIZER_GO && defined(__aarch64__) +#if !SANITIZER_GO && !HAS_48_BIT_ADDRESS_SPACE uptr max_vm = GetMaxUserVirtualAddress() + 1; if (max_vm != Mapping::kHiAppMemEnd) { Printf("ThreadSanitizer: unsupported vm address limit %p, expected %p.\n", @@ -306,14 +306,13 @@ void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) { #if !SANITIZER_GO // Note: this function runs with async signals enabled, // so it must not touch any tsan state. -int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, - void *abstime), void *c, void *m, void *abstime, - void(*cleanup)(void *arg), void *arg) { +int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), + void (*cleanup)(void *arg), void *arg) { // pthread_cleanup_push/pop are hardcore macros mess. // We can't intercept nor call them w/o including pthread.h. int res; pthread_cleanup_push(cleanup, arg); - res = fn(c, m, abstime); + res = fn(arg); pthread_cleanup_pop(0); return res; } diff --git a/libsanitizer/tsan/tsan_platform_posix.cpp b/libsanitizer/tsan/tsan_platform_posix.cpp index d56b6c3..73e1d45 100644 --- a/libsanitizer/tsan/tsan_platform_posix.cpp +++ b/libsanitizer/tsan/tsan_platform_posix.cpp @@ -99,7 +99,7 @@ void CheckAndProtect() { Die(); } -#if defined(__aarch64__) && defined(__APPLE__) +#if defined(__aarch64__) && defined(__APPLE__) && !HAS_48_BIT_ADDRESS_SPACE ProtectRange(HeapMemEnd(), ShadowBeg()); ProtectRange(ShadowEnd(), MetaShadowBeg()); ProtectRange(MetaShadowEnd(), TraceMemBeg()); diff --git a/libsanitizer/tsan/tsan_report.cpp b/libsanitizer/tsan/tsan_report.cpp index 968c7b9..8ef9f0c 100644 --- a/libsanitizer/tsan/tsan_report.cpp +++ b/libsanitizer/tsan/tsan_report.cpp @@ -69,7 +69,7 @@ ReportDesc::~ReportDesc() { const int kThreadBufSize = 32; const char *thread_name(char *buf, int tid) { - if (tid == 0) + if (tid == kMainTid) return "main thread"; internal_snprintf(buf, kThreadBufSize, "thread T%d", tid); return buf; @@ -127,7 +127,7 @@ void PrintStack(const ReportStack *ent) { } SymbolizedStack *frame = ent->frames; for (int i = 0; frame && frame->info.address; frame = frame->next, i++) { - InternalScopedString res(2 * GetPageSizeCached()); + InternalScopedString res; RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info.address, &frame->info, common_flags()->symbolize_vs_style, @@ -250,7 +250,7 @@ static void PrintMutex(const ReportMutex *rm) { static void PrintThread(const ReportThread *rt) { Decorator d; - if (rt->id == 0) // Little sense in describing the main thread. + if (rt->id == kMainTid) // Little sense in describing the main thread. return; Printf("%s", d.ThreadDescription()); Printf(" Thread T%d", rt->id); @@ -394,7 +394,7 @@ void PrintReport(const ReportDesc *rep) { #else // #if !SANITIZER_GO -const int kMainThreadId = 1; +const u32 kMainGoroutineId = 1; void PrintStack(const ReportStack *ent) { if (ent == 0 || ent->frames == 0) { @@ -415,7 +415,7 @@ static void PrintMop(const ReportMop *mop, bool first) { Printf("%s at %p by ", (first ? (mop->write ? "Write" : "Read") : (mop->write ? "Previous write" : "Previous read")), mop->addr); - if (mop->tid == kMainThreadId) + if (mop->tid == kMainGoroutineId) Printf("main goroutine:\n"); else Printf("goroutine %d:\n", mop->tid); @@ -428,7 +428,7 @@ static void PrintLocation(const ReportLocation *loc) { Printf("\n"); Printf("Heap block of size %zu at %p allocated by ", loc->heap_chunk_size, loc->heap_chunk_start); - if (loc->tid == kMainThreadId) + if (loc->tid == kMainGoroutineId) Printf("main goroutine:\n"); else Printf("goroutine %d:\n", loc->tid); @@ -448,7 +448,7 @@ static void PrintLocation(const ReportLocation *loc) { } static void PrintThread(const ReportThread *rt) { - if (rt->id == kMainThreadId) + if (rt->id == kMainGoroutineId) return; Printf("\n"); Printf("Goroutine %d (%s) created at:\n", diff --git a/libsanitizer/tsan/tsan_rtl.cpp b/libsanitizer/tsan/tsan_rtl.cpp index 3d721eb..0efa997 100644 --- a/libsanitizer/tsan/tsan_rtl.cpp +++ b/libsanitizer/tsan/tsan_rtl.cpp @@ -11,17 +11,19 @@ // Main file (entry points) for the TSan run-time. //===----------------------------------------------------------------------===// +#include "tsan_rtl.h" + #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_file.h" #include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "tsan_defs.h" -#include "tsan_platform.h" -#include "tsan_rtl.h" +#include "tsan_interface.h" #include "tsan_mman.h" +#include "tsan_platform.h" #include "tsan_suppressions.h" #include "tsan_symbolize.h" #include "ubsan/ubsan_init.h" @@ -56,12 +58,23 @@ Context *ctx; bool OnFinalize(bool failed); void OnInitialize(); #else +#include <dlfcn.h> SANITIZER_WEAK_CXX_DEFAULT_IMPL bool OnFinalize(bool failed) { +#if !SANITIZER_GO + if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_finalize")) + return reinterpret_cast<decltype(&__tsan_on_finalize)>(ptr)(failed); +#endif return failed; } SANITIZER_WEAK_CXX_DEFAULT_IMPL -void OnInitialize() {} +void OnInitialize() { +#if !SANITIZER_GO + if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_initialize")) { + return reinterpret_cast<decltype(&__tsan_on_initialize)>(ptr)(); + } +#endif +} #endif static char thread_registry_placeholder[sizeof(ThreadRegistry)]; @@ -77,12 +90,19 @@ static ThreadContextBase *CreateThreadContext(u32 tid) { new((void*)hdr) Trace(); // We are going to use only a small part of the trace with the default // value of history_size. However, the constructor writes to the whole trace. - // Unmap the unused part. + // Release the unused part. uptr hdr_end = hdr + sizeof(Trace); hdr_end -= sizeof(TraceHeader) * (kTraceParts - TraceParts()); hdr_end = RoundUp(hdr_end, GetPageSizeCached()); - if (hdr_end < hdr + sizeof(Trace)) - UnmapOrDie((void*)hdr_end, hdr + sizeof(Trace) - hdr_end); + if (hdr_end < hdr + sizeof(Trace)) { + ReleaseMemoryPagesToOS(hdr_end, hdr + sizeof(Trace)); + uptr unused = hdr + sizeof(Trace) - hdr_end; + if (hdr_end != (uptr)MmapFixedNoAccess(hdr_end, unused)) { + Report("ThreadSanitizer: failed to mprotect(%p, %p)\n", + hdr_end, unused); + CHECK("unable to mprotect" && 0); + } + } void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext)); return new(mem) ThreadContext(tid); } @@ -94,42 +114,45 @@ static const u32 kThreadQuarantineSize = 64; #endif Context::Context() - : initialized() - , report_mtx(MutexTypeReport, StatMtxReport) - , nreported() - , nmissed_expected() - , thread_registry(new(thread_registry_placeholder) ThreadRegistry( - CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse)) - , racy_mtx(MutexTypeRacy, StatMtxRacy) - , racy_stacks() - , racy_addresses() - , fired_suppressions_mtx(MutexTypeFired, StatMtxFired) - , clock_alloc("clock allocator") { + : initialized(), + report_mtx(MutexTypeReport, StatMtxReport), + nreported(), + nmissed_expected(), + thread_registry(new (thread_registry_placeholder) ThreadRegistry( + CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse)), + racy_mtx(MutexTypeRacy, StatMtxRacy), + racy_stacks(), + racy_addresses(), + fired_suppressions_mtx(MutexTypeFired, StatMtxFired), + clock_alloc(LINKER_INITIALIZED, "clock allocator") { fired_suppressions.reserve(8); } // The objects are allocated in TLS, so one may rely on zero-initialization. -ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, - unsigned reuse_count, - uptr stk_addr, uptr stk_size, +ThreadState::ThreadState(Context *ctx, u32 tid, int unique_id, u64 epoch, + unsigned reuse_count, uptr stk_addr, uptr stk_size, uptr tls_addr, uptr tls_size) - : fast_state(tid, epoch) - // Do not touch these, rely on zero initialization, - // they may be accessed before the ctor. - // , ignore_reads_and_writes() - // , ignore_interceptors() - , clock(tid, reuse_count) + : fast_state(tid, epoch) + // Do not touch these, rely on zero initialization, + // they may be accessed before the ctor. + // , ignore_reads_and_writes() + // , ignore_interceptors() + , + clock(tid, reuse_count) #if !SANITIZER_GO - , jmp_bufs() + , + jmp_bufs() #endif - , tid(tid) - , unique_id(unique_id) - , stk_addr(stk_addr) - , stk_size(stk_size) - , tls_addr(tls_addr) - , tls_size(tls_size) + , + tid(tid), + unique_id(unique_id), + stk_addr(stk_addr), + stk_size(stk_size), + tls_addr(tls_addr), + tls_size(tls_size) #if !SANITIZER_GO - , last_sleep_clock(tid) + , + last_sleep_clock(tid) #endif { } @@ -160,12 +183,12 @@ static void *BackgroundThread(void *arg) { } else if (internal_strcmp(flags()->profile_memory, "stderr") == 0) { mprof_fd = 2; } else { - InternalScopedString filename(kMaxPathLength); + InternalScopedString filename; filename.append("%s.%d", flags()->profile_memory, (int)internal_getpid()); fd_t fd = OpenFile(filename.data(), WrOnly); if (fd == kInvalidFd) { Printf("ThreadSanitizer: failed to open memory profile file '%s'\n", - &filename[0]); + filename.data()); } else { mprof_fd = fd; } @@ -351,6 +374,18 @@ static void TsanOnDeadlySignal(int signo, void *siginfo, void *context) { } #endif +void CheckUnwind() { + // There is high probability that interceptors will check-fail as well, + // on the other hand there is no sense in processing interceptors + // since we are going to die soon. + ScopedIgnoreInterceptors ignore; +#if !SANITIZER_GO + cur_thread()->ignore_sync++; + cur_thread()->ignore_reads_and_writes++; +#endif + PrintCurrentStackSlow(StackTrace::GetCurrentPc()); +} + void Initialize(ThreadState *thr) { // Thread safe because done before all threads exist. static bool is_initialized = false; @@ -361,7 +396,7 @@ void Initialize(ThreadState *thr) { ScopedIgnoreInterceptors ignore; SanitizerToolName = "ThreadSanitizer"; // Install tool-specific callbacks in sanitizer_common. - SetCheckFailedCallback(TsanCheckFailed); + SetCheckUnwindCallback(CheckUnwind); ctx = new(ctx_placeholder) Context; const char *env_name = SANITIZER_GO ? "GORACE" : "TSAN_OPTIONS"; @@ -499,23 +534,27 @@ int Finalize(ThreadState *thr) { void ForkBefore(ThreadState *thr, uptr pc) { ctx->thread_registry->Lock(); ctx->report_mtx.Lock(); - // Ignore memory accesses in the pthread_atfork callbacks. - // If any of them triggers a data race we will deadlock - // on the report_mtx. - // We could ignore interceptors and sync operations as well, + // Suppress all reports in the pthread_atfork callbacks. + // Reports will deadlock on the report_mtx. + // We could ignore sync operations as well, // but so far it's unclear if it will do more good or harm. // Unnecessarily ignoring things can lead to false positives later. - ThreadIgnoreBegin(thr, pc); + thr->suppress_reports++; + // On OS X, REAL(fork) can call intercepted functions (OSSpinLockLock), and + // we'll assert in CheckNoLocks() unless we ignore interceptors. + thr->ignore_interceptors++; } void ForkParentAfter(ThreadState *thr, uptr pc) { - ThreadIgnoreEnd(thr, pc); // Begin is in ForkBefore. + thr->suppress_reports--; // Enabled in ForkBefore. + thr->ignore_interceptors--; ctx->report_mtx.Unlock(); ctx->thread_registry->Unlock(); } void ForkChildAfter(ThreadState *thr, uptr pc) { - ThreadIgnoreEnd(thr, pc); // Begin is in ForkBefore. + thr->suppress_reports--; // Enabled in ForkBefore. + thr->ignore_interceptors--; ctx->report_mtx.Unlock(); ctx->thread_registry->Unlock(); diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h index 04d474e..3ae519d 100644 --- a/libsanitizer/tsan/tsan_rtl.h +++ b/libsanitizer/tsan/tsan_rtl.h @@ -84,9 +84,6 @@ typedef Allocator::AllocatorCache AllocatorCache; Allocator *allocator(); #endif -void TsanCheckFailed(const char *file, int line, const char *cond, - u64 v1, u64 v2); - const u64 kShadowRodata = (u64)-1; // .rodata shadow marker // FastState (from most significant bit): @@ -406,7 +403,7 @@ struct ThreadState { #if TSAN_COLLECT_STATS u64 stat[StatCnt]; #endif - const int tid; + const u32 tid; const int unique_id; bool in_symbolizer; bool in_ignored_lib; @@ -447,9 +444,8 @@ struct ThreadState { const ReportDesc *current_report; - explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, - unsigned reuse_count, - uptr stk_addr, uptr stk_size, + explicit ThreadState(Context *ctx, u32 tid, int unique_id, u64 epoch, + unsigned reuse_count, uptr stk_addr, uptr stk_size, uptr tls_addr, uptr tls_size); }; @@ -624,6 +620,7 @@ class ScopedReport : public ScopedReportBase { ScopedErrorReportLock lock_; }; +bool ShouldReport(ThreadState *thr, ReportType typ); ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack); void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, MutexSet *mset, uptr *tag = nullptr); diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cpp b/libsanitizer/tsan/tsan_rtl_mutex.cpp index 27897f0..0a8f3aa 100644 --- a/libsanitizer/tsan/tsan_rtl_mutex.cpp +++ b/libsanitizer/tsan/tsan_rtl_mutex.cpp @@ -51,6 +51,8 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ, // or false positives (e.g. unlock in a different thread). if (SANITIZER_GO) return; + if (!ShouldReport(thr, typ)) + return; ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(typ); rep.AddMutex(mid); @@ -96,9 +98,8 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { ctx->dd->MutexInit(&cb, &s->dd); } bool unlock_locked = false; - if (flags()->report_destroy_locked - && s->owner_tid != SyncVar::kInvalidTid - && !s->IsFlagSet(MutexFlagBroken)) { + if (flags()->report_destroy_locked && s->owner_tid != kInvalidTid && + !s->IsFlagSet(MutexFlagBroken)) { s->SetFlags(MutexFlagBroken); unlock_locked = true; } @@ -107,7 +108,7 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { if (!unlock_locked) s->Reset(thr->proc()); // must not reset it before the report is printed s->mtx.Unlock(); - if (unlock_locked) { + if (unlock_locked && ShouldReport(thr, ReportTypeMutexDestroyLocked)) { ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeMutexDestroyLocked); rep.AddMutex(mid); @@ -169,7 +170,7 @@ void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz, int rec) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId()); bool report_double_lock = false; - if (s->owner_tid == SyncVar::kInvalidTid) { + if (s->owner_tid == kInvalidTid) { CHECK_EQ(s->recursion, 0); s->owner_tid = thr->tid; s->last_lock = thr->fast_state.raw(); @@ -229,7 +230,7 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { s->recursion -= rec; if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); - s->owner_tid = SyncVar::kInvalidTid; + s->owner_tid = kInvalidTid; ReleaseStoreImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); @@ -275,7 +276,7 @@ void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); bool report_bad_lock = false; - if (s->owner_tid != SyncVar::kInvalidTid) { + if (s->owner_tid != kInvalidTid) { if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) { s->SetFlags(MutexFlagBroken); report_bad_lock = true; @@ -314,7 +315,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); bool report_bad_unlock = false; - if (s->owner_tid != SyncVar::kInvalidTid) { + if (s->owner_tid != kInvalidTid) { if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) { s->SetFlags(MutexFlagBroken); report_bad_unlock = true; @@ -344,7 +345,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true); bool write = true; bool report_bad_unlock = false; - if (s->owner_tid == SyncVar::kInvalidTid) { + if (s->owner_tid == kInvalidTid) { // Seems to be read unlock. write = false; StatInc(thr, StatMutexReadUnlock); @@ -359,7 +360,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { s->recursion--; if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); - s->owner_tid = SyncVar::kInvalidTid; + s->owner_tid = kInvalidTid; ReleaseStoreImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); @@ -387,7 +388,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr); SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true); - s->owner_tid = SyncVar::kInvalidTid; + s->owner_tid = kInvalidTid; s->recursion = 0; s->mtx.Unlock(); } @@ -534,7 +535,7 @@ void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { } void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { - if (r == 0) + if (r == 0 || !ShouldReport(thr, ReportTypeDeadlock)) return; ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeDeadlock); diff --git a/libsanitizer/tsan/tsan_rtl_report.cpp b/libsanitizer/tsan/tsan_rtl_report.cpp index 208d0df..706794f 100644 --- a/libsanitizer/tsan/tsan_rtl_report.cpp +++ b/libsanitizer/tsan/tsan_rtl_report.cpp @@ -31,23 +31,6 @@ using namespace __sanitizer; static ReportStack *SymbolizeStack(StackTrace trace); -void TsanCheckFailed(const char *file, int line, const char *cond, - u64 v1, u64 v2) { - // There is high probability that interceptors will check-fail as well, - // on the other hand there is no sense in processing interceptors - // since we are going to die soon. - ScopedIgnoreInterceptors ignore; -#if !SANITIZER_GO - cur_thread()->ignore_sync++; - cur_thread()->ignore_reads_and_writes++; -#endif - Printf("FATAL: ThreadSanitizer CHECK failed: " - "%s:%d \"%s\" (0x%zx, 0x%zx)\n", - file, line, cond, (uptr)v1, (uptr)v2); - PrintCurrentStackSlow(StackTrace::GetCurrentPc()); - Die(); -} - // Can be overriden by an application/test to intercept reports. #ifdef TSAN_EXTERNAL_HOOKS bool OnReport(const ReportDesc *rep, bool suppressed); @@ -142,6 +125,34 @@ static ReportStack *SymbolizeStack(StackTrace trace) { return stack; } +bool ShouldReport(ThreadState *thr, ReportType typ) { + // We set thr->suppress_reports in the fork context. + // Taking any locking in the fork context can lead to deadlocks. + // If any locks are already taken, it's too late to do this check. + CheckNoLocks(thr); + // For the same reason check we didn't lock thread_registry yet. + if (SANITIZER_DEBUG) + ThreadRegistryLock l(ctx->thread_registry); + if (!flags()->report_bugs || thr->suppress_reports) + return false; + switch (typ) { + case ReportTypeSignalUnsafe: + return flags()->report_signal_unsafe; + case ReportTypeThreadLeak: +#if !SANITIZER_GO + // It's impossible to join phantom threads + // in the child after fork. + if (ctx->after_multithreaded_fork) + return false; +#endif + return flags()->report_thread_leaks; + case ReportTypeMutexDestroyLocked: + return flags()->report_destroy_locked; + default: + return true; + } +} + ScopedReportBase::ScopedReportBase(ReportType typ, uptr tag) { ctx->thread_registry->CheckLocked(); void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc)); @@ -497,8 +508,10 @@ static bool HandleRacyAddress(ThreadState *thr, uptr addr_min, uptr addr_max) { } bool OutputReport(ThreadState *thr, const ScopedReport &srep) { - if (!flags()->report_bugs || thr->suppress_reports) - return false; + // These should have been checked in ShouldReport. + // It's too late to check them here, we have already taken locks. + CHECK(flags()->report_bugs); + CHECK(!thr->suppress_reports); atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime()); const ReportDesc *rep = srep.GetReport(); CHECK_EQ(thr->current_report, nullptr); @@ -589,7 +602,7 @@ void ReportRace(ThreadState *thr) { // at best it will cause deadlocks on internal mutexes. ScopedIgnoreInterceptors ignore; - if (!flags()->report_bugs) + if (!ShouldReport(thr, ReportTypeRace)) return; if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) return; @@ -722,8 +735,7 @@ void PrintCurrentStack(ThreadState *thr, uptr pc) { // However, this solution is not reliable enough, please see dvyukov's comment // http://reviews.llvm.org/D19148#406208 // Also see PR27280 comment 2 and 3 for breaking examples and analysis. -ALWAYS_INLINE -void PrintCurrentStackSlow(uptr pc) { +ALWAYS_INLINE USED void PrintCurrentStackSlow(uptr pc) { #if !SANITIZER_GO uptr bp = GET_CURRENT_FRAME(); BufferedStackTrace *ptrace = diff --git a/libsanitizer/tsan/tsan_rtl_thread.cpp b/libsanitizer/tsan/tsan_rtl_thread.cpp index d801467..6d1ccd8 100644 --- a/libsanitizer/tsan/tsan_rtl_thread.cpp +++ b/libsanitizer/tsan/tsan_rtl_thread.cpp @@ -51,7 +51,7 @@ struct OnCreatedArgs { void ThreadContext::OnCreated(void *arg) { thr = 0; - if (tid == 0) + if (tid == kMainTid) return; OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg); if (!args->thr) // GCD workers don't have a parent thread. @@ -179,7 +179,7 @@ static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) { #if !SANITIZER_GO static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) { - if (tctx->tid == 0) { + if (tctx->tid == kMainTid) { Printf("ThreadSanitizer: main thread finished with ignores enabled\n"); } else { Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled," @@ -210,7 +210,7 @@ static void ThreadCheckIgnore(ThreadState *thr) {} void ThreadFinalize(ThreadState *thr) { ThreadCheckIgnore(thr); #if !SANITIZER_GO - if (!flags()->report_thread_leaks) + if (!ShouldReport(thr, ReportTypeThreadLeak)) return; ThreadRegistryLock l(ctx->thread_registry); Vector<ThreadLeak> leaks; @@ -250,9 +250,10 @@ void ThreadStart(ThreadState *thr, int tid, tid_t os_id, uptr tls_size = 0; #if !SANITIZER_GO if (thread_type != ThreadType::Fiber) - GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size); + GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_size, &tls_addr, + &tls_size); - if (tid) { + if (tid != kMainTid) { if (stk_addr && stk_size) MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size); @@ -313,7 +314,7 @@ static bool ConsumeThreadByUid(ThreadContextBase *tctx, void *arg) { int ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid) { ConsumeThreadContext findCtx = {uid, nullptr}; ctx->thread_registry->FindThread(ConsumeThreadByUid, &findCtx); - int tid = findCtx.tctx ? findCtx.tctx->tid : ThreadRegistry::kUnknownTid; + int tid = findCtx.tctx ? findCtx.tctx->tid : kInvalidTid; DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, tid); return tid; } diff --git a/libsanitizer/tsan/tsan_sync.cpp b/libsanitizer/tsan/tsan_sync.cpp index 17ddd50f..ba24f98 100644 --- a/libsanitizer/tsan/tsan_sync.cpp +++ b/libsanitizer/tsan/tsan_sync.cpp @@ -53,8 +53,8 @@ void SyncVar::Reset(Processor *proc) { } MetaMap::MetaMap() - : block_alloc_("heap block allocator") - , sync_alloc_("sync allocator") { + : block_alloc_(LINKER_INITIALIZED, "heap block allocator"), + sync_alloc_(LINKER_INITIALIZED, "sync allocator") { atomic_store(&uid_gen_, 0, memory_order_relaxed); } diff --git a/libsanitizer/tsan/tsan_sync.h b/libsanitizer/tsan/tsan_sync.h index 47f2739..c4056f6 100644 --- a/libsanitizer/tsan/tsan_sync.h +++ b/libsanitizer/tsan/tsan_sync.h @@ -50,13 +50,11 @@ enum MutexFlags { struct SyncVar { SyncVar(); - static const int kInvalidTid = -1; - uptr addr; // overwritten by DenseSlabAlloc freelist Mutex mtx; u64 uid; // Globally unique id. u32 creation_stack_id; - int owner_tid; // Set only by exclusive owners. + u32 owner_tid; // Set only by exclusive owners. u64 last_lock; int recursion; atomic_uint32_t flags; @@ -130,8 +128,8 @@ class MetaMap { static const u32 kFlagMask = 3u << 30; static const u32 kFlagBlock = 1u << 30; static const u32 kFlagSync = 2u << 30; - typedef DenseSlabAlloc<MBlock, 1<<16, 1<<12> BlockAlloc; - typedef DenseSlabAlloc<SyncVar, 1<<16, 1<<10> SyncAlloc; + typedef DenseSlabAlloc<MBlock, 1 << 18, 1 << 12, kFlagMask> BlockAlloc; + typedef DenseSlabAlloc<SyncVar, 1 << 20, 1 << 10, kFlagMask> SyncAlloc; BlockAlloc block_alloc_; SyncAlloc sync_alloc_; atomic_uint64_t uid_gen_; diff --git a/libsanitizer/ubsan/ubsan_diag.cpp b/libsanitizer/ubsan/ubsan_diag.cpp index 1b2828d..ef2e495 100644 --- a/libsanitizer/ubsan/ubsan_diag.cpp +++ b/libsanitizer/ubsan/ubsan_diag.cpp @@ -278,7 +278,7 @@ static void PrintMemorySnippet(const Decorator &Decor, MemoryLocation Loc, } // Emit data. - InternalScopedString Buffer(1024); + InternalScopedString Buffer; for (uptr P = Min; P != Max; ++P) { unsigned char C = *reinterpret_cast<const unsigned char*>(P); Buffer.append("%s%02x", (P % 8 == 0) ? " " : " ", C); @@ -346,7 +346,7 @@ Diag::~Diag() { // All diagnostics should be printed under report mutex. ScopedReport::CheckLocked(); Decorator Decor; - InternalScopedString Buffer(1024); + InternalScopedString Buffer; // Prepare a report that a monitor process can inspect. if (Level == DL_Error) { @@ -388,6 +388,10 @@ ScopedReport::ScopedReport(ReportOptions Opts, Location SummaryLoc, ScopedReport::~ScopedReport() { MaybePrintStackTrace(Opts.pc, Opts.bp); MaybeReportErrorSummary(SummaryLoc, Type); + + if (common_flags()->print_module_map >= 2) + DumpProcessMap(); + if (flags()->halt_on_error) Die(); } diff --git a/libsanitizer/ubsan/ubsan_init.cpp b/libsanitizer/ubsan/ubsan_init.cpp index e0be5a7..9931d85 100644 --- a/libsanitizer/ubsan/ubsan_init.cpp +++ b/libsanitizer/ubsan/ubsan_init.cpp @@ -33,6 +33,11 @@ static void CommonInit() { InitializeSuppressions(); } +static void UbsanDie() { + if (common_flags()->print_module_map >= 1) + DumpProcessMap(); +} + static void CommonStandaloneInit() { SanitizerToolName = GetSanititizerToolName(); CacheBinaryName(); @@ -42,6 +47,10 @@ static void CommonStandaloneInit() { AndroidLogInit(); InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir); CommonInit(); + + // Only add die callback when running in standalone mode to avoid printing + // the same information from multiple sanitizers' output + AddDieCallback(UbsanDie); Symbolizer::LateInitialize(); } diff --git a/libsanitizer/ubsan/ubsan_monitor.cpp b/libsanitizer/ubsan/ubsan_monitor.cpp index d064e95..69dd986 100644 --- a/libsanitizer/ubsan/ubsan_monitor.cpp +++ b/libsanitizer/ubsan/ubsan_monitor.cpp @@ -17,7 +17,7 @@ using namespace __ubsan; UndefinedBehaviorReport::UndefinedBehaviorReport(const char *IssueKind, Location &Loc, InternalScopedString &Msg) - : IssueKind(IssueKind), Loc(Loc), Buffer(Msg.length() + 1) { + : IssueKind(IssueKind), Loc(Loc) { // We have the common sanitizer reporting lock, so it's safe to register a // new UB report. RegisterUndefinedBehaviorReport(this); @@ -52,9 +52,9 @@ void __ubsan::__ubsan_get_current_report_data(const char **OutIssueKind, // Ensure that the first character of the diagnostic text can't start with a // lowercase letter. - char FirstChar = Buf.data()[0]; + char FirstChar = *Buf.data(); if (FirstChar >= 'a' && FirstChar <= 'z') - Buf.data()[0] = FirstChar - 'a' + 'A'; + *Buf.data() += 'A' - 'a'; *OutIssueKind = CurrentUBR->IssueKind; *OutMessage = Buf.data(); diff --git a/libsanitizer/ubsan/ubsan_platform.h b/libsanitizer/ubsan/ubsan_platform.h index 98542fce..42944a0 100644 --- a/libsanitizer/ubsan/ubsan_platform.h +++ b/libsanitizer/ubsan/ubsan_platform.h @@ -15,7 +15,7 @@ #ifndef CAN_SANITIZE_UB // Other platforms should be easy to add, and probably work as-is. #if defined(__linux__) || defined(__FreeBSD__) || defined(__APPLE__) || \ - defined(__NetBSD__) || \ + defined(__NetBSD__) || defined(__DragonFly__) || \ (defined(__sun__) && defined(__svr4__)) || \ defined(_WIN32) || defined(__Fuchsia__) || defined(__rtems__) # define CAN_SANITIZE_UB 1 diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 6ca96fd..e48de21 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,47 @@ +2021-05-12 Jonathan Wakely <jwakely@redhat.com> + + * testsuite/25_algorithms/pstl/alg_nonmodifying/find_end.cc: + Increase dg-timeout-factor to 4. Fix -Wunused-parameter + warnings. Replace bitwise AND with logical AND in loop + condition. + * testsuite/25_algorithms/pstl/alg_nonmodifying/search_n.cc: + Replace bitwise AND with logical AND in loop condition. + * testsuite/util/pstl/test_utils.h: Remove unused parameter + names. + +2021-05-11 Patrick Palka <ppalka@redhat.com> + + * src/c++17/ryu/LOCAL_PATCHES: Update. + * src/c++17/ryu/ryu_generic_128.h: Remove extern "C". + Remove declarations for never-defined functions. + * testsuite/20_util/to_chars/4.cc: New test. + +2021-05-11 Jonathan Wakely <jwakely@redhat.com> + + * testsuite/20_util/headers/memory/synopsis.cc: Define C++98 + alternative for macro. + * testsuite/20_util/shared_ptr/creation/99006.cc: Add effective + target keyword. + * testsuite/25_algorithms/copy/debug/99402.cc: Avoid C++11 + syntax. + +2021-05-11 Jonathan Wakely <jwakely@redhat.com> + + * include/bits/allocator.h (allocator<void>) [C++20]: Add + missing noexcept to constructor. Restore missing POCMA and + is_always_equal_traits. + [C++17]: Make construct and destroy members private and + declare allocator_traits as a friend. + * include/bits/memoryfwd.h (allocator_traits): Declare. + * include/ext/malloc_allocator.h (malloc_allocator::allocate): + Add nodiscard attribute. Add static assertion for LWG 3307. + * include/ext/new_allocator.h (new_allocator::allocate): Add + static assertion for LWG 3307. + * testsuite/20_util/allocator/void.cc: Check that converting + constructor is noexcept. Check for propagation traits and + size_type and difference_type. Check that pointer and + const_pointer are gone in C++20. + 2021-05-10 Jonathan Wakely <jwakely@redhat.com> * include/std/stop_token: Remove TODO comment. diff --git a/libstdc++-v3/doc/Makefile.am b/libstdc++-v3/doc/Makefile.am index 2f8bb07..487e862 100644 --- a/libstdc++-v3/doc/Makefile.am +++ b/libstdc++-v3/doc/Makefile.am @@ -226,10 +226,10 @@ ${doxygen_outdir}/man: mkdir -p ${doxygen_outdir}/man stamp-xml-doxygen: ${doxygen_outdir}/xml - -(srcdir=`cd ${top_srcdir}; ${PWD_COMMAND}`; \ - builddir=`cd ..; ${PWD_COMMAND}`; \ + @builddir=`cd ..; ${PWD_COMMAND}`; \ ${SHELL} ${doxygen_script} \ - --host_alias=${host_alias} --mode=xml $${srcdir} $${builddir} NO) + --host_alias=${host_alias} --mode=xml \ + "${top_srcdir}" "$${builddir}" NO || true $(STAMP) stamp-xml-doxygen stamp-xml-single-doxygen: stamp-xml-doxygen @@ -239,29 +239,29 @@ stamp-xml-single-doxygen: stamp-xml-doxygen $(STAMP) stamp-xml-single-doxygen stamp-html-doxygen: ${doxygen_outdir}/html - -(srcdir=`cd ${top_srcdir}; ${PWD_COMMAND}`; \ - builddir=`cd ..; ${PWD_COMMAND}`; \ + @builddir=`cd ..; ${PWD_COMMAND}`; \ ${SHELL} ${doxygen_script} \ - --host_alias=${host_alias} --mode=html $${srcdir} $${builddir} YES) + --host_alias=${host_alias} --mode=html \ + "${top_srcdir}" "$${builddir}" YES || true $(STAMP) stamp-html-doxygen stamp-latex-doxygen: ${doxygen_outdir}/latex - -(srcdir=`cd ${top_srcdir}; ${PWD_COMMAND}`; \ - builddir=`cd ..; ${PWD_COMMAND}`; \ + @builddir=`cd ..; ${PWD_COMMAND}`; \ ${SHELL} ${doxygen_script} \ - --host_alias=${host_alias} --mode=latex $${srcdir} $${builddir} NO) + --host_alias=${host_alias} --mode=latex --latex_cmd=$(LATEX_CMD) \ + "${top_srcdir}" "$${builddir}" NO || true $(STAMP) stamp-latex-doxygen # Chance of loooooonnggg creation time on this rule. Iff this fails, # look at refman.log and see if TeX's memory is exhausted. Symptoms # include asking a wizard to enlarge capacity. If this is the case, # find texmf.cnf and add a zero for pool_size, string_vacancies, -# max_strings, and pool_free values. A much simpler workaround is to install -# lualatex and set LATEX_CMD_NAME = lualatex in the doxygen user.cfg file. +# max_strings, and pool_free values. A much simpler workaround is to +# install lualatex and set LATEX_CMD=lualatex when running make. # Errors like "File `foo.sty' not found" mean a TeX package is missing. stamp-pdf-doxygen: stamp-latex-doxygen ${doxygen_outdir}/pdf - -(cd ${doxygen_outdir}/latex && $(MAKE) -i pdf;) @echo "Generating doxygen pdf file..."; + -$(MAKE) -C ${doxygen_outdir}/latex -i pdf @if [ -f ${doxygen_pdf} ]; then \ mv ${doxygen_pdf} ${api_pdf} ; \ echo ":: PDF file is ${api_pdf}"; \ @@ -274,10 +274,10 @@ stamp-pdf-doxygen: stamp-latex-doxygen ${doxygen_outdir}/pdf $(STAMP) stamp-pdf-doxygen stamp-man-doxygen: ${doxygen_outdir}/man - -(srcdir=`cd ${top_srcdir}; ${PWD_COMMAND}`; \ - builddir=`cd ..; ${PWD_COMMAND}`; \ + @builddir=`cd ..; ${PWD_COMMAND}`; \ ${SHELL} ${doxygen_script} \ - --host_alias=${host_alias} --mode=man $${srcdir} $${builddir} YES) + --host_alias=${host_alias} --mode=man \ + "${top_srcdir}" "$${builddir}" YES || true $(STAMP) stamp-man-doxygen doc-xml-doxygen: stamp-xml-doxygen diff --git a/libstdc++-v3/doc/Makefile.in b/libstdc++-v3/doc/Makefile.in index 18a6a99..fe1113e 100644 --- a/libstdc++-v3/doc/Makefile.in +++ b/libstdc++-v3/doc/Makefile.in @@ -917,10 +917,10 @@ ${doxygen_outdir}/man: mkdir -p ${doxygen_outdir}/man stamp-xml-doxygen: ${doxygen_outdir}/xml - -(srcdir=`cd ${top_srcdir}; ${PWD_COMMAND}`; \ - builddir=`cd ..; ${PWD_COMMAND}`; \ + @builddir=`cd ..; ${PWD_COMMAND}`; \ ${SHELL} ${doxygen_script} \ - --host_alias=${host_alias} --mode=xml $${srcdir} $${builddir} NO) + --host_alias=${host_alias} --mode=xml \ + "${top_srcdir}" "$${builddir}" NO || true $(STAMP) stamp-xml-doxygen stamp-xml-single-doxygen: stamp-xml-doxygen @@ -930,29 +930,29 @@ stamp-xml-single-doxygen: stamp-xml-doxygen $(STAMP) stamp-xml-single-doxygen stamp-html-doxygen: ${doxygen_outdir}/html - -(srcdir=`cd ${top_srcdir}; ${PWD_COMMAND}`; \ - builddir=`cd ..; ${PWD_COMMAND}`; \ + @builddir=`cd ..; ${PWD_COMMAND}`; \ ${SHELL} ${doxygen_script} \ - --host_alias=${host_alias} --mode=html $${srcdir} $${builddir} YES) + --host_alias=${host_alias} --mode=html \ + "${top_srcdir}" "$${builddir}" YES || true $(STAMP) stamp-html-doxygen stamp-latex-doxygen: ${doxygen_outdir}/latex - -(srcdir=`cd ${top_srcdir}; ${PWD_COMMAND}`; \ - builddir=`cd ..; ${PWD_COMMAND}`; \ + @builddir=`cd ..; ${PWD_COMMAND}`; \ ${SHELL} ${doxygen_script} \ - --host_alias=${host_alias} --mode=latex $${srcdir} $${builddir} NO) + --host_alias=${host_alias} --mode=latex --latex_cmd=$(LATEX_CMD) \ + "${top_srcdir}" "$${builddir}" NO || true $(STAMP) stamp-latex-doxygen # Chance of loooooonnggg creation time on this rule. Iff this fails, # look at refman.log and see if TeX's memory is exhausted. Symptoms # include asking a wizard to enlarge capacity. If this is the case, # find texmf.cnf and add a zero for pool_size, string_vacancies, -# max_strings, and pool_free values. A much simpler workaround is to install -# lualatex and set LATEX_CMD_NAME = lualatex in the doxygen user.cfg file. +# max_strings, and pool_free values. A much simpler workaround is to +# install lualatex and set LATEX_CMD=lualatex when running make. # Errors like "File `foo.sty' not found" mean a TeX package is missing. stamp-pdf-doxygen: stamp-latex-doxygen ${doxygen_outdir}/pdf - -(cd ${doxygen_outdir}/latex && $(MAKE) -i pdf;) @echo "Generating doxygen pdf file..."; + -$(MAKE) -C ${doxygen_outdir}/latex -i pdf @if [ -f ${doxygen_pdf} ]; then \ mv ${doxygen_pdf} ${api_pdf} ; \ echo ":: PDF file is ${api_pdf}"; \ @@ -965,10 +965,10 @@ stamp-pdf-doxygen: stamp-latex-doxygen ${doxygen_outdir}/pdf $(STAMP) stamp-pdf-doxygen stamp-man-doxygen: ${doxygen_outdir}/man - -(srcdir=`cd ${top_srcdir}; ${PWD_COMMAND}`; \ - builddir=`cd ..; ${PWD_COMMAND}`; \ + @builddir=`cd ..; ${PWD_COMMAND}`; \ ${SHELL} ${doxygen_script} \ - --host_alias=${host_alias} --mode=man $${srcdir} $${builddir} YES) + --host_alias=${host_alias} --mode=man \ + "${top_srcdir}" "$${builddir}" YES || true $(STAMP) stamp-man-doxygen doc-xml-doxygen: stamp-xml-doxygen diff --git a/libstdc++-v3/doc/doxygen/user.cfg.in b/libstdc++-v3/doc/doxygen/user.cfg.in index 4be2e19..8c4fed5 100644 --- a/libstdc++-v3/doc/doxygen/user.cfg.in +++ b/libstdc++-v3/doc/doxygen/user.cfg.in @@ -1945,7 +1945,7 @@ LATEX_OUTPUT = latex # the output language. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_CMD_NAME = +LATEX_CMD_NAME = @latex_cmd@ # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate # index for LaTeX. diff --git a/libstdc++-v3/scripts/run_doxygen b/libstdc++-v3/scripts/run_doxygen index 3a24a1f..a367721 100644 --- a/libstdc++-v3/scripts/run_doxygen +++ b/libstdc++-v3/scripts/run_doxygen @@ -35,8 +35,7 @@ find_doxygen() { fi done if test -z "$doxygen"; then - echo run_doxygen error: Could not find Doxygen $DOXYVER in path. 1>&2 - print_usage + fail "Could not find Doxygen $DOXYVER in path." fi # We need to use other tools from the same package/version. echo :: Using Doxygen tools from ${dir}. @@ -45,8 +44,8 @@ find_doxygen() { } print_usage() { - cat 1>&2 <<EOF -Usage: run_doxygen --mode=MODE --host_alias=BUILD_ALIAS [<options>] + cat <<EOF +Usage: run_doxygen --mode=MODE --host_alias=HOST_ALIAS [<options>] <v3-src-dir> <v3-build-dir> <shortnamesp> MODE is one of: html Generate user-level HTML library documentation. @@ -54,48 +53,67 @@ Usage: run_doxygen --mode=MODE --host_alias=BUILD_ALIAS [<options>] xml Generate user-level XML pages. latex Generate user-level LaTeX pages. - BUILD_ALIAS is the GCC build alias set at configure time. + HOST_ALIAS is the GCC host alias triplet set at configure time. + + shortnamesp is one of YES or NO and is used as the SHORT_NAMES value + in the Doxygen config file. + + Supported options: + + --help | -h Print this message and exit. + --latex_cmd=CMD Set LATEX_CMD_NAME=CMD in the Doxygen config file. Note: Requires Doxygen ${DOXYVER} or later; get it at ftp://ftp.stack.nl/pub/users/dimitri/doxygen-${DOXYVER}.src.tar.gz EOF - exit 1 +} + +# Print an error message followed by usage to stderr, then exit. +fail() { + echo "$0: error: $*" 1>&2 + echo 1>&2 + print_usage 1>&2 + exit 1 } parse_options() { - for o + while [ $# -ne 0 ] do # Blatantly ripped from autoconf, er, I mean, "gratefully standing # on the shoulders of those giants who have gone before us." - case "$o" in - -*=*) arg=`echo "$o" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + case "$1" in + -*=*) arg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) arg= ;; esac - case "$o" in + case "$1" in --mode=*) mode=$arg ;; --host_alias=*) host_alias=$arg ;; - --mode | --host_alias | --help | -h) - print_usage ;; + --help | -h) + print_usage ; exit ;; + --mode | --host_alias) + fail "missing argument: $1" ;; + --latex_cmd=*) + latex_cmd=$arg ;; + --*) + fail "invalid option: $1" ;; *) - # this turned out to be a mess, maybe change to --srcdir=, etc - if test $srcdir = unset; then - srcdir=$o - elif test $outdir = unset; then - builddir=${o} - outdir=${o}/doc/doxygen - elif test $shortname = unset; then - shortname=$o - else - echo run_doxygen error: Too many arguments 1>&2 - exit 1 - fi - ;; - esac + break ;; + esac + shift done + + if [ $# -ne 3 ] + then + fail "wrong number of arguments" + fi + srcdir="$1" + builddir="$2" + outdir="$2/doc/doxygen" + shortname="$3" } @@ -109,6 +127,7 @@ do_html=false do_man=false do_xml=false do_latex=false +latex_cmd= enabled_sections= generate_tagfile= DATEtext=`date '+%Y-%m-%d'` @@ -121,8 +140,7 @@ find_doxygen if test $srcdir = unset || test $outdir = unset || test $mode = unset || test $shortname = unset || test $host_alias = unset; then # this could be better - echo run_doxygen error: You have not given enough information...! 1>&2 - print_usage + fail "You have not given enough information...! $srcdir - " fi case x"$mode" in @@ -173,6 +191,7 @@ chmod u+w $outdir -e "s=@enabled_sections@=${enabled_sections}=" \ -e "s=@do_html@=${do_html}=" \ -e "s=@do_latex@=${do_latex}=" \ + -e "s=@latex_cmd@=${latex_cmd}=" \ -e "s=@do_man@=${do_man}=" \ -e "s=@do_xml@=${do_xml}=" \ -e "s=@generate_tagfile@=${generate_tagfile}=" \ diff --git a/libstdc++-v3/testsuite/25_algorithms/pstl/alg_nonmodifying/find_end.cc b/libstdc++-v3/testsuite/25_algorithms/pstl/alg_nonmodifying/find_end.cc index 0b950e2..6e49947 100644 --- a/libstdc++-v3/testsuite/25_algorithms/pstl/alg_nonmodifying/find_end.cc +++ b/libstdc++-v3/testsuite/25_algorithms/pstl/alg_nonmodifying/find_end.cc @@ -1,7 +1,7 @@ // -*- C++ -*- // { dg-options "-ltbb" } // { dg-do run { target c++17 } } -// { dg-timeout-factor 3 } +// { dg-timeout-factor 4 } // { dg-require-effective-target tbb-backend } //===-- find_end.pass.cpp -------------------------------------------------===// @@ -78,8 +78,8 @@ test(const std::size_t bits) const std::size_t max_n1 = 1000; const std::size_t max_n2 = (max_n1 * 10) / 8; - Sequence<T> in(max_n1, [max_n1, bits](std::size_t k) { return T(2 * HashBits(max_n1, bits - 1) ^ 1); }); - Sequence<T> sub(max_n2, [max_n1, bits](std::size_t k) { return T(2 * HashBits(max_n1, bits - 1)); }); + Sequence<T> in(max_n1, [max_n1, bits](std::size_t) { return T(2 * HashBits(max_n1, bits - 1) ^ 1); }); + Sequence<T> sub(max_n2, [max_n1, bits](std::size_t) { return T(2 * HashBits(max_n1, bits - 1)); }); for (std::size_t n1 = 0; n1 <= max_n1; n1 = n1 <= 16 ? n1 + 1 : size_t(3.1415 * n1)) { std::size_t sub_n[] = {0, 1, 3, n1, (n1 * 10) / 8}; @@ -89,7 +89,7 @@ test(const std::size_t bits) for (auto r : res) { std::size_t i = r, isub = 0; - for (; i < n1 & isub < n2; ++i, ++isub) + for (; i < n1 && isub < n2; ++i, ++isub) in[i] = sub[isub]; invoke_on_all_policies(test_one_policy(), in.begin(), in.begin() + n1, sub.begin(), sub.begin() + n2, std::equal_to<T>()); diff --git a/libstdc++-v3/testsuite/25_algorithms/pstl/alg_nonmodifying/search_n.cc b/libstdc++-v3/testsuite/25_algorithms/pstl/alg_nonmodifying/search_n.cc index 4706737..6b6fb03 100644 --- a/libstdc++-v3/testsuite/25_algorithms/pstl/alg_nonmodifying/search_n.cc +++ b/libstdc++-v3/testsuite/25_algorithms/pstl/alg_nonmodifying/search_n.cc @@ -79,7 +79,7 @@ test() { Sequence<T> in(n1, [n1](std::size_t k) { return T(0); }); std::size_t i = r, isub = 0; - for (; i < n1 & isub < n2; ++i, ++isub) + for (; i < n1 && isub < n2; ++i, ++isub) in[i] = value; invoke_on_all_policies(test_one_policy(), in.begin(), in.begin() + n1, n2, value, std::equal_to<T>()); diff --git a/libstdc++-v3/testsuite/util/pstl/test_utils.h b/libstdc++-v3/testsuite/util/pstl/test_utils.h index 6547d93..80a8f9c 100644 --- a/libstdc++-v3/testsuite/util/pstl/test_utils.h +++ b/libstdc++-v3/testsuite/util/pstl/test_utils.h @@ -752,7 +752,7 @@ struct invoke_if_<std::false_type, std::false_type> { template <typename Op, typename... Rest> void - operator()(bool is_allow, Op op, Rest&&... rest) + operator()(bool, Op op, Rest&&... rest) { op(std::forward<Rest>(rest)...); } @@ -787,14 +787,14 @@ struct non_const_wrapper_tagged : non_const_wrapper template <typename Policy, typename Iterator> typename std::enable_if<IsPositiveCondition != is_same_iterator_category<Iterator, IteratorTag>::value, void>::type - operator()(Policy&& exec, Iterator iter) + operator()(Policy&&, Iterator) { } template <typename Policy, typename InputIterator, typename OutputIterator> typename std::enable_if<IsPositiveCondition != is_same_iterator_category<OutputIterator, IteratorTag>::value, void>::type - operator()(Policy&& exec, InputIterator input_iter, OutputIterator out_iter) + operator()(Policy&&, InputIterator, OutputIterator) { } }; @@ -999,7 +999,7 @@ struct iterator_invoker<std::forward_iterator_tag, /*isReverse=*/std::true_type> { template <typename... Rest> void - operator()(Rest&&... rest) + operator()(Rest&&...) { } }; @@ -1226,7 +1226,7 @@ test_algo_basic_double(F&& f) template <typename Policy, typename F> static void -invoke_if(Policy&& p, F f) +invoke_if(Policy&&, F f) { #if _PSTL_ICC_16_VC14_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN || _PSTL_ICC_17_VC141_TEST_SIMD_LAMBDA_DEBUG_32_BROKEN __pstl::__internal::invoke_if_not(__pstl::__internal::allow_unsequenced<Policy>(), f); diff --git a/maintainer-scripts/ChangeLog b/maintainer-scripts/ChangeLog index ab18cfd..8f30655 100644 --- a/maintainer-scripts/ChangeLog +++ b/maintainer-scripts/ChangeLog @@ -1,3 +1,7 @@ +2021-05-14 Jakub Jelinek <jakub@redhat.com> + + * crontab: Stop doing gcc-8 snapshots. + 2021-04-20 Jakub Jelinek <jakub@redhat.com> * crontab: Snapshots from trunk are now GCC 12 related. diff --git a/maintainer-scripts/crontab b/maintainer-scripts/crontab index 8f2c0d8..93612a3 100644 --- a/maintainer-scripts/crontab +++ b/maintainer-scripts/crontab @@ -1,7 +1,6 @@ 16 0 * * * sh /home/gccadmin/scripts/update_version_git 50 0 * * * sh /home/gccadmin/scripts/update_web_docs_git 55 0 * * * sh /home/gccadmin/scripts/update_web_docs_libstdcxx_git -32 22 * * 3 sh /home/gccadmin/scripts/gcc_release -s 8:releases/gcc-8 -l -d /sourceware/snapshot-tmp/gcc all 32 22 * * 4 sh /home/gccadmin/scripts/gcc_release -s 9:releases/gcc-9 -l -d /sourceware/snapshot-tmp/gcc all 32 22 * * 5 sh /home/gccadmin/scripts/gcc_release -s 10:releases/gcc-10 -l -d /sourceware/snapshot-tmp/gcc all 32 22 * * 6 sh /home/gccadmin/scripts/gcc_release -s 11:releases/gcc-11 -l -d /sourceware/snapshot-tmp/gcc all |