From 70523ecfaca692bf5d0192e466c34ae7514624ea Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Mon, 7 Sep 2020 02:27:11 -0400 Subject: [PATCH 01/36] [Sparc] Select the UltraSPARC instruction set with the external assembler Select the UltraSPARC instruction set with the external assembler on Linux / FreeBSD / OpenBSD, matches GCC. --- clang/lib/Driver/ToolChains/Arch/Sparc.cpp | 9 ++++++++- clang/test/Driver/freebsd.c | 2 +- clang/test/Driver/linux-as.c | 4 ++-- clang/test/Driver/openbsd.c | 2 +- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/clang/lib/Driver/ToolChains/Arch/Sparc.cpp b/clang/lib/Driver/ToolChains/Arch/Sparc.cpp index 043b7f257c01d3..70ba8eb2a7d0dc 100644 --- a/clang/lib/Driver/ToolChains/Arch/Sparc.cpp +++ b/clang/lib/Driver/ToolChains/Arch/Sparc.cpp @@ -21,12 +21,19 @@ using namespace llvm::opt; const char *sparc::getSparcAsmModeForCPU(StringRef Name, const llvm::Triple &Triple) { if (Triple.getArch() == llvm::Triple::sparcv9) { + const char *DefV9CPU; + + if (Triple.isOSLinux() || Triple.isOSFreeBSD() || Triple.isOSOpenBSD()) + DefV9CPU = "-Av9a"; + else + DefV9CPU = "-Av9"; + return llvm::StringSwitch(Name) .Case("niagara", "-Av9b") .Case("niagara2", "-Av9b") .Case("niagara3", "-Av9d") .Case("niagara4", "-Av9d") - .Default("-Av9"); + .Default(DefV9CPU); } else { return llvm::StringSwitch(Name) .Case("v8", "-Av8") diff --git a/clang/test/Driver/freebsd.c b/clang/test/Driver/freebsd.c index 769bb22da0dc70..1bf6dab802a1c2 100644 --- a/clang/test/Driver/freebsd.c +++ b/clang/test/Driver/freebsd.c @@ -176,7 +176,7 @@ // RUN: %clang -mcpu=ultrasparc -target sparc64-unknown-freebsd8 %s -### -no-integrated-as 2>&1 \ // RUN: | FileCheck --check-prefix=CHECK-SPARC-CPU %s // CHECK-SPARC-CPU: cc1{{.*}}" "-target-cpu" "ultrasparc" -// CHECK-SPARC-CPU: as{{.*}}" "-Av9 +// CHECK-SPARC-CPU: as{{.*}}" "-Av9a // Check that -G flags are passed to the linker for mips // RUN: %clang -target mips-unknown-freebsd %s -### -G0 2>&1 \ diff --git a/clang/test/Driver/linux-as.c b/clang/test/Driver/linux-as.c index 77ac05f30942ca..0959bd7ba0a114 100644 --- a/clang/test/Driver/linux-as.c +++ b/clang/test/Driver/linux-as.c @@ -168,7 +168,7 @@ // RUN: | FileCheck -check-prefix=CHECK-SPARCV9 %s // CHECK-SPARCV9: as // CHECK-SPARCV9: -64 -// CHECK-SPARCV9: -Av9 +// CHECK-SPARCV9: -Av9a // CHECK-SPARCV9-NOT: -KPIC // CHECK-SPARCV9: -o // @@ -177,7 +177,7 @@ // RUN: | FileCheck -check-prefix=CHECK-SPARCV9PIC %s // CHECK-SPARCV9PIC: as // CHECK-SPARCV9PIC: -64 -// CHECK-SPARCV9PIC: -Av9 +// CHECK-SPARCV9PIC: -Av9a // CHECK-SPARCV9PIC: -KPIC // CHECK-SPARCV9PIC: -o // diff --git a/clang/test/Driver/openbsd.c b/clang/test/Driver/openbsd.c index 203b4b4a2ff0f7..ae1aa64416907c 100644 --- a/clang/test/Driver/openbsd.c +++ b/clang/test/Driver/openbsd.c @@ -70,7 +70,7 @@ // RUN: | FileCheck -check-prefix=CHECK-MIPS64EL-PIC %s // CHECK-AMD64-M32: as{{.*}}" "--32" // CHECK-POWERPC: as{{.*}}" "-mppc" "-many" -// CHECK-SPARC64: as{{.*}}" "-64" "-Av9" +// CHECK-SPARC64: as{{.*}}" "-64" "-Av9a" // CHECK-MIPS64: as{{.*}}" "-mabi" "64" "-EB" // CHECK-MIPS64-PIC: as{{.*}}" "-mabi" "64" "-EB" "-KPIC" // CHECK-MIPS64EL: as{{.*}}" "-mabi" "64" "-EL" From b3205e2ace4378600dedba0cc5a42b481c4e22c9 Mon Sep 17 00:00:00 2001 From: Tomas Rix Date: Mon, 7 Sep 2020 09:10:15 +0200 Subject: [PATCH 02/36] [scan-view] Explicitly use utf-8 in send_string send_patched_file decodes with utf-8. The default encoder for python 2 is ascii. So it is necessary to also change send_string to use utf-8. Differential Revision: https://reviews.llvm.org/D83984 --- clang/tools/scan-view/share/ScanView.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/tools/scan-view/share/ScanView.py b/clang/tools/scan-view/share/ScanView.py index a6cc7692ffe003..5a5d15e85b30c5 100644 --- a/clang/tools/scan-view/share/ScanView.py +++ b/clang/tools/scan-view/share/ScanView.py @@ -744,7 +744,7 @@ def send_file(self, f, ctype): return f def send_string(self, s, ctype='text/html', headers=True, mtime=None): - encoded_s = s.encode() + encoded_s = s.encode('utf-8') if headers: self.send_response(200) self.send_header("Content-type", ctype) From 80186e4efc92aaa0c279846a438950c7bbe1e022 Mon Sep 17 00:00:00 2001 From: Raul Tambre Date: Sat, 5 Sep 2020 18:21:05 +0300 Subject: [PATCH 03/36] [CMake][TableGen] Simplify code by using list(TRANSFORM) LLVM requires CMake 3.13.4 so now we can simplify the code. Reviewed By: phosek Differential Revision: https://reviews.llvm.org/D87193 --- llvm/cmake/modules/TableGen.cmake | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/llvm/cmake/modules/TableGen.cmake b/llvm/cmake/modules/TableGen.cmake index 73c1e96d3d9ad8..d58ee1de043fee 100644 --- a/llvm/cmake/modules/TableGen.cmake +++ b/llvm/cmake/modules/TableGen.cmake @@ -80,14 +80,6 @@ function(tablegen project ofn) set(tblgen_change_flag "--write-if-changed") endif() - # With CMake 3.12 this can be reduced to: - # get_directory_property(tblgen_includes "INCLUDE_DIRECTORIES") - # list(TRANSFORM tblgen_includes PREPEND -I) - set(tblgen_includes) - get_directory_property(includes "INCLUDE_DIRECTORIES") - foreach(include ${includes}) - list(APPEND tblgen_includes -I ${include}) - endforeach() # We need both _TABLEGEN_TARGET and _TABLEGEN_EXE in the DEPENDS list # (both the target and the file) to have .inc files rebuilt on # a tablegen change, as cmake does not propagate file-level dependencies @@ -97,6 +89,9 @@ function(tablegen project ofn) # dependency twice in the result file when # ("${${project}_TABLEGEN_TARGET}" STREQUAL "${${project}_TABLEGEN_EXE}") # but lets us having smaller and cleaner code here. + get_directory_property(tblgen_includes INCLUDE_DIRECTORIES) + list(TRANSFORM tblgen_includes PREPEND -I) + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ofn} COMMAND ${${project}_TABLEGEN_EXE} ${ARGN} -I ${CMAKE_CURRENT_SOURCE_DIR} ${tblgen_includes} From 098130fa403a82f2a425761bbccdede022fac3ff Mon Sep 17 00:00:00 2001 From: Raul Tambre Date: Sat, 5 Sep 2020 18:15:32 +0300 Subject: [PATCH 04/36] [CMake][Polly] Remove dead CMake code LLVM requires CMake 3.13.4 so remove code behind checks for an older version. Reviewed By: phosek Differential Revision: https://reviews.llvm.org/D87192 --- polly/cmake/polly_macros.cmake | 15 --------------- polly/lib/External/CMakeLists.txt | 3 +-- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/polly/cmake/polly_macros.cmake b/polly/cmake/polly_macros.cmake index 86de6f10686ebc..518a09b45a4205 100644 --- a/polly/cmake/polly_macros.cmake +++ b/polly/cmake/polly_macros.cmake @@ -72,21 +72,6 @@ macro(add_polly_loadable_module name) endif() endmacro(add_polly_loadable_module) -# Use C99-compatible compile mode for all C source files of a target. -function(target_enable_c99 _target) - if(CMAKE_VERSION VERSION_GREATER "3.1") - set_target_properties("${_target}" PROPERTIES C_STANDARD 99) - elseif(CMAKE_COMPILER_IS_GNUCC) - get_target_property(_sources "${_target}" SOURCES) - foreach(_file IN LISTS _sources) - get_source_file_property(_lang "${_file}" LANGUAGE) - if(_lang STREQUAL "C") - set_source_files_properties(${_file} COMPILE_FLAGS "-std=gnu99") - endif() - endforeach() - endif() -endfunction() - # Recursive helper for setup_source_group. Traverse the file system and add # source files matching the glob_expr to the prefix, recursing into # subdirectories as they are encountered diff --git a/polly/lib/External/CMakeLists.txt b/polly/lib/External/CMakeLists.txt index 1039079cb49ca8..c953ea48475d73 100644 --- a/polly/lib/External/CMakeLists.txt +++ b/polly/lib/External/CMakeLists.txt @@ -293,8 +293,7 @@ if (POLLY_BUNDLED_ISL) ) # ISL requires at least C99 to compile. gcc < 5.0 use -std=gnu89 as default. - target_enable_c99(PollyISL) - target_enable_c99(polly-isl-test) + set_property(TARGET PollyISL polly-isl-test PROPERTY C_STANDARD 99) endif (POLLY_BUNDLED_ISL) set(PET_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/pet") From 21c0e74c9e7fa33153c484a6dabf33b38aede0d1 Mon Sep 17 00:00:00 2001 From: Raul Tambre Date: Sat, 5 Sep 2020 18:11:34 +0300 Subject: [PATCH 05/36] [CMake][OpenMP] Remove old dead CMake code LLVM requires CMake 3.13.4 so remove code behind checks for an older version. Reviewed By: phosek Differential Revision: https://reviews.llvm.org/D87191 --- openmp/cmake/OpenMPTesting.cmake | 9 +--- .../cmake/LibompCheckFortranFlag.cmake | 47 +------------------ 2 files changed, 2 insertions(+), 54 deletions(-) diff --git a/openmp/cmake/OpenMPTesting.cmake b/openmp/cmake/OpenMPTesting.cmake index 90e0704c4a94c5..1d46b141ffdf82 100644 --- a/openmp/cmake/OpenMPTesting.cmake +++ b/openmp/cmake/OpenMPTesting.cmake @@ -50,13 +50,6 @@ endfunction() if (${OPENMP_STANDALONE_BUILD}) find_standalone_test_dependencies() - # Make sure we can use the console pool for recent CMake and Ninja > 1.5. - if (CMAKE_VERSION VERSION_LESS 3.1.20141117) - set(cmake_3_2_USES_TERMINAL) - else() - set(cmake_3_2_USES_TERMINAL USES_TERMINAL) - endif() - # Set lit arguments. set(DEFAULT_LIT_ARGS "-sv --show-unsupported --show-xfail") if (MSVC OR XCODE) @@ -189,7 +182,7 @@ function(add_openmp_testsuite target comment) COMMAND ${PYTHON_EXECUTABLE} ${OPENMP_LLVM_LIT_EXECUTABLE} ${LIT_ARGS} ${ARG_UNPARSED_ARGUMENTS} COMMENT ${comment} DEPENDS ${ARG_DEPENDS} - ${cmake_3_2_USES_TERMINAL} + USES_TERMINAL ) else() if (ARG_EXCLUDE_FROM_CHECK_ALL) diff --git a/openmp/runtime/cmake/LibompCheckFortranFlag.cmake b/openmp/runtime/cmake/LibompCheckFortranFlag.cmake index 21837ef068e36f..b8cdb28a4bf28b 100644 --- a/openmp/runtime/cmake/LibompCheckFortranFlag.cmake +++ b/openmp/runtime/cmake/LibompCheckFortranFlag.cmake @@ -19,54 +19,9 @@ function(libomp_check_fortran_flag flag boolean) print *, \"Hello World!\" end program hello") - set(failed_regexes "[Ee]rror;[Uu]nknown;[Ss]kipping") - if(CMAKE_VERSION VERSION_GREATER 3.1 OR CMAKE_VERSION VERSION_EQUAL 3.1) + set(failed_regexes "[Ee]rror;[Uu]nknown;[Ss]kipping") include(CheckFortranSourceCompiles) check_fortran_source_compiles("${fortran_source}" ${boolean} FAIL_REGEX "${failed_regexes}") set(${boolean} ${${boolean}} PARENT_SCOPE) - return() - else() - # Our manual check for cmake versions that don't have CheckFortranSourceCompiles - set(base_dir ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/fortran_flag_check) - file(MAKE_DIRECTORY ${base_dir}) - file(WRITE ${base_dir}/fortran_source.f "${fortran_source}") - - message(STATUS "Performing Test ${boolean}") - execute_process( - COMMAND ${CMAKE_Fortran_COMPILER} "${flag}" ${base_dir}/fortran_source.f - WORKING_DIRECTORY ${base_dir} - RESULT_VARIABLE exit_code - OUTPUT_VARIABLE OUTPUT - ERROR_VARIABLE OUTPUT - ) - - if(${exit_code} EQUAL 0) - foreach(regex IN LISTS failed_regexes) - if("${OUTPUT}" MATCHES ${regex}) - set(retval FALSE) - endif() - endforeach() - else() - set(retval FALSE) - endif() - - if(${retval}) - set(${boolean} 1 CACHE INTERNAL "Test ${boolean}") - message(STATUS "Performing Test ${boolean} - Success") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeOutput.log - "Performing Fortran Compiler Flag test ${boolean} succeeded with the following output:\n" - "${OUTPUT}\n" - "Source file was:\n${fortran_source}\n") - else() - set(${boolean} "" CACHE INTERNAL "Test ${boolean}") - message(STATUS "Performing Test ${boolean} - Failed") - file(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log - "Performing Fortran Compiler Flag test ${boolean} failed with the following output:\n" - "${OUTPUT}\n" - "Source file was:\n${fortran_source}\n") - endif() - endif() - - set(${boolean} ${retval} PARENT_SCOPE) endif() endfunction() From f4835b94f2cfc89e430263d1807b118e0e937f4d Mon Sep 17 00:00:00 2001 From: Raul Tambre Date: Sat, 5 Sep 2020 18:05:14 +0300 Subject: [PATCH 06/36] [CMake][TableGen] Remove dead CMake version checks LLVM requires CMake 3.13.4, so remove version checks that are dead code. Reviewed By: phosek Differential Revision: https://reviews.llvm.org/D87190 --- llvm/cmake/modules/TableGen.cmake | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/llvm/cmake/modules/TableGen.cmake b/llvm/cmake/modules/TableGen.cmake index d58ee1de043fee..5f07acc1f6922a 100644 --- a/llvm/cmake/modules/TableGen.cmake +++ b/llvm/cmake/modules/TableGen.cmake @@ -8,9 +8,8 @@ function(tablegen project ofn) message(FATAL_ERROR "${project}_TABLEGEN_EXE not set") endif() - # Use depfile instead of globbing arbitrary *.td(s) - # DEPFILE is available for Ninja Generator with CMake>=3.7. - if(CMAKE_GENERATOR STREQUAL "Ninja" AND NOT CMAKE_VERSION VERSION_LESS 3.7) + # Use depfile instead of globbing arbitrary *.td(s) for Ninja. + if(CMAKE_GENERATOR STREQUAL "Ninja") # Make output path relative to build.ninja, assuming located on # ${CMAKE_BINARY_DIR}. # CMake emits build targets as relative paths but Ninja doesn't identify @@ -134,8 +133,8 @@ macro(add_tablegen target project) set(${target}_OLD_LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS}) set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} TableGen) - # CMake-3.9 doesn't let compilation units depend on their dependent libraries. - if(NOT (CMAKE_GENERATOR STREQUAL "Ninja" AND NOT CMAKE_VERSION VERSION_LESS 3.9) AND NOT XCODE) + # CMake doesn't let compilation units depend on their dependent libraries on some generators. + if(NOT CMAKE_GENERATOR STREQUAL "Ninja" AND NOT XCODE) # FIXME: It leaks to user, callee of add_tablegen. set(LLVM_ENABLE_OBJLIB ON) endif() From 04ea680a8ccc4f9a4d7333cd712333960348c35b Mon Sep 17 00:00:00 2001 From: Kristina Bessonova Date: Mon, 7 Sep 2020 10:03:32 +0200 Subject: [PATCH 07/36] [cmake] Fix build of attribute plugin example on Windows Seems '${cmake_2_8_12_PRIVATE}' was removed a long time ago, so it should be just PRIVATE keyword here. Reviewed By: john.brawn Differential Revision: https://reviews.llvm.org/D86091 --- clang/examples/Attribute/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/examples/Attribute/CMakeLists.txt b/clang/examples/Attribute/CMakeLists.txt index ed02f5e5992f5a..42f04f5039bc75 100644 --- a/clang/examples/Attribute/CMakeLists.txt +++ b/clang/examples/Attribute/CMakeLists.txt @@ -1,7 +1,7 @@ add_llvm_library(Attribute MODULE Attribute.cpp PLUGIN_TOOL clang) if(LLVM_ENABLE_PLUGINS AND (WIN32 OR CYGWIN)) - target_link_libraries(Attribute ${cmake_2_8_12_PRIVATE} + target_link_libraries(Attribute PRIVATE clangAST clangBasic clangFrontend From 5350e1b5096aa4707aa525baf7398d93b4a4f1a5 Mon Sep 17 00:00:00 2001 From: Jay Foad Date: Wed, 2 Sep 2020 16:01:48 +0100 Subject: [PATCH 08/36] [KnownBits] Implement accurate unsigned and signed max and min Use the new implementation in ValueTracking, SelectionDAG and GlobalISel. Differential Revision: https://reviews.llvm.org/D87034 --- llvm/include/llvm/Support/KnownBits.h | 16 +++++ llvm/lib/Analysis/ValueTracking.cpp | 70 +++++++------------ .../lib/CodeGen/GlobalISel/GISelKnownBits.cpp | 36 +++++----- .../lib/CodeGen/SelectionDAG/SelectionDAG.cpp | 27 ++----- llvm/lib/Support/KnownBits.cpp | 62 ++++++++++++++++ .../CodeGen/GlobalISel/KnownBitsTest.cpp | 4 +- llvm/unittests/Support/KnownBitsTest.cpp | 44 ++++++++++-- 7 files changed, 169 insertions(+), 90 deletions(-) diff --git a/llvm/include/llvm/Support/KnownBits.h b/llvm/include/llvm/Support/KnownBits.h index 5b3de63cd359a3..a29e150b904a31 100644 --- a/llvm/include/llvm/Support/KnownBits.h +++ b/llvm/include/llvm/Support/KnownBits.h @@ -173,6 +173,10 @@ struct KnownBits { One.extractBits(NumBits, BitPosition)); } + /// Return KnownBits based on this, but updated given that the underlying + /// value is known to be greater than or equal to Val. + KnownBits makeGE(const APInt &Val) const; + /// Returns the minimum number of trailing zero bits. unsigned countMinTrailingZeros() const { return Zero.countTrailingOnes(); @@ -241,6 +245,18 @@ struct KnownBits { static KnownBits computeForAddSub(bool Add, bool NSW, const KnownBits &LHS, KnownBits RHS); + /// Compute known bits for umax(LHS, RHS). + static KnownBits umax(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for umin(LHS, RHS). + static KnownBits umin(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for smax(LHS, RHS). + static KnownBits smax(const KnownBits &LHS, const KnownBits &RHS); + + /// Compute known bits for smin(LHS, RHS). + static KnownBits smin(const KnownBits &LHS, const KnownBits &RHS); + /// Insert the bits from a smaller known bits starting at bitPosition. void insertBits(const KnownBits &SubBits, unsigned BitPosition) { Zero.insertBits(SubBits.Zero, BitPosition); diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp index 3a6ee355c646d0..6e5a7195bb1943 100644 --- a/llvm/lib/Analysis/ValueTracking.cpp +++ b/llvm/lib/Analysis/ValueTracking.cpp @@ -1212,59 +1212,41 @@ static void computeKnownBitsFromOperator(const Operator *I, if (SelectPatternResult::isMinOrMax(SPF)) { computeKnownBits(RHS, Known, Depth + 1, Q); computeKnownBits(LHS, Known2, Depth + 1, Q); - } else { - computeKnownBits(I->getOperand(2), Known, Depth + 1, Q); - computeKnownBits(I->getOperand(1), Known2, Depth + 1, Q); + switch (SPF) { + default: + llvm_unreachable("Unhandled select pattern flavor!"); + case SPF_SMAX: + Known = KnownBits::smax(Known, Known2); + break; + case SPF_SMIN: + Known = KnownBits::smin(Known, Known2); + break; + case SPF_UMAX: + Known = KnownBits::umax(Known, Known2); + break; + case SPF_UMIN: + Known = KnownBits::umin(Known, Known2); + break; + } + break; } - unsigned MaxHighOnes = 0; - unsigned MaxHighZeros = 0; - if (SPF == SPF_SMAX) { - // If both sides are negative, the result is negative. - if (Known.isNegative() && Known2.isNegative()) - // We can derive a lower bound on the result by taking the max of the - // leading one bits. - MaxHighOnes = - std::max(Known.countMinLeadingOnes(), Known2.countMinLeadingOnes()); - // If either side is non-negative, the result is non-negative. - else if (Known.isNonNegative() || Known2.isNonNegative()) - MaxHighZeros = 1; - } else if (SPF == SPF_SMIN) { - // If both sides are non-negative, the result is non-negative. - if (Known.isNonNegative() && Known2.isNonNegative()) - // We can derive an upper bound on the result by taking the max of the - // leading zero bits. - MaxHighZeros = std::max(Known.countMinLeadingZeros(), - Known2.countMinLeadingZeros()); - // If either side is negative, the result is negative. - else if (Known.isNegative() || Known2.isNegative()) - MaxHighOnes = 1; - } else if (SPF == SPF_UMAX) { - // We can derive a lower bound on the result by taking the max of the - // leading one bits. - MaxHighOnes = - std::max(Known.countMinLeadingOnes(), Known2.countMinLeadingOnes()); - } else if (SPF == SPF_UMIN) { - // We can derive an upper bound on the result by taking the max of the - // leading zero bits. - MaxHighZeros = - std::max(Known.countMinLeadingZeros(), Known2.countMinLeadingZeros()); - } else if (SPF == SPF_ABS) { + computeKnownBits(I->getOperand(2), Known, Depth + 1, Q); + computeKnownBits(I->getOperand(1), Known2, Depth + 1, Q); + + // Only known if known in both the LHS and RHS. + Known.One &= Known2.One; + Known.Zero &= Known2.Zero; + + if (SPF == SPF_ABS) { // RHS from matchSelectPattern returns the negation part of abs pattern. // If the negate has an NSW flag we can assume the sign bit of the result // will be 0 because that makes abs(INT_MIN) undefined. if (match(RHS, m_Neg(m_Specific(LHS))) && Q.IIQ.hasNoSignedWrap(cast(RHS))) - MaxHighZeros = 1; + Known.Zero.setSignBit(); } - // Only known if known in both the LHS and RHS. - Known.One &= Known2.One; - Known.Zero &= Known2.Zero; - if (MaxHighOnes > 0) - Known.One.setHighBits(MaxHighOnes); - if (MaxHighZeros > 0) - Known.Zero.setHighBits(MaxHighZeros); break; } case Instruction::FPTrunc: diff --git a/llvm/lib/CodeGen/GlobalISel/GISelKnownBits.cpp b/llvm/lib/CodeGen/GlobalISel/GISelKnownBits.cpp index c615462af407e4..3ebbac9fd659aa 100644 --- a/llvm/lib/CodeGen/GlobalISel/GISelKnownBits.cpp +++ b/llvm/lib/CodeGen/GlobalISel/GISelKnownBits.cpp @@ -308,11 +308,24 @@ void GISelKnownBits::computeKnownBitsImpl(Register R, KnownBits &Known, Known, DemandedElts, Depth + 1); break; } - case TargetOpcode::G_SMIN: + case TargetOpcode::G_SMIN: { + // TODO: Handle clamp pattern with number of sign bits + KnownBits KnownRHS; + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known, DemandedElts, + Depth + 1); + computeKnownBitsImpl(MI.getOperand(2).getReg(), KnownRHS, DemandedElts, + Depth + 1); + Known = KnownBits::smin(Known, KnownRHS); + break; + } case TargetOpcode::G_SMAX: { // TODO: Handle clamp pattern with number of sign bits - computeKnownBitsMin(MI.getOperand(1).getReg(), MI.getOperand(2).getReg(), - Known, DemandedElts, Depth + 1); + KnownBits KnownRHS; + computeKnownBitsImpl(MI.getOperand(1).getReg(), Known, DemandedElts, + Depth + 1); + computeKnownBitsImpl(MI.getOperand(2).getReg(), KnownRHS, DemandedElts, + Depth + 1); + Known = KnownBits::smax(Known, KnownRHS); break; } case TargetOpcode::G_UMIN: { @@ -321,13 +334,7 @@ void GISelKnownBits::computeKnownBitsImpl(Register R, KnownBits &Known, DemandedElts, Depth + 1); computeKnownBitsImpl(MI.getOperand(2).getReg(), KnownRHS, DemandedElts, Depth + 1); - - // UMIN - we know that the result will have the maximum of the - // known zero leading bits of the inputs. - unsigned LeadZero = Known.countMinLeadingZeros(); - LeadZero = std::max(LeadZero, KnownRHS.countMinLeadingZeros()); - Known &= KnownRHS; - Known.Zero.setHighBits(LeadZero); + Known = KnownBits::umin(Known, KnownRHS); break; } case TargetOpcode::G_UMAX: { @@ -336,14 +343,7 @@ void GISelKnownBits::computeKnownBitsImpl(Register R, KnownBits &Known, DemandedElts, Depth + 1); computeKnownBitsImpl(MI.getOperand(2).getReg(), KnownRHS, DemandedElts, Depth + 1); - - // UMAX - we know that the result will have the maximum of the - // known one leading bits of the inputs. - unsigned LeadOne = Known.countMinLeadingOnes(); - LeadOne = std::max(LeadOne, KnownRHS.countMinLeadingOnes()); - Known.Zero &= KnownRHS.Zero; - Known.One &= KnownRHS.One; - Known.One.setHighBits(LeadOne); + Known = KnownBits::umax(Known, KnownRHS); break; } case TargetOpcode::G_FCMP: diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp index 73e042c475402c..d2b3e009c20264 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -3390,29 +3390,13 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts, case ISD::UMIN: { Known = computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1); Known2 = computeKnownBits(Op.getOperand(1), DemandedElts, Depth + 1); - - // UMIN - we know that the result will have the maximum of the - // known zero leading bits of the inputs. - unsigned LeadZero = Known.countMinLeadingZeros(); - LeadZero = std::max(LeadZero, Known2.countMinLeadingZeros()); - - Known.Zero &= Known2.Zero; - Known.One &= Known2.One; - Known.Zero.setHighBits(LeadZero); + Known = KnownBits::umin(Known, Known2); break; } case ISD::UMAX: { Known = computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1); Known2 = computeKnownBits(Op.getOperand(1), DemandedElts, Depth + 1); - - // UMAX - we know that the result will have the maximum of the - // known one leading bits of the inputs. - unsigned LeadOne = Known.countMinLeadingOnes(); - LeadOne = std::max(LeadOne, Known2.countMinLeadingOnes()); - - Known.Zero &= Known2.Zero; - Known.One &= Known2.One; - Known.One.setHighBits(LeadOne); + Known = KnownBits::umax(Known, Known2); break; } case ISD::SMIN: @@ -3446,12 +3430,13 @@ KnownBits SelectionDAG::computeKnownBits(SDValue Op, const APInt &DemandedElts, } } - // Fallback - just get the shared known bits of the operands. Known = computeKnownBits(Op.getOperand(0), DemandedElts, Depth + 1); if (Known.isUnknown()) break; // Early-out Known2 = computeKnownBits(Op.getOperand(1), DemandedElts, Depth + 1); - Known.Zero &= Known2.Zero; - Known.One &= Known2.One; + if (IsMax) + Known = KnownBits::smax(Known, Known2); + else + Known = KnownBits::smin(Known, Known2); break; } case ISD::FrameIndex: diff --git a/llvm/lib/Support/KnownBits.cpp b/llvm/lib/Support/KnownBits.cpp index 1ff66d504cbeaa..aad50e1240341d 100644 --- a/llvm/lib/Support/KnownBits.cpp +++ b/llvm/lib/Support/KnownBits.cpp @@ -83,6 +83,68 @@ KnownBits KnownBits::computeForAddSub(bool Add, bool NSW, return KnownOut; } +KnownBits KnownBits::makeGE(const APInt &Val) const { + // Count the number of leading bit positions where our underlying value is + // known to be less than or equal to Val. + unsigned N = (Zero | Val).countLeadingOnes(); + + // For each of those bit positions, if Val has a 1 in that bit then our + // underlying value must also have a 1. + APInt MaskedVal(Val); + MaskedVal.clearLowBits(getBitWidth() - N); + return KnownBits(Zero, One | MaskedVal); +} + +KnownBits KnownBits::umax(const KnownBits &LHS, const KnownBits &RHS) { + // If we can prove that LHS >= RHS then use LHS as the result. Likewise for + // RHS. Ideally our caller would already have spotted these cases and + // optimized away the umax operation, but we handle them here for + // completeness. + if (LHS.getMinValue().uge(RHS.getMaxValue())) + return LHS; + if (RHS.getMinValue().uge(LHS.getMaxValue())) + return RHS; + + // If the result of the umax is LHS then it must be greater than or equal to + // the minimum possible value of RHS. Likewise for RHS. Any known bits that + // are common to these two values are also known in the result. + KnownBits L = LHS.makeGE(RHS.getMinValue()); + KnownBits R = RHS.makeGE(LHS.getMinValue()); + return KnownBits(L.Zero & R.Zero, L.One & R.One); +} + +KnownBits KnownBits::umin(const KnownBits &LHS, const KnownBits &RHS) { + // Flip the range of values: [0, 0xFFFFFFFF] <-> [0xFFFFFFFF, 0] + auto Flip = [](KnownBits Val) { return KnownBits(Val.One, Val.Zero); }; + return Flip(umax(Flip(LHS), Flip(RHS))); +} + +KnownBits KnownBits::smax(const KnownBits &LHS, const KnownBits &RHS) { + // Flip the range of values: [-0x80000000, 0x7FFFFFFF] <-> [0, 0xFFFFFFFF] + auto Flip = [](KnownBits Val) { + unsigned SignBitPosition = Val.getBitWidth() - 1; + APInt Zero = Val.Zero; + APInt One = Val.One; + Zero.setBitVal(SignBitPosition, Val.One[SignBitPosition]); + One.setBitVal(SignBitPosition, Val.Zero[SignBitPosition]); + return KnownBits(Zero, One); + }; + return Flip(umax(Flip(LHS), Flip(RHS))); +} + +KnownBits KnownBits::smin(const KnownBits &LHS, const KnownBits &RHS) { + // Flip the range of values: [-0x80000000, 0x7FFFFFFF] <-> [0xFFFFFFFF, 0] + auto Flip = [](KnownBits Val) { + unsigned SignBitPosition = Val.getBitWidth() - 1; + APInt Zero = Val.One; + APInt One = Val.Zero; + Zero.setBitVal(SignBitPosition, Val.Zero[SignBitPosition]); + One.setBitVal(SignBitPosition, Val.One[SignBitPosition]); + return KnownBits(Zero, One); + }; + return Flip(umax(Flip(LHS), Flip(RHS))); +} + KnownBits &KnownBits::operator&=(const KnownBits &RHS) { // Result bit is 0 if either operand bit is 0. Zero |= RHS.Zero; diff --git a/llvm/unittests/CodeGen/GlobalISel/KnownBitsTest.cpp b/llvm/unittests/CodeGen/GlobalISel/KnownBitsTest.cpp index 30ff37536fafc5..faf6f7087ac0cd 100644 --- a/llvm/unittests/CodeGen/GlobalISel/KnownBitsTest.cpp +++ b/llvm/unittests/CodeGen/GlobalISel/KnownBitsTest.cpp @@ -719,9 +719,9 @@ TEST_F(AArch64GISelMITest, TestKnownBitsUMax) { KnownBits KnownUmax = Info.getKnownBits(CopyUMax); EXPECT_EQ(64u, KnownUmax.getBitWidth()); - EXPECT_EQ(0u, KnownUmax.Zero.getZExtValue()); + EXPECT_EQ(0xffu, KnownUmax.Zero.getZExtValue()); EXPECT_EQ(0xffffffffffffff00, KnownUmax.One.getZExtValue()); - EXPECT_EQ(0u, KnownUmax.Zero.getZExtValue()); + EXPECT_EQ(0xffu, KnownUmax.Zero.getZExtValue()); EXPECT_EQ(0xffffffffffffff00, KnownUmax.One.getZExtValue()); } diff --git a/llvm/unittests/Support/KnownBitsTest.cpp b/llvm/unittests/Support/KnownBitsTest.cpp index 694e5c4dcc7128..89555a5881a53a 100644 --- a/llvm/unittests/Support/KnownBitsTest.cpp +++ b/llvm/unittests/Support/KnownBitsTest.cpp @@ -103,13 +103,15 @@ TEST(KnownBitsTest, BinaryExhaustive) { unsigned Bits = 4; ForeachKnownBits(Bits, [&](const KnownBits &Known1) { ForeachKnownBits(Bits, [&](const KnownBits &Known2) { - KnownBits KnownAnd(Bits), KnownOr(Bits), KnownXor(Bits); + KnownBits KnownAnd(Bits); KnownAnd.Zero.setAllBits(); KnownAnd.One.setAllBits(); - KnownOr.Zero.setAllBits(); - KnownOr.One.setAllBits(); - KnownXor.Zero.setAllBits(); - KnownXor.One.setAllBits(); + KnownBits KnownOr(KnownAnd); + KnownBits KnownXor(KnownAnd); + KnownBits KnownUMax(KnownAnd); + KnownBits KnownUMin(KnownAnd); + KnownBits KnownSMax(KnownAnd); + KnownBits KnownSMin(KnownAnd); ForeachNumInKnownBits(Known1, [&](const APInt &N1) { ForeachNumInKnownBits(Known2, [&](const APInt &N2) { @@ -126,6 +128,22 @@ TEST(KnownBitsTest, BinaryExhaustive) { Res = N1 ^ N2; KnownXor.One &= Res; KnownXor.Zero &= ~Res; + + Res = APIntOps::umax(N1, N2); + KnownUMax.One &= Res; + KnownUMax.Zero &= ~Res; + + Res = APIntOps::umin(N1, N2); + KnownUMin.One &= Res; + KnownUMin.Zero &= ~Res; + + Res = APIntOps::smax(N1, N2); + KnownSMax.One &= Res; + KnownSMax.Zero &= ~Res; + + Res = APIntOps::smin(N1, N2); + KnownSMin.One &= Res; + KnownSMin.Zero &= ~Res; }); }); @@ -140,6 +158,22 @@ TEST(KnownBitsTest, BinaryExhaustive) { KnownBits ComputedXor = Known1 ^ Known2; EXPECT_EQ(KnownXor.Zero, ComputedXor.Zero); EXPECT_EQ(KnownXor.One, ComputedXor.One); + + KnownBits ComputedUMax = KnownBits::umax(Known1, Known2); + EXPECT_EQ(KnownUMax.Zero, ComputedUMax.Zero); + EXPECT_EQ(KnownUMax.One, ComputedUMax.One); + + KnownBits ComputedUMin = KnownBits::umin(Known1, Known2); + EXPECT_EQ(KnownUMin.Zero, ComputedUMin.Zero); + EXPECT_EQ(KnownUMin.One, ComputedUMin.One); + + KnownBits ComputedSMax = KnownBits::smax(Known1, Known2); + EXPECT_EQ(KnownSMax.Zero, ComputedSMax.Zero); + EXPECT_EQ(KnownSMax.One, ComputedSMax.One); + + KnownBits ComputedSMin = KnownBits::smin(Known1, Known2); + EXPECT_EQ(KnownSMin.Zero, ComputedSMin.Zero); + EXPECT_EQ(KnownSMin.One, ComputedSMin.One); }); }); } From a98b126696ef8edc42d193d2e03048cd0d61ebc2 Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Mon, 7 Sep 2020 10:14:22 +0200 Subject: [PATCH 09/36] Add BinaryFormat/ELFRelocs/CSKY.def to LLVM modulemap --- llvm/include/llvm/module.modulemap | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/include/llvm/module.modulemap b/llvm/include/llvm/module.modulemap index 22959c62fc81e2..b1d0a703850b98 100644 --- a/llvm/include/llvm/module.modulemap +++ b/llvm/include/llvm/module.modulemap @@ -65,6 +65,7 @@ module LLVM_BinaryFormat { textual header "BinaryFormat/ELFRelocs/ARC.def" textual header "BinaryFormat/ELFRelocs/AVR.def" textual header "BinaryFormat/ELFRelocs/BPF.def" + textual header "BinaryFormat/ELFRelocs/CSKY.def" textual header "BinaryFormat/ELFRelocs/Hexagon.def" textual header "BinaryFormat/ELFRelocs/i386.def" textual header "BinaryFormat/ELFRelocs/Lanai.def" From 40f4131fce787fe7a8596f06cef5fb6a06bf5ded Mon Sep 17 00:00:00 2001 From: Xing GUO Date: Mon, 7 Sep 2020 16:16:38 +0800 Subject: [PATCH 10/36] [DWARFYAML] Make the debug_addr section optional. This patch makes the debug_addr section optional. When an empty debug_addr section is specified, yaml2obj only emits a section header for it. Reviewed By: jhenderson Differential Revision: https://reviews.llvm.org/D87205 --- llvm/include/llvm/ObjectYAML/DWARFYAML.h | 2 +- llvm/lib/ObjectYAML/DWARFEmitter.cpp | 2 +- llvm/lib/ObjectYAML/DWARFYAML.cpp | 2 +- .../test/tools/yaml2obj/ELF/DWARF/debug-addr.yaml | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/ObjectYAML/DWARFYAML.h b/llvm/include/llvm/ObjectYAML/DWARFYAML.h index 19b7f3500ee67f..99a7af87d2c78d 100644 --- a/llvm/include/llvm/ObjectYAML/DWARFYAML.h +++ b/llvm/include/llvm/ObjectYAML/DWARFYAML.h @@ -215,7 +215,7 @@ struct Data { Optional> DebugStrOffsets; Optional> DebugAranges; std::vector DebugRanges; - std::vector DebugAddr; + Optional> DebugAddr; Optional PubNames; Optional PubTypes; diff --git a/llvm/lib/ObjectYAML/DWARFEmitter.cpp b/llvm/lib/ObjectYAML/DWARFEmitter.cpp index a0a445ae0c9db0..bf29f40579ceb2 100644 --- a/llvm/lib/ObjectYAML/DWARFEmitter.cpp +++ b/llvm/lib/ObjectYAML/DWARFEmitter.cpp @@ -594,7 +594,7 @@ Error DWARFYAML::emitDebugLine(raw_ostream &OS, const DWARFYAML::Data &DI) { } Error DWARFYAML::emitDebugAddr(raw_ostream &OS, const Data &DI) { - for (const AddrTableEntry &TableEntry : DI.DebugAddr) { + for (const AddrTableEntry &TableEntry : *DI.DebugAddr) { uint8_t AddrSize; if (TableEntry.AddrSize) AddrSize = *TableEntry.AddrSize; diff --git a/llvm/lib/ObjectYAML/DWARFYAML.cpp b/llvm/lib/ObjectYAML/DWARFYAML.cpp index 046dddbf9a3978..353e5058a0e5df 100644 --- a/llvm/lib/ObjectYAML/DWARFYAML.cpp +++ b/llvm/lib/ObjectYAML/DWARFYAML.cpp @@ -32,7 +32,7 @@ SetVector DWARFYAML::Data::getNonEmptySectionNames() const { SecNames.insert("debug_ranges"); if (!DebugLines.empty()) SecNames.insert("debug_line"); - if (!DebugAddr.empty()) + if (DebugAddr) SecNames.insert("debug_addr"); if (!DebugAbbrev.empty()) SecNames.insert("debug_abbrev"); diff --git a/llvm/test/tools/yaml2obj/ELF/DWARF/debug-addr.yaml b/llvm/test/tools/yaml2obj/ELF/DWARF/debug-addr.yaml index 52841e167b4471..6a8dc84d98aa78 100644 --- a/llvm/test/tools/yaml2obj/ELF/DWARF/debug-addr.yaml +++ b/llvm/test/tools/yaml2obj/ELF/DWARF/debug-addr.yaml @@ -631,3 +631,18 @@ DWARF: [[SIZENAME]]: 3 Entries: - Address: 0x1234 + +## n) Test that the .debug_addr section header is emitted if the "debug_addr" +## entry is empty. + +# RUN: yaml2obj --docnum=12 %s -o %t12.o +# RUN: llvm-readobj --sections %t12.o | \ +# RUN: FileCheck %s -DSIZE=0 -DADDRALIGN=1 --check-prefix=SHDR + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +DWARF: + debug_addr: [] From 0af4147804aa0aa906a2ac913fe5639639afb9bb Mon Sep 17 00:00:00 2001 From: Sam Parker Date: Tue, 25 Aug 2020 12:17:24 +0100 Subject: [PATCH 11/36] [ARM][CostModel] CodeSize costs for i1 arith ops When optimising for size, make the cost of i1 logical operations relatively expensive so that optimisations don't try to combine predicates. Differential Revision: https://reviews.llvm.org/D86525 --- llvm/lib/Target/ARM/ARMTargetTransformInfo.cpp | 17 ++++++++++++++++- llvm/test/Analysis/CostModel/ARM/arith.ll | 6 +++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/ARM/ARMTargetTransformInfo.cpp b/llvm/lib/Target/ARM/ARMTargetTransformInfo.cpp index f3206306a3b60f..c789b35f32af52 100644 --- a/llvm/lib/Target/ARM/ARMTargetTransformInfo.cpp +++ b/llvm/lib/Target/ARM/ARMTargetTransformInfo.cpp @@ -1039,13 +1039,28 @@ int ARMTTIImpl::getArithmeticInstrCost(unsigned Opcode, Type *Ty, TTI::OperandValueProperties Opd2PropInfo, ArrayRef Args, const Instruction *CxtI) { + int ISDOpcode = TLI->InstructionOpcodeToISD(Opcode); + if (ST->isThumb() && CostKind == TTI::TCK_CodeSize && Ty->isIntegerTy(1)) { + // Make operations on i1 relatively expensive as this often involves + // combining predicates. AND and XOR should be easier to handle with IT + // blocks. + switch (ISDOpcode) { + default: + break; + case ISD::AND: + case ISD::XOR: + return 2; + case ISD::OR: + return 3; + } + } + // TODO: Handle more cost kinds. if (CostKind != TTI::TCK_RecipThroughput) return BaseT::getArithmeticInstrCost(Opcode, Ty, CostKind, Op1Info, Op2Info, Opd1PropInfo, Opd2PropInfo, Args, CxtI); - int ISDOpcode = TLI->InstructionOpcodeToISD(Opcode); std::pair LT = TLI->getTypeLegalizationCost(DL, Ty); if (ST->hasNEON()) { diff --git a/llvm/test/Analysis/CostModel/ARM/arith.ll b/llvm/test/Analysis/CostModel/ARM/arith.ll index 8513cefe5c119a..a1a0d11cac2ec5 100644 --- a/llvm/test/Analysis/CostModel/ARM/arith.ll +++ b/llvm/test/Analysis/CostModel/ARM/arith.ll @@ -63,9 +63,9 @@ define void @i1() { ; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 1 for instruction: %f = ashr i1 undef, undef ; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 1 for instruction: %g = lshr i1 undef, undef ; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 1 for instruction: %h = shl i1 undef, undef -; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 1 for instruction: %i = and i1 undef, undef -; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 1 for instruction: %j = or i1 undef, undef -; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 1 for instruction: %k = xor i1 undef, undef +; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 2 for instruction: %i = and i1 undef, undef +; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 3 for instruction: %j = or i1 undef, undef +; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 2 for instruction: %k = xor i1 undef, undef ; CHECK-MVE-SIZE-NEXT: Cost Model: Found an estimated cost of 1 for instruction: ret void ; %c = add i1 undef, undef From 713c2ad60c137a88c0a64cc98f2db4be702a25e9 Mon Sep 17 00:00:00 2001 From: Jay Foad Date: Thu, 27 Aug 2020 14:26:38 +0100 Subject: [PATCH 12/36] [GlobalISel] Extend not_cmp_fold to work on conditional expressions Differential Revision: https://reviews.llvm.org/D86709 --- .../llvm/CodeGen/GlobalISel/CombinerHelper.h | 4 +- .../include/llvm/Target/GlobalISel/Combine.td | 2 +- .../lib/CodeGen/GlobalISel/CombinerHelper.cpp | 93 ++++++++++---- .../prelegalizercombiner-invert-cmp.mir | 118 ++++++++++++++++++ 4 files changed, 192 insertions(+), 25 deletions(-) diff --git a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h index e1f0535affcdba..8607ad02d50637 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/CombinerHelper.h @@ -356,8 +356,8 @@ class CombinerHelper { bool matchRedundantSExtInReg(MachineInstr &MI); /// Combine inverting a result of a compare into the opposite cond code. - bool matchNotCmp(MachineInstr &MI, Register &CmpReg); - bool applyNotCmp(MachineInstr &MI, Register &CmpReg); + bool matchNotCmp(MachineInstr &MI, SmallVectorImpl &RegsToNegate); + bool applyNotCmp(MachineInstr &MI, SmallVectorImpl &RegsToNegate); /// Try to transform \p MI by using all of the above /// combine functions. Returns true if changed. diff --git a/llvm/include/llvm/Target/GlobalISel/Combine.td b/llvm/include/llvm/Target/GlobalISel/Combine.td index 4b0fe43c186847..6a6f97ae78b04d 100644 --- a/llvm/include/llvm/Target/GlobalISel/Combine.td +++ b/llvm/include/llvm/Target/GlobalISel/Combine.td @@ -373,7 +373,7 @@ def ext_ext_fold: GICombineRule < (apply [{ return Helper.applyCombineExtOfExt(*${root}, ${matchinfo}); }]) >; -def not_cmp_fold_matchinfo : GIDefMatchData<"Register">; +def not_cmp_fold_matchinfo : GIDefMatchData<"SmallVector">; def not_cmp_fold : GICombineRule< (defs root:$d, not_cmp_fold_matchinfo:$info), (match (wip_match_opcode G_XOR): $d, diff --git a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp index 6a89060805e090..10cd58f17e9aaa 100644 --- a/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CombinerHelper.cpp @@ -2243,13 +2243,13 @@ static bool isConstValidTrue(const TargetLowering &TLI, unsigned ScalarSizeBits, isConstTrueVal(TLI, Cst, IsVector, IsFP); } -bool CombinerHelper::matchNotCmp(MachineInstr &MI, Register &CmpReg) { +bool CombinerHelper::matchNotCmp(MachineInstr &MI, + SmallVectorImpl &RegsToNegate) { assert(MI.getOpcode() == TargetOpcode::G_XOR); LLT Ty = MRI.getType(MI.getOperand(0).getReg()); const auto &TLI = *Builder.getMF().getSubtarget().getTargetLowering(); Register XorSrc; Register CstReg; - int64_t Cst; // We match xor(src, true) here. if (!mi_match(MI.getOperand(0).getReg(), MRI, m_GXor(m_Reg(XorSrc), m_Reg(CstReg)))) @@ -2258,15 +2258,51 @@ bool CombinerHelper::matchNotCmp(MachineInstr &MI, Register &CmpReg) { if (!MRI.hasOneNonDBGUse(XorSrc)) return false; - // Now try match src to either icmp or fcmp. + // Check that XorSrc is the root of a tree of comparisons combined with ANDs + // and ORs. The suffix of RegsToNegate starting from index I is used a work + // list of tree nodes to visit. + RegsToNegate.push_back(XorSrc); + // Remember whether the comparisons are all integer or all floating point. + bool IsInt = false; bool IsFP = false; - if (!mi_match(XorSrc, MRI, m_GICmp(m_Pred(), m_Reg(), m_Reg()))) { - // Try fcmp. - if (!mi_match(XorSrc, MRI, m_GFCmp(m_Pred(), m_Reg(), m_Reg()))) + for (unsigned I = 0; I < RegsToNegate.size(); ++I) { + Register Reg = RegsToNegate[I]; + if (!MRI.hasOneNonDBGUse(Reg)) + return false; + MachineInstr *Def = MRI.getVRegDef(Reg); + switch (Def->getOpcode()) { + default: + // Don't match if the tree contains anything other than ANDs, ORs and + // comparisons. return false; - IsFP = true; + case TargetOpcode::G_ICMP: + if (IsFP) + return false; + IsInt = true; + // When we apply the combine we will invert the predicate. + break; + case TargetOpcode::G_FCMP: + if (IsInt) + return false; + IsFP = true; + // When we apply the combine we will invert the predicate. + break; + case TargetOpcode::G_AND: + case TargetOpcode::G_OR: + // Implement De Morgan's laws: + // ~(x & y) -> ~x | ~y + // ~(x | y) -> ~x & ~y + // When we apply the combine we will change the opcode and recursively + // negate the operands. + RegsToNegate.push_back(Def->getOperand(1).getReg()); + RegsToNegate.push_back(Def->getOperand(2).getReg()); + break; + } } + // Now we know whether the comparisons are integer or floating point, check + // the constant in the xor. + int64_t Cst; if (Ty.isVector()) { MachineInstr *CstDef = MRI.getVRegDef(CstReg); auto MaybeCst = getBuildVectorConstantSplat(*CstDef, MRI); @@ -2281,25 +2317,38 @@ bool CombinerHelper::matchNotCmp(MachineInstr &MI, Register &CmpReg) { return false; } - CmpReg = XorSrc; return true; } -bool CombinerHelper::applyNotCmp(MachineInstr &MI, Register &CmpReg) { - MachineInstr *CmpDef = MRI.getVRegDef(CmpReg); - assert(CmpDef && "Should have been given an MI reg"); - assert(CmpDef->getOpcode() == TargetOpcode::G_ICMP || - CmpDef->getOpcode() == TargetOpcode::G_FCMP); - - Observer.changingInstr(*CmpDef); - MachineOperand &PredOp = CmpDef->getOperand(1); - CmpInst::Predicate NewP = CmpInst::getInversePredicate( - (CmpInst::Predicate)PredOp.getPredicate()); - PredOp.setPredicate(NewP); - Observer.changedInstr(*CmpDef); +bool CombinerHelper::applyNotCmp(MachineInstr &MI, + SmallVectorImpl &RegsToNegate) { + for (Register Reg : RegsToNegate) { + MachineInstr *Def = MRI.getVRegDef(Reg); + Observer.changingInstr(*Def); + // For each comparison, invert the opcode. For each AND and OR, change the + // opcode. + switch (Def->getOpcode()) { + default: + llvm_unreachable("Unexpected opcode"); + case TargetOpcode::G_ICMP: + case TargetOpcode::G_FCMP: { + MachineOperand &PredOp = Def->getOperand(1); + CmpInst::Predicate NewP = CmpInst::getInversePredicate( + (CmpInst::Predicate)PredOp.getPredicate()); + PredOp.setPredicate(NewP); + break; + } + case TargetOpcode::G_AND: + Def->setDesc(Builder.getTII().get(TargetOpcode::G_OR)); + break; + case TargetOpcode::G_OR: + Def->setDesc(Builder.getTII().get(TargetOpcode::G_AND)); + break; + } + Observer.changedInstr(*Def); + } - replaceRegWith(MRI, MI.getOperand(0).getReg(), - CmpDef->getOperand(0).getReg()); + replaceRegWith(MRI, MI.getOperand(0).getReg(), MI.getOperand(1).getReg()); MI.eraseFromParent(); return true; } diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-invert-cmp.mir b/llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-invert-cmp.mir index 3356206c4cfcae..93f8e4284cd4cb 100644 --- a/llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-invert-cmp.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/prelegalizercombiner-invert-cmp.mir @@ -164,3 +164,121 @@ body: | $q0 = COPY %5(<4 x s32>) RET_ReallyLR implicit $q0 ... +--- +name: icmp_and_icmp +tracksRegLiveness: true +body: | + bb.1: + liveins: $x0 + + ; CHECK-LABEL: name: icmp_and_icmp + ; CHECK: liveins: $x0 + ; CHECK: [[COPY:%[0-9]+]]:_(s64) = COPY $x0 + ; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 1 + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(sle), [[COPY]](s64), [[C]] + ; CHECK: [[ICMP1:%[0-9]+]]:_(s1) = G_ICMP intpred(ule), [[COPY]](s64), [[C]] + ; CHECK: [[OR:%[0-9]+]]:_(s1) = G_OR [[ICMP]], [[ICMP1]] + ; CHECK: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[OR]](s1) + ; CHECK: $w0 = COPY [[ANYEXT]](s32) + ; CHECK: RET_ReallyLR implicit $w0 + %0:_(s64) = COPY $x0 + %1:_(s64) = G_CONSTANT i64 1 + %2:_(s1) = G_CONSTANT i1 1 + %3:_(s1) = G_ICMP intpred(sgt), %0(s64), %1 + %4:_(s1) = G_ICMP intpred(ugt), %0(s64), %1 + %5:_(s1) = G_AND %3, %4 + %6:_(s1) = G_XOR %5, %2 + %7:_(s32) = G_ANYEXT %6 + $w0 = COPY %7(s32) + RET_ReallyLR implicit $w0 +... +--- +name: icmp_or_icmp +tracksRegLiveness: true +body: | + bb.1: + liveins: $x0 + + ; CHECK-LABEL: name: icmp_or_icmp + ; CHECK: liveins: $x0 + ; CHECK: [[COPY:%[0-9]+]]:_(s64) = COPY $x0 + ; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 1 + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(sle), [[COPY]](s64), [[C]] + ; CHECK: [[ICMP1:%[0-9]+]]:_(s1) = G_ICMP intpred(ule), [[COPY]](s64), [[C]] + ; CHECK: [[AND:%[0-9]+]]:_(s1) = G_AND [[ICMP]], [[ICMP1]] + ; CHECK: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[AND]](s1) + ; CHECK: $w0 = COPY [[ANYEXT]](s32) + ; CHECK: RET_ReallyLR implicit $w0 + %0:_(s64) = COPY $x0 + %1:_(s64) = G_CONSTANT i64 1 + %2:_(s1) = G_CONSTANT i1 1 + %3:_(s1) = G_ICMP intpred(sgt), %0(s64), %1 + %4:_(s1) = G_ICMP intpred(ugt), %0(s64), %1 + %5:_(s1) = G_OR %3, %4 + %6:_(s1) = G_XOR %5, %2 + %7:_(s32) = G_ANYEXT %6 + $w0 = COPY %7(s32) + RET_ReallyLR implicit $w0 +... +--- +name: icmp_and_icmp_or_icmp +tracksRegLiveness: true +body: | + bb.1: + liveins: $x0 + + ; CHECK-LABEL: name: icmp_and_icmp_or_icmp + ; CHECK: liveins: $x0 + ; CHECK: [[COPY:%[0-9]+]]:_(s64) = COPY $x0 + ; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 1 + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(sle), [[COPY]](s64), [[C]] + ; CHECK: [[ICMP1:%[0-9]+]]:_(s1) = G_ICMP intpred(ule), [[COPY]](s64), [[C]] + ; CHECK: [[OR:%[0-9]+]]:_(s1) = G_OR [[ICMP]], [[ICMP1]] + ; CHECK: [[ICMP2:%[0-9]+]]:_(s1) = G_ICMP intpred(eq), [[COPY]](s64), [[C]] + ; CHECK: [[AND:%[0-9]+]]:_(s1) = G_AND [[OR]], [[ICMP2]] + ; CHECK: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[AND]](s1) + ; CHECK: $w0 = COPY [[ANYEXT]](s32) + ; CHECK: RET_ReallyLR implicit $w0 + %0:_(s64) = COPY $x0 + %1:_(s64) = G_CONSTANT i64 1 + %2:_(s1) = G_CONSTANT i1 1 + %3:_(s1) = G_ICMP intpred(sgt), %0(s64), %1 + %4:_(s1) = G_ICMP intpred(ugt), %0(s64), %1 + %5:_(s1) = G_AND %3, %4 + %6:_(s1) = G_ICMP intpred(ne), %0(s64), %1 + %7:_(s1) = G_OR %5, %6 + %8:_(s1) = G_XOR %7, %2 + %9:_(s32) = G_ANYEXT %8 + $w0 = COPY %9(s32) + RET_ReallyLR implicit $w0 +... +--- +name: icmp_and_trunc +tracksRegLiveness: true +body: | + bb.1: + liveins: $x0 + + ; CHECK-LABEL: name: icmp_and_trunc + ; CHECK: liveins: $x0 + ; CHECK: [[COPY:%[0-9]+]]:_(s64) = COPY $x0 + ; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 1 + ; CHECK: [[C1:%[0-9]+]]:_(s1) = G_CONSTANT i1 true + ; CHECK: [[ICMP:%[0-9]+]]:_(s1) = G_ICMP intpred(sgt), [[COPY]](s64), [[C]] + ; CHECK: [[TRUNC:%[0-9]+]]:_(s1) = G_TRUNC [[COPY]](s64) + ; CHECK: [[AND:%[0-9]+]]:_(s1) = G_AND [[ICMP]], [[TRUNC]] + ; CHECK: [[XOR:%[0-9]+]]:_(s1) = G_XOR [[AND]], [[C1]] + ; CHECK: [[ANYEXT:%[0-9]+]]:_(s32) = G_ANYEXT [[XOR]](s1) + ; CHECK: $w0 = COPY [[ANYEXT]](s32) + ; CHECK: RET_ReallyLR implicit $w0 + %0:_(s64) = COPY $x0 + %1:_(s64) = G_CONSTANT i64 1 + %2:_(s1) = G_CONSTANT i1 1 + %3:_(s1) = G_ICMP intpred(sgt), %0(s64), %1 + %4:_(s1) = G_TRUNC %0(s64) + %5:_(s1) = G_AND %3, %4 + %6:_(s1) = G_XOR %5, %2 + %7:_(s32) = G_ANYEXT %6 + $w0 = COPY %7(s32) + RET_ReallyLR implicit $w0 +... From 65f78e73ad574bb73bb625c787850acd261ba53a Mon Sep 17 00:00:00 2001 From: Sam Parker Date: Mon, 7 Sep 2020 09:08:07 +0100 Subject: [PATCH 13/36] [SimplifyCFG] Consider cost of combining predicates. Modify FoldBranchToCommonDest to consider the cost of inserting instructions when attempting to combine predicates to fold blocks. The threshold can be controlled via a new option: -simplifycfg-branch-fold-threshold which defaults to '2' to allow the insertion of a not and another logical operator. Differential Revision: https://reviews.llvm.org/D86526 --- llvm/include/llvm/Transforms/Utils/Local.h | 1 + llvm/lib/Transforms/Utils/SimplifyCFG.cpp | 28 ++- .../SimplifyCFG/ARM/branch-fold-threshold.ll | 162 ++++++++++++------ 3 files changed, 137 insertions(+), 54 deletions(-) diff --git a/llvm/include/llvm/Transforms/Utils/Local.h b/llvm/include/llvm/Transforms/Utils/Local.h index 5ab2dd496282fb..fb6f0269a0ac22 100644 --- a/llvm/include/llvm/Transforms/Utils/Local.h +++ b/llvm/include/llvm/Transforms/Utils/Local.h @@ -199,6 +199,7 @@ bool FlattenCFG(BasicBlock *BB, AAResults *AA = nullptr); /// branches to us and one of our successors, fold the setcc into the /// predecessor and use logical operations to pick the right destination. bool FoldBranchToCommonDest(BranchInst *BI, MemorySSAUpdater *MSSAU = nullptr, + const TargetTransformInfo *TTI = nullptr, unsigned BonusInstThreshold = 1); /// This function takes a virtual register computed by an Instruction and diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp index ae2471969160c3..124a7c423e72cd 100644 --- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp +++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp @@ -143,6 +143,13 @@ MaxSmallBlockSize("simplifycfg-max-small-block-size", cl::Hidden, cl::init(10), cl::desc("Max size of a block which is still considered " "small enough to thread through")); +// Two is chosen to allow one negation and a logical combine. +static cl::opt + BranchFoldThreshold("simplifycfg-branch-fold-threshold", cl::Hidden, + cl::init(2), + cl::desc("Maximum cost of combining conditions when " + "folding branches")); + STATISTIC(NumBitMaps, "Number of switch instructions turned into bitmaps"); STATISTIC(NumLinearMaps, "Number of switch instructions turned into linear mapping"); @@ -2684,12 +2691,16 @@ static bool extractPredSuccWeights(BranchInst *PBI, BranchInst *BI, /// and one of our successors, fold the block into the predecessor and use /// logical operations to pick the right destination. bool llvm::FoldBranchToCommonDest(BranchInst *BI, MemorySSAUpdater *MSSAU, + const TargetTransformInfo *TTI, unsigned BonusInstThreshold) { BasicBlock *BB = BI->getParent(); const unsigned PredCount = pred_size(BB); bool Changed = false; + TargetTransformInfo::TargetCostKind CostKind = + BB->getParent()->hasMinSize() ? TargetTransformInfo::TCK_CodeSize + : TargetTransformInfo::TCK_SizeAndLatency; Instruction *Cond = nullptr; if (BI->isConditional()) @@ -2818,6 +2829,19 @@ bool llvm::FoldBranchToCommonDest(BranchInst *BI, MemorySSAUpdater *MSSAU, continue; } + // Check the cost of inserting the necessary logic before performing the + // transformation. + if (TTI && Opc != Instruction::BinaryOpsEnd) { + Type *Ty = BI->getCondition()->getType(); + unsigned Cost = TTI->getArithmeticInstrCost(Opc, Ty, CostKind); + if (InvertPredCond && (!PBI->getCondition()->hasOneUse() || + !isa(PBI->getCondition()))) + Cost += TTI->getArithmeticInstrCost(Instruction::Xor, Ty, CostKind); + + if (Cost > BranchFoldThreshold) + continue; + } + LLVM_DEBUG(dbgs() << "FOLDING BRANCH TO COMMON DEST:\n" << *PBI << *BB); Changed = true; @@ -6013,7 +6037,7 @@ bool SimplifyCFGOpt::simplifyUncondBranch(BranchInst *BI, // branches to us and our successor, fold the comparison into the // predecessor and use logical operations to update the incoming value // for PHI nodes in common successor. - if (FoldBranchToCommonDest(BI, nullptr, Options.BonusInstThreshold)) + if (FoldBranchToCommonDest(BI, nullptr, &TTI, Options.BonusInstThreshold)) return requestResimplify(); return false; } @@ -6076,7 +6100,7 @@ bool SimplifyCFGOpt::simplifyCondBranch(BranchInst *BI, IRBuilder<> &Builder) { // If this basic block is ONLY a compare and a branch, and if a predecessor // branches to us and one of our successors, fold the comparison into the // predecessor and use logical operations to pick the right destination. - if (FoldBranchToCommonDest(BI, nullptr, Options.BonusInstThreshold)) + if (FoldBranchToCommonDest(BI, nullptr, &TTI, Options.BonusInstThreshold)) return requestResimplify(); // We have a conditional branch to two blocks that are only reachable diff --git a/llvm/test/Transforms/SimplifyCFG/ARM/branch-fold-threshold.ll b/llvm/test/Transforms/SimplifyCFG/ARM/branch-fold-threshold.ll index 2bcbaff50a9737..ffb13ca583f7f4 100644 --- a/llvm/test/Transforms/SimplifyCFG/ARM/branch-fold-threshold.ll +++ b/llvm/test/Transforms/SimplifyCFG/ARM/branch-fold-threshold.ll @@ -169,19 +169,34 @@ cond.end: } define i32 @or_predicate_minsize(i32 %a, i32 %b, i32 %c, i32 %d, i32* %input) #0 { -; CHECK-LABEL: @or_predicate_minsize( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i32 [[D:%.*]], 3 -; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] -; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] -; CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]] -; CHECK-NEXT: br i1 [[OR_COND]], label [[COND_END:%.*]], label [[COND_FALSE:%.*]] -; CHECK: cond.false: -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 -; CHECK-NEXT: br label [[COND_END]] -; CHECK: cond.end: -; CHECK-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[ENTRY:%.*]] ] -; CHECK-NEXT: ret i32 [[COND]] +; THUMB-LABEL: @or_predicate_minsize( +; THUMB-NEXT: entry: +; THUMB-NEXT: [[CMP:%.*]] = icmp sgt i32 [[D:%.*]], 3 +; THUMB-NEXT: br i1 [[CMP]], label [[COND_END:%.*]], label [[LOR_LHS_FALSE:%.*]] +; THUMB: lor.lhs.false: +; THUMB-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] +; THUMB-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] +; THUMB-NEXT: br i1 [[CMP1]], label [[COND_END]], label [[COND_FALSE:%.*]] +; THUMB: cond.false: +; THUMB-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 +; THUMB-NEXT: br label [[COND_END]] +; THUMB: cond.end: +; THUMB-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[LOR_LHS_FALSE]] ], [ 0, [[ENTRY:%.*]] ] +; THUMB-NEXT: ret i32 [[COND]] +; +; ARM-LABEL: @or_predicate_minsize( +; ARM-NEXT: entry: +; ARM-NEXT: [[CMP:%.*]] = icmp sgt i32 [[D:%.*]], 3 +; ARM-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] +; ARM-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] +; ARM-NEXT: [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]] +; ARM-NEXT: br i1 [[OR_COND]], label [[COND_END:%.*]], label [[COND_FALSE:%.*]] +; ARM: cond.false: +; ARM-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 +; ARM-NEXT: br label [[COND_END]] +; ARM: cond.end: +; ARM-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[ENTRY:%.*]] ] +; ARM-NEXT: ret i32 [[COND]] ; entry: %cmp = icmp sgt i32 %d, 3 @@ -202,19 +217,34 @@ cond.end: } define i32 @or_invert_predicate_minsize(i32 %a, i32 %b, i32 %c, i32 %d, i32* %input) #0 { -; CHECK-LABEL: @or_invert_predicate_minsize( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CMP:%.*]] = icmp sle i32 [[D:%.*]], 3 -; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] -; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] -; CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]] -; CHECK-NEXT: br i1 [[OR_COND]], label [[COND_END:%.*]], label [[COND_FALSE:%.*]] -; CHECK: cond.false: -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 -; CHECK-NEXT: br label [[COND_END]] -; CHECK: cond.end: -; CHECK-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[ENTRY:%.*]] ] -; CHECK-NEXT: ret i32 [[COND]] +; THUMB-LABEL: @or_invert_predicate_minsize( +; THUMB-NEXT: entry: +; THUMB-NEXT: [[CMP:%.*]] = icmp sgt i32 [[D:%.*]], 3 +; THUMB-NEXT: br i1 [[CMP]], label [[LOR_LHS_FALSE:%.*]], label [[COND_END:%.*]] +; THUMB: lor.lhs.false: +; THUMB-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] +; THUMB-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] +; THUMB-NEXT: br i1 [[CMP1]], label [[COND_END]], label [[COND_FALSE:%.*]] +; THUMB: cond.false: +; THUMB-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 +; THUMB-NEXT: br label [[COND_END]] +; THUMB: cond.end: +; THUMB-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[LOR_LHS_FALSE]] ], [ 0, [[ENTRY:%.*]] ] +; THUMB-NEXT: ret i32 [[COND]] +; +; ARM-LABEL: @or_invert_predicate_minsize( +; ARM-NEXT: entry: +; ARM-NEXT: [[CMP:%.*]] = icmp sle i32 [[D:%.*]], 3 +; ARM-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] +; ARM-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] +; ARM-NEXT: [[OR_COND:%.*]] = or i1 [[CMP]], [[CMP1]] +; ARM-NEXT: br i1 [[OR_COND]], label [[COND_END:%.*]], label [[COND_FALSE:%.*]] +; ARM: cond.false: +; ARM-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 +; ARM-NEXT: br label [[COND_END]] +; ARM: cond.end: +; ARM-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[ENTRY:%.*]] ] +; ARM-NEXT: ret i32 [[COND]] ; entry: %cmp = icmp sgt i32 %d, 3 @@ -267,19 +297,33 @@ cond.end: } define i32 @or_xor_predicate_minsize(i32 %a, i32 %b, i32 %c, i32 %d, i32* %input, i1 %cmp) #0 { -; CHECK-LABEL: @or_xor_predicate_minsize( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CMP_NOT:%.*]] = xor i1 [[CMP:%.*]], true -; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] -; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] -; CHECK-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP1]] -; CHECK-NEXT: br i1 [[OR_COND]], label [[COND_END:%.*]], label [[COND_FALSE:%.*]] -; CHECK: cond.false: -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 -; CHECK-NEXT: br label [[COND_END]] -; CHECK: cond.end: -; CHECK-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[ENTRY:%.*]] ] -; CHECK-NEXT: ret i32 [[COND]] +; THUMB-LABEL: @or_xor_predicate_minsize( +; THUMB-NEXT: entry: +; THUMB-NEXT: br i1 [[CMP:%.*]], label [[LOR_LHS_FALSE:%.*]], label [[COND_END:%.*]] +; THUMB: lor.lhs.false: +; THUMB-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] +; THUMB-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] +; THUMB-NEXT: br i1 [[CMP1]], label [[COND_END]], label [[COND_FALSE:%.*]] +; THUMB: cond.false: +; THUMB-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 +; THUMB-NEXT: br label [[COND_END]] +; THUMB: cond.end: +; THUMB-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[LOR_LHS_FALSE]] ], [ 0, [[ENTRY:%.*]] ] +; THUMB-NEXT: ret i32 [[COND]] +; +; ARM-LABEL: @or_xor_predicate_minsize( +; ARM-NEXT: entry: +; ARM-NEXT: [[CMP_NOT:%.*]] = xor i1 [[CMP:%.*]], true +; ARM-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] +; ARM-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] +; ARM-NEXT: [[OR_COND:%.*]] = or i1 [[CMP_NOT]], [[CMP1]] +; ARM-NEXT: br i1 [[OR_COND]], label [[COND_END:%.*]], label [[COND_FALSE:%.*]] +; ARM: cond.false: +; ARM-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 +; ARM-NEXT: br label [[COND_END]] +; ARM: cond.end: +; ARM-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[ENTRY:%.*]] ] +; ARM-NEXT: ret i32 [[COND]] ; entry: br i1 %cmp, label %lor.lhs.false, label %cond.end @@ -331,19 +375,33 @@ cond.end: } define i32 @and_xor_minsize(i32 %a, i32 %b, i32 %c, i32 %d, i32* %input, i1 %cmp) #0 { -; CHECK-LABEL: @and_xor_minsize( -; CHECK-NEXT: entry: -; CHECK-NEXT: [[CMP_NOT:%.*]] = xor i1 [[CMP:%.*]], true -; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] -; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] -; CHECK-NEXT: [[OR_COND:%.*]] = and i1 [[CMP_NOT]], [[CMP1]] -; CHECK-NEXT: br i1 [[OR_COND]], label [[COND_FALSE:%.*]], label [[COND_END:%.*]] -; CHECK: cond.false: -; CHECK-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 -; CHECK-NEXT: br label [[COND_END]] -; CHECK: cond.end: -; CHECK-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[ENTRY:%.*]] ] -; CHECK-NEXT: ret i32 [[COND]] +; THUMB-LABEL: @and_xor_minsize( +; THUMB-NEXT: entry: +; THUMB-NEXT: br i1 [[CMP:%.*]], label [[COND_END:%.*]], label [[LOR_LHS_FALSE:%.*]] +; THUMB: lor.lhs.false: +; THUMB-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] +; THUMB-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] +; THUMB-NEXT: br i1 [[CMP1]], label [[COND_FALSE:%.*]], label [[COND_END]] +; THUMB: cond.false: +; THUMB-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 +; THUMB-NEXT: br label [[COND_END]] +; THUMB: cond.end: +; THUMB-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[LOR_LHS_FALSE]] ], [ 0, [[ENTRY:%.*]] ] +; THUMB-NEXT: ret i32 [[COND]] +; +; ARM-LABEL: @and_xor_minsize( +; ARM-NEXT: entry: +; ARM-NEXT: [[CMP_NOT:%.*]] = xor i1 [[CMP:%.*]], true +; ARM-NEXT: [[ADD:%.*]] = add nsw i32 [[C:%.*]], [[A:%.*]] +; ARM-NEXT: [[CMP1:%.*]] = icmp slt i32 [[ADD]], [[B:%.*]] +; ARM-NEXT: [[OR_COND:%.*]] = and i1 [[CMP_NOT]], [[CMP1]] +; ARM-NEXT: br i1 [[OR_COND]], label [[COND_FALSE:%.*]], label [[COND_END:%.*]] +; ARM: cond.false: +; ARM-NEXT: [[TMP0:%.*]] = load i32, i32* [[INPUT:%.*]], align 4 +; ARM-NEXT: br label [[COND_END]] +; ARM: cond.end: +; ARM-NEXT: [[COND:%.*]] = phi i32 [ [[TMP0]], [[COND_FALSE]] ], [ 0, [[ENTRY:%.*]] ] +; ARM-NEXT: ret i32 [[COND]] ; entry: br i1 %cmp, label %cond.end, label %lor.lhs.false From e7bd058c7e2cb2c675a4b78ec770ea725bff8c64 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 7 Sep 2020 09:26:05 +0100 Subject: [PATCH 14/36] [clang-format] Allow configuring list of macros that map to attributes This adds a `AttributeMacros` configuration option that causes certain identifiers to be parsed like a __attribute__((foo)) annotation. This is motivated by our CHERI C/C++ fork which adds a __capability qualifier for pointer/reference. Without this change clang-format parses many type declarations as multiplications/bitwise-and instead. I initially considered adding "__capability" as a new clang-format keyword, but having a list of macros that should be treated as attributes is more flexible since it can be used e.g. for static analyzer annotations or other language extensions. Example: std::vector -> std::vector Depends on D86775 (to apply cleanly) Reviewed By: MyDeveloperDay, jrtc27 Differential Revision: https://reviews.llvm.org/D86782 --- clang/docs/ClangFormatStyleOptions.rst | 17 +++++ clang/include/clang/Format/Format.h | 19 ++++++ clang/lib/Format/Format.cpp | 2 + clang/lib/Format/FormatToken.h | 4 +- clang/lib/Format/FormatTokenLexer.cpp | 2 + clang/lib/Format/TokenAnnotator.cpp | 11 +-- clang/unittests/Format/FormatTest.cpp | 92 ++++++++++++++++++++++++-- 7 files changed, 137 insertions(+), 10 deletions(-) diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index c35718b51248c6..72a25032151ff4 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -758,7 +758,24 @@ the configuration (without a prefix: ``Auto``). int bbbbbbbbbbbbbbbbbbbbb) { } +**AttributeMacros** (``std::vector``) + A vector of strings that should be interpreted as attributes/qualifiers + instead of identifiers. This can be useful for language extensions or + static analyzer annotations: + .. code-block:: c++ + + x = (char *__capability)&y; + int function(void) __ununsed; + void only_writes_to_buffer(char *__output buffer); + + In the .clang-format configuration file, this can be configured like: + + .. code-block:: yaml + + AttributeMacros: ['__capability', '__output', '__ununsed'] + + For example: __capability. **BinPackArguments** (``bool``) If ``false``, a function call's arguments will either be all on the diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 269eab971a2cba..6bb828d60071f4 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -583,6 +583,24 @@ struct FormatStyle { /// The template declaration breaking style to use. BreakTemplateDeclarationsStyle AlwaysBreakTemplateDeclarations; + /// A vector of strings that should be interpreted as attributes/qualifiers + /// instead of identifiers. This can be useful for language extensions or + /// static analyzer annotations. + /// + /// For example: + /// \code + /// x = (char *__capability)&y; + /// int function(void) __ununsed; + /// void only_writes_to_buffer(char *__output buffer); + /// \endcode + /// + /// In the .clang-format configuration file, this can be configured like: + /// \code{.yaml} + /// AttributeMacros: ['__capability', '__output', '__ununsed'] + /// \endcode + /// + std::vector AttributeMacros; + /// If ``false``, a function call's arguments will either be all on the /// same line or will have one line each. /// \code @@ -2351,6 +2369,7 @@ struct FormatStyle { R.AlwaysBreakBeforeMultilineStrings && AlwaysBreakTemplateDeclarations == R.AlwaysBreakTemplateDeclarations && + AttributeMacros == R.AttributeMacros && BinPackArguments == R.BinPackArguments && BinPackParameters == R.BinPackParameters && BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index fe11cba9bfdf08..5dda2bda06b54a 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -475,6 +475,7 @@ template <> struct MappingTraits { Style.AlwaysBreakBeforeMultilineStrings); IO.mapOptional("AlwaysBreakTemplateDeclarations", Style.AlwaysBreakTemplateDeclarations); + IO.mapOptional("AttributeMacros", Style.AttributeMacros); IO.mapOptional("BinPackArguments", Style.BinPackArguments); IO.mapOptional("BinPackParameters", Style.BinPackParameters); IO.mapOptional("BraceWrapping", Style.BraceWrapping); @@ -842,6 +843,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; LLVMStyle.AlwaysBreakBeforeMultilineStrings = false; LLVMStyle.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_MultiLine; + LLVMStyle.AttributeMacros.push_back("__capability"); LLVMStyle.BinPackArguments = true; LLVMStyle.BinPackParameters = true; LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index ad72a95062abe7..795c268896294c 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -29,6 +29,7 @@ namespace format { TYPE(ArrayInitializerLSquare) \ TYPE(ArraySubscriptLSquare) \ TYPE(AttributeColon) \ + TYPE(AttributeMacro) \ TYPE(AttributeParen) \ TYPE(AttributeSquare) \ TYPE(BinaryOperator) \ @@ -442,7 +443,8 @@ struct FormatToken { bool canBePointerOrReferenceQualifier() const { return isOneOf(tok::kw_const, tok::kw_restrict, tok::kw_volatile, tok::kw___attribute, tok::kw__Nonnull, tok::kw__Nullable, - tok::kw__Null_unspecified, tok::kw___ptr32, tok::kw___ptr64); + tok::kw__Null_unspecified, tok::kw___ptr32, tok::kw___ptr64, + TT_AttributeMacro); } /// Determine whether the token is a simple-type-specifier. diff --git a/clang/lib/Format/FormatTokenLexer.cpp b/clang/lib/Format/FormatTokenLexer.cpp index 1fd153d1112eb9..f6db58acd8dbef 100644 --- a/clang/lib/Format/FormatTokenLexer.cpp +++ b/clang/lib/Format/FormatTokenLexer.cpp @@ -39,6 +39,8 @@ FormatTokenLexer::FormatTokenLexer( for (const std::string &ForEachMacro : Style.ForEachMacros) Macros.insert({&IdentTable.get(ForEachMacro), TT_ForEachMacro}); + for (const std::string &AttributeMacro : Style.AttributeMacros) + Macros.insert({&IdentTable.get(AttributeMacro), TT_AttributeMacro}); for (const std::string &StatementMacro : Style.StatementMacros) Macros.insert({&IdentTable.get(StatementMacro), TT_StatementMacro}); for (const std::string &TypenameMacro : Style.TypenameMacros) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index f04f101f045938..fc6a226dc4a12c 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1333,11 +1333,12 @@ class AnnotatingParser { // Reset token type in case we have already looked at it and then // recovered from an error (e.g. failure to find the matching >). if (!CurrentToken->isOneOf( - TT_LambdaLSquare, TT_LambdaLBrace, TT_ForEachMacro, - TT_TypenameMacro, TT_FunctionLBrace, TT_ImplicitStringLiteral, - TT_InlineASMBrace, TT_JsFatArrow, TT_LambdaArrow, TT_NamespaceMacro, - TT_OverloadedOperator, TT_RegexLiteral, TT_TemplateString, - TT_ObjCStringLiteral, TT_UntouchableMacroFunc)) + TT_LambdaLSquare, TT_LambdaLBrace, TT_AttributeMacro, + TT_ForEachMacro, TT_TypenameMacro, TT_FunctionLBrace, + TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_JsFatArrow, + TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator, + TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral, + TT_UntouchableMacroFunc)) CurrentToken->setType(TT_Unknown); CurrentToken->Role.reset(); CurrentToken->MatchingParen = nullptr; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index a2d694947990f0..f224ab03271d91 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -8040,7 +8040,20 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("vector v;"); verifyFormat("vector v;"); verifyFormat("vector v;"); + verifyFormat("vector v;"); + FormatStyle CustomQualifier = getLLVMStyle(); + // Add indentifers that should not be parsed as a qualifier by default. + CustomQualifier.AttributeMacros.push_back("__my_qualifier"); + CustomQualifier.AttributeMacros.push_back("_My_qualifier"); + CustomQualifier.AttributeMacros.push_back("my_other_qualifier"); + verifyFormat("vector parse_as_multiply;"); + verifyFormat("vector v;", CustomQualifier); + verifyFormat("vector parse_as_multiply;"); + verifyFormat("vector v;", CustomQualifier); + verifyFormat("vector parse_as_multiply;"); + verifyFormat("vector v;", CustomQualifier); verifyFormat("vector v;"); + verifyFormat("vector v;"); verifyFormat("vector v;"); verifyFormat("foo();"); verifyFormat("foo();"); @@ -8084,10 +8097,23 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyIndependentOfContext("MACRO(A *[[clang::attr(\"foo\")]] a);"); verifyIndependentOfContext("MACRO(A *__ptr32 a);"); verifyIndependentOfContext("MACRO(A *__ptr64 a);"); + verifyIndependentOfContext("MACRO(A *__capability);"); + verifyIndependentOfContext("MACRO(A &__capability);"); + verifyFormat("MACRO(A *__my_qualifier);"); // type declaration + verifyFormat("void f() { MACRO(A * __my_qualifier); }"); // multiplication + // If we add __my_qualifier to AttributeMacros it should always be parsed as + // a type declaration: + verifyFormat("MACRO(A *__my_qualifier);", CustomQualifier); + verifyFormat("void f() { MACRO(A *__my_qualifier); }", CustomQualifier); + verifyIndependentOfContext("MACRO('0' <= c && c <= '9');"); verifyFormat("void f() { f(float{1}, a * a); }"); // FIXME: Is there a way to make this work? // verifyIndependentOfContext("MACRO(A *a);"); + verifyFormat("MACRO(A &B);"); + verifyFormat("MACRO(A *B);"); + verifyFormat("void f() { MACRO(A * B); }"); + verifyFormat("void f() { MACRO(A & B); }"); verifyFormat("DatumHandle const *operator->() const { return input_; }"); verifyFormat("return options != nullptr && operator==(*options);"); @@ -8137,10 +8163,47 @@ TEST_F(FormatTest, UnderstandsAttributes) { verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa __attribute__((unused))\n" "aaaaaaaaaaaaaaaaaaaaaaa(int i);"); FormatStyle AfterType = getLLVMStyle(); - AfterType.AlwaysBreakAfterReturnType = FormatStyle::RTBS_AllDefinitions; + AfterType.AlwaysBreakAfterReturnType = FormatStyle::RTBS_All; verifyFormat("__attribute__((nodebug)) void\n" "foo() {}\n", AfterType); + verifyFormat("__unused void\n" + "foo() {}", + AfterType); + + FormatStyle CustomAttrs = getLLVMStyle(); + CustomAttrs.AttributeMacros.push_back("__unused"); + CustomAttrs.AttributeMacros.push_back("__attr1"); + CustomAttrs.AttributeMacros.push_back("__attr2"); + CustomAttrs.AttributeMacros.push_back("no_underscore_attr"); + verifyFormat("vector v;"); + verifyFormat("vector v;"); + verifyFormat("vector v;"); + // Check that it is parsed as a multiplication without AttributeMacros and + // as a pointer qualifier when we add __attr1/__attr2 to AttributeMacros. + verifyFormat("vector v;"); + verifyFormat("vector v;"); + verifyFormat("vector v;"); + verifyFormat("vector v;"); + verifyFormat("vector v;", CustomAttrs); + verifyFormat("vector v;", CustomAttrs); + verifyFormat("vector v;", CustomAttrs); + verifyFormat("vector v;", CustomAttrs); + verifyFormat("vector v;", CustomAttrs); + verifyFormat("vector v;", CustomAttrs); + verifyFormat("vector v;", CustomAttrs); + + // Check that these are not parsed as function declarations: + CustomAttrs.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; + CustomAttrs.BreakBeforeBraces = FormatStyle::BS_Allman; + verifyFormat("SomeType s(InitValue);", CustomAttrs); + verifyFormat("SomeType s{InitValue};", CustomAttrs); + verifyFormat("SomeType *__unused s(InitValue);", CustomAttrs); + verifyFormat("SomeType *__unused s{InitValue};", CustomAttrs); + verifyFormat("SomeType s __unused(InitValue);", CustomAttrs); + verifyFormat("SomeType s __unused{InitValue};", CustomAttrs); + verifyFormat("SomeType *__capability s(InitValue);", CustomAttrs); + verifyFormat("SomeType *__capability s{InitValue};", CustomAttrs); } TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) { @@ -8157,6 +8220,7 @@ TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) { verifyFormat("x = (foo *[[clang::attr(\"foo\")]])*v;"); verifyFormat("x = (foo *__ptr32)*v;"); verifyFormat("x = (foo *__ptr64)*v;"); + verifyFormat("x = (foo *__capability)*v;"); // Check that we handle multiple trailing qualifiers and skip them all to // determine that the expression is a cast to a pointer type. @@ -8165,7 +8229,7 @@ TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) { LongPointerLeft.PointerAlignment = FormatStyle::PAS_Left; StringRef AllQualifiers = "const volatile restrict __attribute__((foo)) _Nonnull _Null_unspecified " - "_Nonnull [[clang::attr]] __ptr32 __ptr64"; + "_Nonnull [[clang::attr]] __ptr32 __ptr64 __capability"; verifyFormat(("x = (foo *" + AllQualifiers + ")*v;").str(), LongPointerRight); verifyFormat(("x = (foo* " + AllQualifiers + ")*v;").str(), LongPointerLeft); @@ -8173,6 +8237,20 @@ TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) { verifyFormat("x = (foo *const)&v;"); verifyFormat(("x = (foo *" + AllQualifiers + ")&v;").str(), LongPointerRight); verifyFormat(("x = (foo* " + AllQualifiers + ")&v;").str(), LongPointerLeft); + + // Check custom qualifiers: + FormatStyle CustomQualifier = getLLVMStyleWithColumns(999); + CustomQualifier.AttributeMacros.push_back("__my_qualifier"); + verifyFormat("x = (foo * __my_qualifier) * v;"); // not parsed as qualifier. + verifyFormat("x = (foo *__my_qualifier)*v;", CustomQualifier); + verifyFormat(("x = (foo *" + AllQualifiers + " __my_qualifier)*v;").str(), + CustomQualifier); + verifyFormat(("x = (foo *" + AllQualifiers + " __my_qualifier)&v;").str(), + CustomQualifier); + + // Check that unknown identifiers result in binary operator parsing: + verifyFormat("x = (foo * __unknown_qualifier) * v;"); + verifyFormat("x = (foo * __unknown_qualifier) & v;"); } TEST_F(FormatTest, UnderstandsSquareAttributes) { @@ -13770,9 +13848,9 @@ TEST_F(FormatTest, GetsCorrectBasedOnStyle) { CHECK_PARSE_NESTED_BOOL_FIELD(STRUCT, FIELD, #FIELD) #define CHECK_PARSE(TEXT, FIELD, VALUE) \ - EXPECT_NE(VALUE, Style.FIELD); \ + EXPECT_NE(VALUE, Style.FIELD) << "Initial value already the same!"; \ EXPECT_EQ(0, parseConfiguration(TEXT, &Style).value()); \ - EXPECT_EQ(VALUE, Style.FIELD) + EXPECT_EQ(VALUE, Style.FIELD) << "Unexpected value after parsing!" TEST_F(FormatTest, ParsesConfigurationBools) { FormatStyle Style = {}; @@ -14162,6 +14240,12 @@ TEST_F(FormatTest, ParsesConfiguration) { CHECK_PARSE("ForEachMacros: [BOOST_FOREACH, Q_FOREACH]", ForEachMacros, BoostAndQForeach); + Style.AttributeMacros.clear(); + CHECK_PARSE("BasedOnStyle: LLVM", AttributeMacros, + std::vector{"__capability"}); + CHECK_PARSE("AttributeMacros: [attr1, attr2]", AttributeMacros, + std::vector({"attr1", "attr2"})); + Style.StatementMacros.clear(); CHECK_PARSE("StatementMacros: [QUNUSED]", StatementMacros, std::vector{"QUNUSED"}); From 8aa3b8da5db2ae73bf536b630915eb9f0ddc15cb Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 7 Sep 2020 09:26:16 +0100 Subject: [PATCH 15/36] [clang-format] Handle typename macros inside cast expressions Before: x = (STACK_OF(uint64_t)) & a; After: x = (STACK_OF(uint64_t))&a; Reviewed By: MyDeveloperDay Differential Revision: https://reviews.llvm.org/D86930 --- clang/lib/Format/FormatToken.h | 1 + clang/lib/Format/TokenAnnotator.cpp | 12 +++++++++--- clang/unittests/Format/FormatTest.cpp | 2 ++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 795c268896294c..a9aeef5e9e52f3 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -102,6 +102,7 @@ namespace format { TYPE(TrailingReturnArrow) \ TYPE(TrailingUnaryOperator) \ TYPE(TypenameMacro) \ + TYPE(TypenameMacroParen) \ TYPE(UnaryOperator) \ TYPE(UntouchableMacroFunc) \ TYPE(CSharpStringLiteral) \ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index fc6a226dc4a12c..097843bdca84d2 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -244,6 +244,8 @@ class AnnotatingParser { Contexts.back().IsExpression = false; } else if (Left->Previous && Left->Previous->is(tok::kw___attribute)) { Left->setType(TT_AttributeParen); + } else if (Left->Previous && Left->Previous->is(TT_TypenameMacro)) { + Left->setType(TT_TypenameMacroParen); } else if (Left->Previous && Left->Previous->is(TT_ForEachMacro)) { // The first argument to a foreach macro is a declaration. Contexts.back().IsForEachMacro = true; @@ -335,6 +337,8 @@ class AnnotatingParser { if (Left->is(TT_AttributeParen)) CurrentToken->setType(TT_AttributeParen); + if (Left->is(TT_TypenameMacroParen)) + CurrentToken->setType(TT_TypenameMacroParen); if (Left->Previous && Left->Previous->is(TT_JavaAnnotation)) CurrentToken->setType(TT_JavaAnnotation); if (Left->Previous && Left->Previous->is(TT_LeadingJavaAnnotation)) @@ -1855,9 +1859,11 @@ class AnnotatingParser { } return T && T->is(TT_PointerOrReference); }; - bool ParensAreType = !Tok.Previous || Tok.Previous->is(TT_TemplateCloser) || - Tok.Previous->isSimpleTypeSpecifier() || - IsQualifiedPointerOrReference(Tok.Previous); + bool ParensAreType = + !Tok.Previous || + Tok.Previous->isOneOf(TT_TemplateCloser, TT_TypenameMacroParen) || + Tok.Previous->isSimpleTypeSpecifier() || + IsQualifiedPointerOrReference(Tok.Previous); bool ParensCouldEndDecl = Tok.Next->isOneOf(tok::equal, tok::semi, tok::l_brace, tok::greater); if (ParensAreType && !ParensCouldEndDecl) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index f224ab03271d91..be68da6f2ef6e2 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -16557,6 +16557,8 @@ TEST_F(FormatTest, TypenameMacros) { Macros.PointerAlignment = FormatStyle::PAS_Left; verifyFormat("STACK_OF(int)* a;", Macros); verifyFormat("STACK_OF(int*)* a;", Macros); + verifyFormat("x = (STACK_OF(uint64_t))*a;", Macros); + verifyFormat("x = (STACK_OF(uint64_t))&a;", Macros); } TEST_F(FormatTest, AmbersandInLamda) { From cd01eec14bc045a8616604cadf94dba025090ba5 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 7 Sep 2020 09:26:47 +0100 Subject: [PATCH 16/36] [clang-format] Check that */& after typename macros are pointers/references Reviewed By: MyDeveloperDay Differential Revision: https://reviews.llvm.org/D86950 --- clang/unittests/Format/FormatTest.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index be68da6f2ef6e2..978c22c6ee69a4 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -8041,6 +8041,14 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("vector v;"); verifyFormat("vector v;"); verifyFormat("vector v;"); + FormatStyle TypeMacros = getLLVMStyle(); + TypeMacros.TypenameMacros = {"LIST"}; + verifyFormat("vector v;", TypeMacros); + verifyFormat("vector v;", TypeMacros); + verifyFormat("vector v;", TypeMacros); + verifyFormat("vector v;", TypeMacros); + verifyFormat("vector v;", TypeMacros); // multiplication + FormatStyle CustomQualifier = getLLVMStyle(); // Add indentifers that should not be parsed as a qualifier by default. CustomQualifier.AttributeMacros.push_back("__my_qualifier"); @@ -8105,6 +8113,9 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { // a type declaration: verifyFormat("MACRO(A *__my_qualifier);", CustomQualifier); verifyFormat("void f() { MACRO(A *__my_qualifier); }", CustomQualifier); + // Also check that TypenameMacros prevents parsing it as multiplication: + verifyIndependentOfContext("MACRO(LIST(uint64_t) * a);"); // multiplication + verifyIndependentOfContext("MACRO(LIST(uint64_t) *a);", TypeMacros); // type verifyIndependentOfContext("MACRO('0' <= c && c <= '9');"); verifyFormat("void f() { f(float{1}, a * a); }"); @@ -16553,12 +16564,15 @@ TEST_F(FormatTest, TypenameMacros) { verifyFormat("STACK_OF(LIST(int)) a, b;", Macros); verifyFormat("for (LIST(int) *a = NULL; a;) {\n}", Macros); verifyFormat("STACK_OF(int) f(LIST(int) *arg);", Macros); + verifyFormat("vector x;", Macros); + verifyFormat("vector f(LIST(uint64_t) *arg);", Macros); Macros.PointerAlignment = FormatStyle::PAS_Left; verifyFormat("STACK_OF(int)* a;", Macros); verifyFormat("STACK_OF(int*)* a;", Macros); verifyFormat("x = (STACK_OF(uint64_t))*a;", Macros); verifyFormat("x = (STACK_OF(uint64_t))&a;", Macros); + verifyFormat("vector x;", Macros); } TEST_F(FormatTest, AmbersandInLamda) { From 56fa7d1dc6a8d23111ff84171036f333cf9cddf2 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Tue, 1 Sep 2020 18:09:07 +0100 Subject: [PATCH 17/36] [clang-format] Fix formatting of _Atomic() qualifier Before: _Atomic(uint64_t) * a; After: _Atomic(uint64_t) *a; This treats _Atomic the same as the the TypenameMacros and decltype. It also allows some cleanup by removing checks whether the token before a paren is kw_decltype and instead checking for TT_TypeDeclarationParen. While touching this code also extend the decltype test cases to also check for typeof() and _Atomic(T). Reviewed By: MyDeveloperDay Differential Revision: https://reviews.llvm.org/D86959 --- clang/lib/Format/FormatToken.cpp | 1 + clang/lib/Format/FormatToken.h | 3 +- clang/lib/Format/TokenAnnotator.cpp | 54 +++++++++++----------- clang/unittests/Format/FormatTest.cpp | 65 +++++++++++++++++++++++++++ 4 files changed, 94 insertions(+), 29 deletions(-) diff --git a/clang/lib/Format/FormatToken.cpp b/clang/lib/Format/FormatToken.cpp index 4bc865b043fd27..8e4994f4c0d57c 100644 --- a/clang/lib/Format/FormatToken.cpp +++ b/clang/lib/Format/FormatToken.cpp @@ -62,6 +62,7 @@ bool FormatToken::isSimpleTypeSpecifier() const { case tok::kw_char32_t: case tok::kw_typeof: case tok::kw_decltype: + case tok::kw__Atomic: return true; default: return false; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index a9aeef5e9e52f3..8253bf18fc6679 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -101,8 +101,8 @@ namespace format { TYPE(TrailingAnnotation) \ TYPE(TrailingReturnArrow) \ TYPE(TrailingUnaryOperator) \ + TYPE(TypeDeclarationParen) \ TYPE(TypenameMacro) \ - TYPE(TypenameMacroParen) \ TYPE(UnaryOperator) \ TYPE(UntouchableMacroFunc) \ TYPE(CSharpStringLiteral) \ @@ -526,6 +526,7 @@ struct FormatToken { case tok::kw_decltype: case tok::kw_noexcept: case tok::kw_static_assert: + case tok::kw__Atomic: case tok::kw___attribute: return true; default: diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 097843bdca84d2..0239dbd63d94e2 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -185,6 +185,8 @@ class AnnotatingParser { if (!CurrentToken) return false; FormatToken *Left = CurrentToken->Previous; + FormatToken *PrevNonComment = + Left ? Left->getPreviousNonComment() : nullptr; Left->ParentBracket = Contexts.back().ContextKind; ScopedContextCreator ContextCreator(*this, tok::l_paren, 1); @@ -216,9 +218,8 @@ class AnnotatingParser { // export type X = (...); Contexts.back().IsExpression = false; } else if (Left->Previous && - (Left->Previous->isOneOf(tok::kw_static_assert, tok::kw_decltype, - tok::kw_while, tok::l_paren, - tok::comma) || + (Left->Previous->isOneOf(tok::kw_static_assert, tok::kw_while, + tok::l_paren, tok::comma) || Left->Previous->isIf() || Left->Previous->is(TT_BinaryOperator))) { // static_assert, if and while usually contain expressions. @@ -242,10 +243,15 @@ class AnnotatingParser { } else if (Contexts[Contexts.size() - 2].CaretFound) { // This is the parameter list of an ObjC block. Contexts.back().IsExpression = false; - } else if (Left->Previous && Left->Previous->is(tok::kw___attribute)) { + } else if (PrevNonComment && PrevNonComment->is(tok::kw___attribute)) { Left->setType(TT_AttributeParen); - } else if (Left->Previous && Left->Previous->is(TT_TypenameMacro)) { - Left->setType(TT_TypenameMacroParen); + } else if (PrevNonComment && + PrevNonComment->isOneOf(TT_TypenameMacro, tok::kw_decltype, + tok::kw_typeof, tok::kw__Atomic)) { + Left->setType(TT_TypeDeclarationParen); + // decltype() and typeof() usually contain expressions. + if (PrevNonComment->isOneOf(tok::kw_decltype, tok::kw_typeof)) + Contexts.back().IsExpression = true; } else if (Left->Previous && Left->Previous->is(TT_ForEachMacro)) { // The first argument to a foreach macro is a declaration. Contexts.back().IsForEachMacro = true; @@ -337,8 +343,8 @@ class AnnotatingParser { if (Left->is(TT_AttributeParen)) CurrentToken->setType(TT_AttributeParen); - if (Left->is(TT_TypenameMacroParen)) - CurrentToken->setType(TT_TypenameMacroParen); + if (Left->is(TT_TypeDeclarationParen)) + CurrentToken->setType(TT_TypeDeclarationParen); if (Left->Previous && Left->Previous->is(TT_JavaAnnotation)) CurrentToken->setType(TT_JavaAnnotation); if (Left->Previous && Left->Previous->is(TT_LeadingJavaAnnotation)) @@ -944,9 +950,9 @@ class AnnotatingParser { return false; if (Line.MustBeDeclaration && Contexts.size() == 1 && !Contexts.back().IsExpression && !Line.startsWith(TT_ObjCProperty) && - (!Tok->Previous || - !Tok->Previous->isOneOf(tok::kw_decltype, tok::kw___attribute, - TT_LeadingJavaAnnotation))) + !Tok->is(TT_TypeDeclarationParen) && + (!Tok->Previous || !Tok->Previous->isOneOf(tok::kw___attribute, + TT_LeadingJavaAnnotation))) Line.MightBeFunctionDecl = true; break; case tok::l_square: @@ -1758,9 +1764,8 @@ class AnnotatingParser { PreviousNotConst->MatchingParen->Previous->isNot(tok::period) && PreviousNotConst->MatchingParen->Previous->isNot(tok::kw_template); - if (PreviousNotConst->is(tok::r_paren) && PreviousNotConst->MatchingParen && - PreviousNotConst->MatchingParen->Previous && - PreviousNotConst->MatchingParen->Previous->is(tok::kw_decltype)) + if (PreviousNotConst->is(tok::r_paren) && + PreviousNotConst->is(TT_TypeDeclarationParen)) return true; return (!IsPPKeyword && @@ -1861,7 +1866,7 @@ class AnnotatingParser { }; bool ParensAreType = !Tok.Previous || - Tok.Previous->isOneOf(TT_TemplateCloser, TT_TypenameMacroParen) || + Tok.Previous->isOneOf(TT_TemplateCloser, TT_TypeDeclarationParen) || Tok.Previous->isSimpleTypeSpecifier() || IsQualifiedPointerOrReference(Tok.Previous); bool ParensCouldEndDecl = @@ -1931,6 +1936,9 @@ class AnnotatingParser { if (PrevToken->is(tok::coloncolon)) return TT_PointerOrReference; + if (PrevToken->is(tok::r_paren) && PrevToken->is(TT_TypeDeclarationParen)) + return TT_PointerOrReference; + if (PrevToken->isOneOf(tok::l_paren, tok::l_square, tok::l_brace, tok::comma, tok::semi, tok::kw_return, tok::colon, tok::equal, tok::kw_delete, tok::kw_sizeof, @@ -1946,15 +1954,6 @@ class AnnotatingParser { if (NextToken->isOneOf(tok::comma, tok::semi)) return TT_PointerOrReference; - if (PrevToken->is(tok::r_paren) && PrevToken->MatchingParen) { - FormatToken *TokenBeforeMatchingParen = - PrevToken->MatchingParen->getPreviousNonComment(); - if (TokenBeforeMatchingParen && - TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype, - TT_TypenameMacro)) - return TT_PointerOrReference; - } - if (PrevToken->Tok.isLiteral() || PrevToken->isOneOf(tok::r_paren, tok::r_square, tok::kw_true, tok::kw_false, tok::r_brace) || @@ -2848,9 +2847,7 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, return true; FormatToken *TokenBeforeMatchingParen = Left.MatchingParen->getPreviousNonComment(); - if (!TokenBeforeMatchingParen || - !TokenBeforeMatchingParen->isOneOf(tok::kw_typeof, tok::kw_decltype, - TT_TypenameMacro)) + if (!TokenBeforeMatchingParen || !Left.is(TT_TypeDeclarationParen)) return true; } return (Left.Tok.isLiteral() || @@ -3948,7 +3945,8 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line, if (Left.is(tok::equal) && Right.is(tok::l_brace) && !Style.Cpp11BracedListStyle) return false; - if (Left.is(tok::l_paren) && Left.is(TT_AttributeParen)) + if (Left.is(tok::l_paren) && + Left.isOneOf(TT_AttributeParen, TT_TypeDeclarationParen)) return false; if (Left.is(tok::l_paren) && Left.Previous && (Left.Previous->isOneOf(TT_BinaryOperator, TT_CastRParen))) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 978c22c6ee69a4..a5943847882fb4 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -168,6 +168,8 @@ TEST_F(FormatTest, NestedNameSpecifiers) { verifyFormat("vector<::Type> v;"); verifyFormat("::ns::SomeFunction(::ns::SomeOtherFunction())"); verifyFormat("static constexpr bool Bar = decltype(bar())::value;"); + verifyFormat("static constexpr bool Bar = typeof(bar())::value;"); + verifyFormat("static constexpr bool Bar = _Atomic(bar())::value;"); verifyFormat("bool a = 2 < ::SomeFunction();"); verifyFormat("ALWAYS_INLINE ::std::string getName();"); verifyFormat("some::string getName();"); @@ -7904,7 +7906,10 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("auto PointerBinding = [](const char *S) {};"); verifyFormat("typedef typeof(int(int, int)) *MyFunc;"); verifyFormat("[](const decltype(*a) &value) {}"); + verifyFormat("[](const typeof(*a) &value) {}"); + verifyFormat("[](const _Atomic(a *) &value) {}"); verifyFormat("decltype(a * b) F();"); + verifyFormat("typeof(a * b) F();"); verifyFormat("#define MACRO() [](A *a) { return 1; }"); verifyFormat("Constructor() : member([](A *a, B *b) {}) {}"); verifyIndependentOfContext("typedef void (*f)(int *a);"); @@ -7970,6 +7975,8 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("delete *x;", Left); verifyFormat("typedef typeof(int(int, int))* MyFuncPtr;", Left); verifyFormat("[](const decltype(*a)* ptr) {}", Left); + verifyFormat("[](const typeof(*a)* ptr) {}", Left); + verifyFormat("[](const _Atomic(a*)* ptr) {}", Left); verifyFormat("typedef typeof /*comment*/ (int(int, int))* MyFuncPtr;", Left); verifyFormat("auto x(A&&, B&&, C&&) -> D;", Left); verifyFormat("auto x = [](A&&, B&&, C&&) -> D {};", Left); @@ -8066,6 +8073,8 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("foo();"); verifyFormat("foo();"); verifyFormat("decltype(*::std::declval()) void F();"); + verifyFormat("typeof(*::std::declval()) void F();"); + verifyFormat("_Atomic(*::std::declval()) void F();"); verifyFormat( "template ::value &&\n" @@ -8089,6 +8098,9 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyIndependentOfContext("MACRO(int *i);"); verifyIndependentOfContext("MACRO(auto *a);"); verifyIndependentOfContext("MACRO(const A *a);"); + verifyIndependentOfContext("MACRO(_Atomic(A) *a);"); + verifyIndependentOfContext("MACRO(decltype(A) *a);"); + verifyIndependentOfContext("MACRO(typeof(A) *a);"); verifyIndependentOfContext("MACRO(A *const a);"); verifyIndependentOfContext("MACRO(A *restrict a);"); verifyIndependentOfContext("MACRO(A *__restrict__ a);"); @@ -8639,6 +8651,10 @@ TEST_F(FormatTest, BreaksLongDeclarations) { "LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}"); verifyFormat("decltype(LoooooooooooooooooooooooooooooooooooooooongName)\n" "LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}"); + verifyFormat("typeof(LoooooooooooooooooooooooooooooooooooooooooongName)\n" + "LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}"); + verifyFormat("_Atomic(LooooooooooooooooooooooooooooooooooooooooongName)\n" + "LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}"); verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n" "LooooooooooooooooooooooooooongFunctionDeclaration(T... t);"); verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n" @@ -8988,6 +9004,8 @@ TEST_F(FormatTest, LayoutCxx11BraceInitializers) { verifyFormat("int foo(int i) { return fo1{}(i); }"); verifyFormat("int foo(int i) { return fo1{}(i); }"); verifyFormat("auto i = decltype(x){};"); + verifyFormat("auto i = typeof(x){};"); + verifyFormat("auto i = _Atomic(x){};"); verifyFormat("std::vector v = {1, 0 /* comment */};"); verifyFormat("Node n{1, Node{1000}, //\n" " 2};"); @@ -11580,6 +11598,8 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("auto i = std::make_unique(5);", NoSpace); verifyFormat("size_t x = sizeof(x);", NoSpace); verifyFormat("auto f(int x) -> decltype(x);", NoSpace); + verifyFormat("auto f(int x) -> typeof(x);", NoSpace); + verifyFormat("auto f(int x) -> _Atomic(x);", NoSpace); verifyFormat("int f(T x) noexcept(x.create());", NoSpace); verifyFormat("alignas(128) char a[128];", NoSpace); verifyFormat("size_t x = alignof(MyType);", NoSpace); @@ -11628,6 +11648,8 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("auto i = std::make_unique (5);", Space); verifyFormat("size_t x = sizeof (x);", Space); verifyFormat("auto f (int x) -> decltype (x);", Space); + verifyFormat("auto f (int x) -> typeof (x);", Space); + verifyFormat("auto f (int x) -> _Atomic (x);", Space); verifyFormat("int f (T x) noexcept (x.create ());", Space); verifyFormat("alignas (128) char a[128];", Space); verifyFormat("size_t x = alignof (MyType);", Space); @@ -11680,6 +11702,8 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("auto i = std::make_unique (5);", SomeSpace); verifyFormat("size_t x = sizeof (x);", SomeSpace); verifyFormat("auto f (int x) -> decltype (x);", SomeSpace); + verifyFormat("auto f (int x) -> typeof (x);", SomeSpace); + verifyFormat("auto f (int x) -> _Atomic (x);", SomeSpace); verifyFormat("int f (T x) noexcept (x.create());", SomeSpace); verifyFormat("alignas (128) char a[128];", SomeSpace); verifyFormat("size_t x = alignof (MyType);", SomeSpace); @@ -14934,6 +14958,8 @@ TEST_F(FormatTest, FormatsLambdas) { "});")); verifyFormat("void f() {\n" " SomeFunction([](decltype(x), A *a) {});\n" + " SomeFunction([](typeof(x), A *a) {});\n" + " SomeFunction([](_Atomic(x), A *a) {});\n" "}"); verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" " [](const aaaaaaaaaa &a) { return a; });"); @@ -16575,6 +16601,45 @@ TEST_F(FormatTest, TypenameMacros) { verifyFormat("vector x;", Macros); } +TEST_F(FormatTest, AtomicQualifier) { + // Check that we treate _Atomic as a type and not a function call + FormatStyle Google = getGoogleStyleWithColumns(0); + verifyFormat("struct foo {\n" + " int a1;\n" + " _Atomic(a) a2;\n" + " _Atomic(_Atomic(int) *const) a3;\n" + "};", + Google); + verifyFormat("_Atomic(uint64_t) a;"); + verifyFormat("_Atomic(uint64_t) *a;"); + verifyFormat("_Atomic(uint64_t const *) *a;"); + verifyFormat("_Atomic(uint64_t *const) *a;"); + verifyFormat("_Atomic(const uint64_t *) *a;"); + verifyFormat("_Atomic(uint64_t) a;"); + verifyFormat("_Atomic(_Atomic(uint64_t)) a;"); + verifyFormat("_Atomic(_Atomic(uint64_t)) a, b;"); + verifyFormat("for (_Atomic(uint64_t) *a = NULL; a;) {\n}"); + verifyFormat("_Atomic(uint64_t) f(_Atomic(uint64_t) *arg);"); + + verifyFormat("_Atomic(uint64_t) *s(InitValue);"); + verifyFormat("_Atomic(uint64_t) *s{InitValue};"); + FormatStyle Style = getLLVMStyle(); + Style.PointerAlignment = FormatStyle::PAS_Left; + verifyFormat("_Atomic(uint64_t)* s(InitValue);", Style); + verifyFormat("_Atomic(uint64_t)* s{InitValue};", Style); + verifyFormat("_Atomic(int)* a;", Style); + verifyFormat("_Atomic(int*)* a;", Style); + verifyFormat("vector<_Atomic(uint64_t)* attr> x;", Style); + + Style.SpacesInCStyleCastParentheses = true; + Style.SpacesInParentheses = false; + verifyFormat("x = ( _Atomic(uint64_t) )*a;", Style); + Style.SpacesInCStyleCastParentheses = false; + Style.SpacesInParentheses = true; + verifyFormat("x = (_Atomic( uint64_t ))*a;", Style); + verifyFormat("x = (_Atomic( uint64_t ))&a;", Style); +} + TEST_F(FormatTest, AmbersandInLamda) { // Test case reported in https://bugs.llvm.org/show_bug.cgi?id=41899 FormatStyle AlignStyle = getLLVMStyle(); From 9a22eba15091ea849fa78c09ac4c9f7260071790 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 7 Sep 2020 09:29:40 +0100 Subject: [PATCH 18/36] [clang-format] Parse __underlying_type(T) as a type Before: MACRO(__underlying_type(A) * a); After: MACRO(__underlying_type(A) *a); Reviewed By: MyDeveloperDay Differential Revision: https://reviews.llvm.org/D86960 --- clang/lib/Format/FormatToken.h | 1 + clang/lib/Format/TokenAnnotator.cpp | 3 ++- clang/unittests/Format/FormatTest.cpp | 11 +++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 8253bf18fc6679..76ef99e72d58ed 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -528,6 +528,7 @@ struct FormatToken { case tok::kw_static_assert: case tok::kw__Atomic: case tok::kw___attribute: + case tok::kw___underlying_type: return true; default: return false; diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 0239dbd63d94e2..4867f9e3d6c1a3 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -247,7 +247,8 @@ class AnnotatingParser { Left->setType(TT_AttributeParen); } else if (PrevNonComment && PrevNonComment->isOneOf(TT_TypenameMacro, tok::kw_decltype, - tok::kw_typeof, tok::kw__Atomic)) { + tok::kw_typeof, tok::kw__Atomic, + tok::kw___underlying_type)) { Left->setType(TT_TypeDeclarationParen); // decltype() and typeof() usually contain expressions. if (PrevNonComment->isOneOf(tok::kw_decltype, tok::kw_typeof)) diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index a5943847882fb4..b1d46a27ef43a1 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -169,6 +169,7 @@ TEST_F(FormatTest, NestedNameSpecifiers) { verifyFormat("::ns::SomeFunction(::ns::SomeOtherFunction())"); verifyFormat("static constexpr bool Bar = decltype(bar())::value;"); verifyFormat("static constexpr bool Bar = typeof(bar())::value;"); + verifyFormat("static constexpr bool Bar = __underlying_type(bar())::value;"); verifyFormat("static constexpr bool Bar = _Atomic(bar())::value;"); verifyFormat("bool a = 2 < ::SomeFunction();"); verifyFormat("ALWAYS_INLINE ::std::string getName();"); @@ -7908,6 +7909,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("[](const decltype(*a) &value) {}"); verifyFormat("[](const typeof(*a) &value) {}"); verifyFormat("[](const _Atomic(a *) &value) {}"); + verifyFormat("[](const __underlying_type(a) &value) {}"); verifyFormat("decltype(a * b) F();"); verifyFormat("typeof(a * b) F();"); verifyFormat("#define MACRO() [](A *a) { return 1; }"); @@ -7977,6 +7979,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("[](const decltype(*a)* ptr) {}", Left); verifyFormat("[](const typeof(*a)* ptr) {}", Left); verifyFormat("[](const _Atomic(a*)* ptr) {}", Left); + verifyFormat("[](const __underlying_type(a)* ptr) {}", Left); verifyFormat("typedef typeof /*comment*/ (int(int, int))* MyFuncPtr;", Left); verifyFormat("auto x(A&&, B&&, C&&) -> D;", Left); verifyFormat("auto x = [](A&&, B&&, C&&) -> D {};", Left); @@ -8075,6 +8078,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyFormat("decltype(*::std::declval()) void F();"); verifyFormat("typeof(*::std::declval()) void F();"); verifyFormat("_Atomic(*::std::declval()) void F();"); + verifyFormat("__underlying_type(*::std::declval()) void F();"); verifyFormat( "template ::value &&\n" @@ -8101,6 +8105,7 @@ TEST_F(FormatTest, UnderstandsUsesOfStarAndAmp) { verifyIndependentOfContext("MACRO(_Atomic(A) *a);"); verifyIndependentOfContext("MACRO(decltype(A) *a);"); verifyIndependentOfContext("MACRO(typeof(A) *a);"); + verifyIndependentOfContext("MACRO(__underlying_type(A) *a);"); verifyIndependentOfContext("MACRO(A *const a);"); verifyIndependentOfContext("MACRO(A *restrict a);"); verifyIndependentOfContext("MACRO(A *__restrict__ a);"); @@ -8655,6 +8660,8 @@ TEST_F(FormatTest, BreaksLongDeclarations) { "LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}"); verifyFormat("_Atomic(LooooooooooooooooooooooooooooooooooooooooongName)\n" "LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}"); + verifyFormat("__underlying_type(LooooooooooooooooooooooooooooooongName)\n" + "LooooooooooooooooooooooooooooooooooongFunctionDefinition() {}"); verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n" "LooooooooooooooooooooooooooongFunctionDeclaration(T... t);"); verifyFormat("LoooooooooooooooooooooooooooooooooooooooongReturnType\n" @@ -11600,6 +11607,7 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("auto f(int x) -> decltype(x);", NoSpace); verifyFormat("auto f(int x) -> typeof(x);", NoSpace); verifyFormat("auto f(int x) -> _Atomic(x);", NoSpace); + verifyFormat("auto f(int x) -> __underlying_type(x);", NoSpace); verifyFormat("int f(T x) noexcept(x.create());", NoSpace); verifyFormat("alignas(128) char a[128];", NoSpace); verifyFormat("size_t x = alignof(MyType);", NoSpace); @@ -11650,6 +11658,7 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("auto f (int x) -> decltype (x);", Space); verifyFormat("auto f (int x) -> typeof (x);", Space); verifyFormat("auto f (int x) -> _Atomic (x);", Space); + verifyFormat("auto f (int x) -> __underlying_type (x);", Space); verifyFormat("int f (T x) noexcept (x.create ());", Space); verifyFormat("alignas (128) char a[128];", Space); verifyFormat("size_t x = alignof (MyType);", Space); @@ -11704,6 +11713,7 @@ TEST_F(FormatTest, ConfigurableSpaceBeforeParens) { verifyFormat("auto f (int x) -> decltype (x);", SomeSpace); verifyFormat("auto f (int x) -> typeof (x);", SomeSpace); verifyFormat("auto f (int x) -> _Atomic (x);", SomeSpace); + verifyFormat("auto f (int x) -> __underlying_type (x);", SomeSpace); verifyFormat("int f (T x) noexcept (x.create());", SomeSpace); verifyFormat("alignas (128) char a[128];", SomeSpace); verifyFormat("size_t x = alignof (MyType);", SomeSpace); @@ -14960,6 +14970,7 @@ TEST_F(FormatTest, FormatsLambdas) { " SomeFunction([](decltype(x), A *a) {});\n" " SomeFunction([](typeof(x), A *a) {});\n" " SomeFunction([](_Atomic(x), A *a) {});\n" + " SomeFunction([](__underlying_type(x), A *a) {});\n" "}"); verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa(\n" " [](const aaaaaaaaaa &a) { return a; });"); From 05147d33091720e2df929d6fea3b0fd2a657ac61 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 7 Sep 2020 09:29:56 +0100 Subject: [PATCH 19/36] [clang-format] Correctly parse function declarations with TypenameMacros When using the always break after return type setting: Before: SomeType funcdecl(LIST(uint64_t)); After: SomeType funcdecl(LIST(uint64_t));" Reviewed By: MyDeveloperDay Differential Revision: https://reviews.llvm.org/D87007 --- clang/lib/Format/TokenAnnotator.cpp | 2 ++ clang/unittests/Format/FormatTest.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 4867f9e3d6c1a3..5dd6a7a9da40b0 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -2400,6 +2400,8 @@ static bool isFunctionDeclarationName(const FormatToken &Current, return true; for (const FormatToken *Tok = Next->Next; Tok && Tok != Next->MatchingParen; Tok = Tok->Next) { + if (Tok->is(TT_TypeDeclarationParen)) + return true; if (Tok->isOneOf(tok::l_paren, TT_TemplateOpener) && Tok->MatchingParen) { Tok = Tok->MatchingParen; continue; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index b1d46a27ef43a1..b198efa4af9ecd 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -6681,9 +6681,12 @@ TEST_F(FormatTest, ReturnTypeBreakingStyle) { Style); // All declarations and definitions should have the return type moved to its - // own - // line. + // own line. Style.AlwaysBreakAfterReturnType = FormatStyle::RTBS_All; + Style.TypenameMacros = {"LIST"}; + verifyFormat("SomeType\n" + "funcdecl(LIST(uint64_t));", + Style); verifyFormat("class E {\n" " int\n" " f() {\n" From 7634c64b6121ba61a6c72c6b45e3561ad8cf345e Mon Sep 17 00:00:00 2001 From: Pushpinder Singh Date: Thu, 3 Sep 2020 07:57:46 -0400 Subject: [PATCH 20/36] [OpenMP][AMDGPU] Use DS_Max_Warp_Number instead of WARPSIZE The size of worker_rootS should have been DS_Max_Warp_Number. This reduces memory usage by deviceRTL on AMDGPU from around 2.3GB to around 770MB. Reviewed By: JonChesterfield, jdoerfert Differential Revision: https://reviews.llvm.org/D87084 --- openmp/libomptarget/deviceRTLs/common/omptarget.h | 2 +- openmp/libomptarget/deviceRTLs/common/src/data_sharing.cu | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openmp/libomptarget/deviceRTLs/common/omptarget.h b/openmp/libomptarget/deviceRTLs/common/omptarget.h index 88807de4e19c76..6d5d6cd19bd6e6 100644 --- a/openmp/libomptarget/deviceRTLs/common/omptarget.h +++ b/openmp/libomptarget/deviceRTLs/common/omptarget.h @@ -252,7 +252,7 @@ class omptarget_nvptx_TeamDescr { workDescrForActiveParallel; // one, ONLY for the active par ALIGN(16) - __kmpc_data_sharing_worker_slot_static worker_rootS[WARPSIZE]; + __kmpc_data_sharing_worker_slot_static worker_rootS[DS_Max_Warp_Number]; ALIGN(16) __kmpc_data_sharing_master_slot_static master_rootS[1]; }; diff --git a/openmp/libomptarget/deviceRTLs/common/src/data_sharing.cu b/openmp/libomptarget/deviceRTLs/common/src/data_sharing.cu index ca2fd1d307542d..9b116aba2fc39c 100644 --- a/openmp/libomptarget/deviceRTLs/common/src/data_sharing.cu +++ b/openmp/libomptarget/deviceRTLs/common/src/data_sharing.cu @@ -26,7 +26,7 @@ INLINE static void data_sharing_init_stack_common() { omptarget_nvptx_TeamDescr *teamDescr = &omptarget_nvptx_threadPrivateContext->TeamContext(); - for (int WID = 0; WID < WARPSIZE; WID++) { + for (int WID = 0; WID < DS_Max_Warp_Number; WID++) { __kmpc_data_sharing_slot *RootS = teamDescr->GetPreallocatedSlotAddr(WID); DataSharingState.SlotPtr[WID] = RootS; DataSharingState.StackPtr[WID] = (void *)&RootS->Data[0]; From 8d64df9f139038b48344dd9f1f20a38b22aba8c9 Mon Sep 17 00:00:00 2001 From: Nicolas Vasilache Date: Fri, 4 Sep 2020 11:43:00 -0400 Subject: [PATCH 21/36] [mlir][Vector] Revisit VectorToSCF. Vector to SCF conversion still had issues due to the interaction with the natural alignment derived by the LLVM data layout. One traditional workaround is to allocate aligned. However, this does not always work for vector sizes that are non-powers of 2. This revision implements a more portable mechanism where the intermediate allocation is always a memref of elemental vector type. AllocOp is extended to use the natural LLVM DataLayout alignment for non-scalar types, when the alignment is not specified in the first place. An integration test is added that exercises the transfer to scf.for + scalar lowering with a 5x5 transposition. Differential Revision: https://reviews.llvm.org/D87150 --- .../mlir/Dialect/Vector/EDSC/Intrinsics.h | 17 ++-- mlir/include/mlir/Dialect/Vector/VectorOps.td | 29 +++++-- .../Vector/CPU/test-transfer-to-loops.mlir | 81 +++++++++++++++++++ .../StandardToLLVM/StandardToLLVM.cpp | 14 +++- .../Conversion/VectorToSCF/VectorToSCF.cpp | 35 ++++---- mlir/lib/Dialect/Vector/VectorOps.cpp | 24 ++++++ .../convert-static-memref-ops.mlir | 6 +- .../VectorToSCF/vector-to-loops.mlir | 31 +++---- mlir/test/EDSC/builder-api-test.cpp | 2 +- 9 files changed, 186 insertions(+), 53 deletions(-) create mode 100644 mlir/integration_test/Dialect/Vector/CPU/test-transfer-to-loops.mlir diff --git a/mlir/include/mlir/Dialect/Vector/EDSC/Intrinsics.h b/mlir/include/mlir/Dialect/Vector/EDSC/Intrinsics.h index f353262750345f..269d9c1b27af03 100644 --- a/mlir/include/mlir/Dialect/Vector/EDSC/Intrinsics.h +++ b/mlir/include/mlir/Dialect/Vector/EDSC/Intrinsics.h @@ -16,10 +16,16 @@ namespace intrinsics { using vector_broadcast = ValueBuilder; using vector_contract = ValueBuilder; -using vector_insert = ValueBuilder; -using vector_fma = ValueBuilder; using vector_extract = ValueBuilder; -using vector_extractelement = ValueBuilder; +using vector_extract_element = ValueBuilder; +using vector_extract_slices = ValueBuilder; +using vector_extract_strided_slice = + ValueBuilder; +using vector_fma = ValueBuilder; +using vector_insert = ValueBuilder; +using vector_insert_element = ValueBuilder; +using vector_insert_slices = ValueBuilder; +using vector_insert_strided_slice = ValueBuilder; using vector_matmul = ValueBuilder; using vector_outerproduct = ValueBuilder; using vector_print = OperationBuilder; @@ -27,11 +33,6 @@ using vector_transfer_read = ValueBuilder; using vector_transfer_write = OperationBuilder; using vector_transpose = ValueBuilder; using vector_type_cast = ValueBuilder; -using vector_extract_slices = ValueBuilder; -using vector_insert_slices = ValueBuilder; -using vector_extract_strided_slice = - ValueBuilder; -using vector_insert_strided_slice = ValueBuilder; } // namespace intrinsics } // namespace edsc diff --git a/mlir/include/mlir/Dialect/Vector/VectorOps.td b/mlir/include/mlir/Dialect/Vector/VectorOps.td index 22fd036df81488..dceb850ad929cd 100644 --- a/mlir/include/mlir/Dialect/Vector/VectorOps.td +++ b/mlir/include/mlir/Dialect/Vector/VectorOps.td @@ -348,15 +348,21 @@ def Vector_ExtractElementOp : %1 = vector.extractelement %0[%c : i32]: vector<16xf32> ``` }]; + let assemblyFormat = [{ + $vector `[` $position `:` type($position) `]` attr-dict `:` type($vector) + }]; + + let builders = [OpBuilder< + "OpBuilder &builder, OperationState &result, Value source, " + "int64_t position">, + OpBuilder< + "OpBuilder &builder, OperationState &result, Value source, " + "Value position">]; let extraClassDeclaration = [{ VectorType getVectorType() { return vector().getType().cast(); } }]; - - let assemblyFormat = [{ - $vector `[` $position `:` type($position) `]` attr-dict `:` type($vector) - }]; } def Vector_ExtractOp : @@ -508,6 +514,17 @@ def Vector_InsertElementOp : %1 = vector.insertelement %f, %0[%c : i32]: vector<16xf32> ``` }]; + let assemblyFormat = [{ + $source `,` $dest `[` $position `:` type($position) `]` attr-dict `:` + type($result) + }]; + + let builders = [OpBuilder< + "OpBuilder &builder, OperationState &result, Value source, " + "Value dest, int64_t position">, + OpBuilder< + "OpBuilder &builder, OperationState &result, Value source, " + "Value dest, Value position">]; let extraClassDeclaration = [{ Type getSourceType() { return source().getType(); } VectorType getDestVectorType() { @@ -515,10 +532,6 @@ def Vector_InsertElementOp : } }]; - let assemblyFormat = [{ - $source `,` $dest `[` $position `:` type($position) `]` attr-dict `:` - type($result) - }]; } def Vector_InsertOp : diff --git a/mlir/integration_test/Dialect/Vector/CPU/test-transfer-to-loops.mlir b/mlir/integration_test/Dialect/Vector/CPU/test-transfer-to-loops.mlir new file mode 100644 index 00000000000000..8d965779dfc6df --- /dev/null +++ b/mlir/integration_test/Dialect/Vector/CPU/test-transfer-to-loops.mlir @@ -0,0 +1,81 @@ +// RUN: mlir-opt %s -convert-vector-to-scf -lower-affine -convert-scf-to-std -convert-vector-to-llvm | \ +// RUN: mlir-cpu-runner -e main -entry-point-result=void \ +// RUN: -shared-libs=%mlir_integration_test_dir/libmlir_runner_utils%shlibext,%mlir_integration_test_dir/libmlir_c_runner_utils%shlibext | \ +// RUN: FileCheck %s + +#map0 = affine_map<(d0, d1) -> (d1, d0)> + +func @print_memref_f32(memref<*xf32>) + +func @alloc_2d_filled_f32(%arg0: index, %arg1: index) -> memref { + %c0 = constant 0 : index + %c1 = constant 1 : index + %c10 = constant 10 : index + %c100 = constant 100 : index + %0 = alloc(%arg0, %arg1) : memref + scf.for %arg5 = %c0 to %arg0 step %c1 { + scf.for %arg6 = %c0 to %arg1 step %c1 { + %arg66 = muli %arg6, %c100 : index + %tmp1 = addi %arg5, %arg66 : index + %tmp2 = index_cast %tmp1 : index to i32 + %tmp3 = sitofp %tmp2 : i32 to f32 + store %tmp3, %0[%arg5, %arg6] : memref + } + } + return %0 : memref +} + +func @main() { + %c0 = constant 0 : index + %c1 = constant 1 : index + %c2 = constant 2 : index + %c6 = constant 6 : index + %cst = constant -4.2e+01 : f32 + %0 = call @alloc_2d_filled_f32(%c6, %c6) : (index, index) -> memref + %converted = memref_cast %0 : memref to memref<*xf32> + call @print_memref_f32(%converted): (memref<*xf32>) -> () + // CHECK: Unranked{{.*}}data = + // CHECK: [ + // CHECK-SAME: [0, 100, 200, 300, 400, 500], + // CHECK-NEXT: [1, 101, 201, 301, 401, 501], + // CHECK-NEXT: [2, 102, 202, 302, 402, 502], + // CHECK-NEXT: [3, 103, 203, 303, 403, 503], + // CHECK-NEXT: [4, 104, 204, 304, 404, 504], + // CHECK-NEXT: [5, 105, 205, 305, 405, 505]] + + %init = vector.transfer_read %0[%c1, %c1], %cst : memref, vector<5x5xf32> + vector.print %init : vector<5x5xf32> + // 5x5 block rooted at {1, 1} + // CHECK-NEXT: ( ( 101, 201, 301, 401, 501 ), + // CHECK-SAME: ( 102, 202, 302, 402, 502 ), + // CHECK-SAME: ( 103, 203, 303, 403, 503 ), + // CHECK-SAME: ( 104, 204, 304, 404, 504 ), + // CHECK-SAME: ( 105, 205, 305, 405, 505 ) ) + + %1 = vector.transfer_read %0[%c1, %c1], %cst {permutation_map = #map0} : memref, vector<5x5xf32> + vector.print %1 : vector<5x5xf32> + // Transposed 5x5 block rooted @{1, 1} in memory. + // CHECK-NEXT: ( ( 101, 102, 103, 104, 105 ), + // CHECK-SAME: ( 201, 202, 203, 204, 205 ), + // CHECK-SAME: ( 301, 302, 303, 304, 305 ), + // CHECK-SAME: ( 401, 402, 403, 404, 405 ), + // CHECK-SAME: ( 501, 502, 503, 504, 505 ) ) + + // Transpose-write the transposed 5x5 block @{0, 0} in memory. + vector.transfer_write %1, %0[%c0, %c0] {permutation_map = #map0} : vector<5x5xf32>, memref + + %2 = vector.transfer_read %0[%c1, %c1], %cst : memref, vector<5x5xf32> + vector.print %2 : vector<5x5xf32> + // New 5x5 block rooted @{1, 1} in memory. + // Here we expect the boundaries from the original data + // (i.e. last row: 105 .. 505, last col: 501 .. 505) + // and the 4x4 subblock 202 .. 505 rooted @{0, 0} in the vector + // CHECK-NEXT: ( ( 202, 302, 402, 502, 501 ), + // CHECK-SAME: ( 203, 303, 403, 503, 502 ), + // CHECK-SAME: ( 204, 304, 404, 504, 503 ), + // CHECK-SAME: ( 205, 305, 405, 505, 504 ), + // CHECK-SAME: ( 105, 205, 305, 405, 505 ) ) + + dealloc %0 : memref + return +} diff --git a/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp b/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp index 401509f1f8a60d..55a926ef1423d8 100644 --- a/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp +++ b/mlir/lib/Conversion/StandardToLLVM/StandardToLLVM.cpp @@ -1893,11 +1893,17 @@ struct AllocLikeOpLowering : public ConvertOpToLLVMPattern { // Adjust the allocation size to consider alignment. if (Optional alignment = allocOp.alignment()) { accessAlignment = createIndexConstant(rewriter, loc, *alignment); - cumulativeSize = rewriter.create( - loc, - rewriter.create(loc, cumulativeSize, accessAlignment), - one); + } else if (!memRefType.getElementType().isSignlessIntOrIndexOrFloat()) { + // In the case where no alignment is specified, we may want to override + // `malloc's` behavior. `malloc` typically aligns at the size of the + // biggest scalar on a target HW. For non-scalars, use the natural + // alignment of the LLVM type given by the LLVM DataLayout. + accessAlignment = + this->getSizeInBytes(loc, memRefType.getElementType(), rewriter); } + if (accessAlignment) + cumulativeSize = + rewriter.create(loc, cumulativeSize, accessAlignment); callArgs.push_back(cumulativeSize); } auto allocFuncSymbol = rewriter.getSymbolRefAttr(allocFunc); diff --git a/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp b/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp index 3c501f046f0743..8f7d43829846b8 100644 --- a/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp +++ b/mlir/lib/Conversion/VectorToSCF/VectorToSCF.cpp @@ -35,8 +35,6 @@ #include "mlir/Pass/Pass.h" #include "mlir/Transforms/Passes.h" -#define ALIGNMENT_SIZE 128 - using namespace mlir; using namespace mlir::edsc; using namespace mlir::edsc::intrinsics; @@ -234,8 +232,7 @@ static Value setAllocAtFunctionEntry(MemRefType memRefMinorVectorType, op->getParentWithTrait(); assert(scope && "Expected op to be inside automatic allocation scope"); b.setInsertionPointToStart(&scope->getRegion(0).front()); - Value res = std_alloca(memRefMinorVectorType, ValueRange{}, - b.getI64IntegerAttr(ALIGNMENT_SIZE)); + Value res = std_alloca(memRefMinorVectorType); return res; } @@ -494,8 +491,10 @@ template MemRefType VectorTransferRewriter::tmpMemRefType( TransferOpTy transfer) const { auto vectorType = transfer.getVectorType(); - return MemRefType::get(vectorType.getShape(), vectorType.getElementType(), {}, - 0); + return MemRefType::get(vectorType.getShape().drop_back(), + VectorType::get(vectorType.getShape().take_back(), + vectorType.getElementType()), + {}, 0); } /// Lowers TransferReadOp into a combination of: @@ -585,8 +584,7 @@ LogicalResult VectorTransferRewriter::matchAndRewrite( steps.push_back(std_constant_index(step)); // 2. Emit alloc-copy-load-dealloc. - Value tmp = std_alloc(tmpMemRefType(transfer), ValueRange{}, - rewriter.getI64IntegerAttr(ALIGNMENT_SIZE)); + Value tmp = setAllocAtFunctionEntry(tmpMemRefType(transfer), transfer); StdIndexedValue local(tmp); Value vec = vector_type_cast(tmp); loopNestBuilder(lbs, ubs, steps, [&](ValueRange loopIvs) { @@ -595,10 +593,15 @@ LogicalResult VectorTransferRewriter::matchAndRewrite( if (coalescedIdx >= 0) std::swap(ivs.back(), ivs[coalescedIdx]); // Computes clippedScalarAccessExprs in the loop nest scope (ivs exist). - local(ivs) = remote(clip(transfer, memRefBoundsCapture, ivs)); + SmallVector indices = clip(transfer, memRefBoundsCapture, ivs); + ArrayRef indicesRef(indices), ivsRef(ivs); + Value pos = + std_index_cast(IntegerType::get(32, op->getContext()), ivsRef.back()); + Value vector = vector_insert_element(remote(indicesRef), + local(ivsRef.drop_back()), pos); + local(ivsRef.drop_back()) = vector; }); Value vectorValue = std_load(vec); - (std_dealloc(tmp)); // vexing parse // 3. Propagate. rewriter.replaceOp(op, vectorValue); @@ -667,8 +670,7 @@ LogicalResult VectorTransferRewriter::matchAndRewrite( steps.push_back(std_constant_index(step)); // 2. Emit alloc-store-copy-dealloc. - Value tmp = std_alloc(tmpMemRefType(transfer), ValueRange{}, - rewriter.getI64IntegerAttr(ALIGNMENT_SIZE)); + Value tmp = setAllocAtFunctionEntry(tmpMemRefType(transfer), transfer); StdIndexedValue local(tmp); Value vec = vector_type_cast(tmp); std_store(vectorValue, vec); @@ -678,10 +680,15 @@ LogicalResult VectorTransferRewriter::matchAndRewrite( if (coalescedIdx >= 0) std::swap(ivs.back(), ivs[coalescedIdx]); // Computes clippedScalarAccessExprs in the loop nest scope (ivs exist). - remote(clip(transfer, memRefBoundsCapture, ivs)) = local(ivs); + SmallVector indices = clip(transfer, memRefBoundsCapture, ivs); + ArrayRef indicesRef(indices), ivsRef(ivs); + Value pos = + std_index_cast(IntegerType::get(32, op->getContext()), ivsRef.back()); + Value scalar = vector_extract_element(local(ivsRef.drop_back()), pos); + remote(indices) = scalar; }); - (std_dealloc(tmp)); // vexing parse... + // 3. Erase. rewriter.eraseOp(op); return success(); } diff --git a/mlir/lib/Dialect/Vector/VectorOps.cpp b/mlir/lib/Dialect/Vector/VectorOps.cpp index 7fa62ea34de19f..d00e56297532c7 100644 --- a/mlir/lib/Dialect/Vector/VectorOps.cpp +++ b/mlir/lib/Dialect/Vector/VectorOps.cpp @@ -537,6 +537,18 @@ Optional> ContractionOp::getShapeForUnroll() { // ExtractElementOp //===----------------------------------------------------------------------===// +void vector::ExtractElementOp::build(OpBuilder &builder, OperationState &result, + Value source, Value position) { + result.addOperands({source, position}); + result.addTypes(source.getType().cast().getElementType()); +} + +void vector::ExtractElementOp::build(OpBuilder &builder, OperationState &result, + Value source, int64_t position) { + Value pos = builder.create(result.location, position, 32); + build(builder, result, source, pos); +} + static LogicalResult verify(vector::ExtractElementOp op) { VectorType vectorType = op.getVectorType(); if (vectorType.getRank() != 1) @@ -1007,6 +1019,18 @@ static ParseResult parseShuffleOp(OpAsmParser &parser, OperationState &result) { // InsertElementOp //===----------------------------------------------------------------------===// +void InsertElementOp::build(OpBuilder &builder, OperationState &result, + Value source, Value dest, Value position) { + result.addOperands({source, dest, position}); + result.addTypes(dest.getType()); +} + +void InsertElementOp::build(OpBuilder &builder, OperationState &result, + Value source, Value dest, int64_t position) { + Value pos = builder.create(result.location, position, 32); + build(builder, result, source, dest, pos); +} + static LogicalResult verify(InsertElementOp op) { auto dstVectorType = op.getDestVectorType(); if (dstVectorType.getRank() != 1) diff --git a/mlir/test/Conversion/StandardToLLVM/convert-static-memref-ops.mlir b/mlir/test/Conversion/StandardToLLVM/convert-static-memref-ops.mlir index b428d37a361672..5cccca3795b3b0 100644 --- a/mlir/test/Conversion/StandardToLLVM/convert-static-memref-ops.mlir +++ b/mlir/test/Conversion/StandardToLLVM/convert-static-memref-ops.mlir @@ -130,8 +130,7 @@ func @aligned_1d_alloc() -> memref<42xf32> { // CHECK-NEXT: llvm.mul %{{.*}}, %[[sizeof]] : !llvm.i64 // CHECK-NEXT: %[[one_1:.*]] = llvm.mlir.constant(1 : index) : !llvm.i64 // CHECK-NEXT: %[[alignment:.*]] = llvm.mlir.constant(8 : index) : !llvm.i64 -// CHECK-NEXT: %[[alignmentMinus1:.*]] = llvm.add {{.*}}, %[[alignment]] : !llvm.i64 -// CHECK-NEXT: %[[allocsize:.*]] = llvm.sub %[[alignmentMinus1]], %[[one_1]] : !llvm.i64 +// CHECK-NEXT: %[[allocsize:.*]] = llvm.add {{.*}}, %[[alignment]] : !llvm.i64 // CHECK-NEXT: %[[allocated:.*]] = llvm.call @malloc(%[[allocsize]]) : (!llvm.i64) -> !llvm.ptr // CHECK-NEXT: %[[ptr:.*]] = llvm.bitcast %{{.*}} : !llvm.ptr to !llvm.ptr // CHECK-NEXT: llvm.mlir.undef : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)> @@ -154,8 +153,7 @@ func @aligned_1d_alloc() -> memref<42xf32> { // BAREPTR-NEXT: llvm.mul %{{.*}}, %[[sizeof]] : !llvm.i64 // BAREPTR-NEXT: %[[one_1:.*]] = llvm.mlir.constant(1 : index) : !llvm.i64 // BAREPTR-NEXT: %[[alignment:.*]] = llvm.mlir.constant(8 : index) : !llvm.i64 -// BAREPTR-NEXT: %[[alignmentMinus1:.*]] = llvm.add {{.*}}, %[[alignment]] : !llvm.i64 -// BAREPTR-NEXT: %[[allocsize:.*]] = llvm.sub %[[alignmentMinus1]], %[[one_1]] : !llvm.i64 +// BAREPTR-NEXT: %[[allocsize:.*]] = llvm.add {{.*}}, %[[alignment]] : !llvm.i64 // BAREPTR-NEXT: %[[allocated:.*]] = llvm.call @malloc(%[[allocsize]]) : (!llvm.i64) -> !llvm.ptr // BAREPTR-NEXT: %[[ptr:.*]] = llvm.bitcast %{{.*}} : !llvm.ptr to !llvm.ptr // BAREPTR-NEXT: llvm.mlir.undef : !llvm.struct<(ptr, ptr, i64, array<1 x i64>, array<1 x i64>)> diff --git a/mlir/test/Conversion/VectorToSCF/vector-to-loops.mlir b/mlir/test/Conversion/VectorToSCF/vector-to-loops.mlir index 986bfe17635154..1a8d1a68a126c3 100644 --- a/mlir/test/Conversion/VectorToSCF/vector-to-loops.mlir +++ b/mlir/test/Conversion/VectorToSCF/vector-to-loops.mlir @@ -19,7 +19,7 @@ func @materialize_read_1d() { // CHECK: %[[FILTERED1:.*]] = select // CHECK: {{.*}} = select // CHECK: %[[FILTERED2:.*]] = select - // CHECK-NEXT: %{{.*}} = load {{.*}}[%[[FILTERED1]], %[[FILTERED2]]] : memref<7x42xf32> + // CHECK: %{{.*}} = load {{.*}}[%[[FILTERED1]], %[[FILTERED2]]] : memref<7x42xf32> } } return @@ -58,6 +58,7 @@ func @materialize_read_1d_partially_specialized(%dyn1 : index, %dyn2 : index, %d // CHECK-LABEL: func @materialize_read(%{{.*}}: index, %{{.*}}: index, %{{.*}}: index, %{{.*}}: index) { func @materialize_read(%M: index, %N: index, %O: index, %P: index) { %f0 = constant 0.0: f32 + // CHECK-DAG: %[[ALLOC:.*]] = alloca() : memref<5x4xvector<3xf32>> // CHECK-DAG: %[[C0:.*]] = constant 0 : index // CHECK-DAG: %[[C1:.*]] = constant 1 : index // CHECK-DAG: %[[C3:.*]] = constant 3 : index @@ -68,7 +69,6 @@ func @materialize_read(%M: index, %N: index, %O: index, %P: index) { // CHECK-NEXT: affine.for %[[I1:.*]] = 0 to %{{.*}} { // CHECK-NEXT: affine.for %[[I2:.*]] = 0 to %{{.*}} { // CHECK-NEXT: affine.for %[[I3:.*]] = 0 to %{{.*}} step 5 { - // CHECK: %[[ALLOC:.*]] = alloc() {alignment = 128 : i64} : memref<5x4x3xf32> // CHECK-NEXT: scf.for %[[I4:.*]] = %[[C0]] to %[[C3]] step %[[C1]] { // CHECK-NEXT: scf.for %[[I5:.*]] = %[[C0]] to %[[C4]] step %[[C1]] { // CHECK-NEXT: scf.for %[[I6:.*]] = %[[C0]] to %[[C5]] step %[[C1]] { @@ -97,13 +97,15 @@ func @materialize_read(%M: index, %N: index, %O: index, %P: index) { // CHECK-NEXT: {{.*}} = select // CHECK-NEXT: {{.*}} = cmpi "slt", {{.*}}, %[[C0]] : index // CHECK-NEXT: %[[L3:.*]] = select + // CHECK-NEXT: %[[VIDX:.*]] = index_cast %[[I4]] // // CHECK-NEXT: {{.*}} = load %{{.*}}[%[[L0]], %[[L1]], %[[L2]], %[[L3]]] : memref - // CHECK-NEXT: store {{.*}}, %[[ALLOC]][%[[I6]], %[[I5]], %[[I4]]] : memref<5x4x3xf32> + // CHECK-NEXT: %[[VEC:.*]] = load %[[ALLOC]][%[[I6]], %[[I5]]] : memref<5x4xvector<3xf32>> + // CHECK-NEXT: %[[RVEC:.*]] = vector.insertelement %25, %[[VEC]][%[[VIDX]] : i32] : vector<3xf32> + // CHECK-NEXT: store %[[RVEC]], %[[ALLOC]][%[[I6]], %[[I5]]] : memref<5x4xvector<3xf32>> // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: } - // CHECK-NEXT: dealloc %[[ALLOC]] : memref<5x4x3xf32> // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: } @@ -134,6 +136,7 @@ func @materialize_read(%M: index, %N: index, %O: index, %P: index) { // CHECK-LABEL:func @materialize_write(%{{.*}}: index, %{{.*}}: index, %{{.*}}: index, %{{.*}}: index) { func @materialize_write(%M: index, %N: index, %O: index, %P: index) { + // CHECK-DAG: %[[ALLOC:.*]] = alloca() : memref<5x4xvector<3xf32>> // CHECK-DAG: %{{.*}} = constant dense<1.000000e+00> : vector<5x4x3xf32> // CHECK-DAG: %[[C0:.*]] = constant 0 : index // CHECK-DAG: %[[C1:.*]] = constant 1 : index @@ -145,8 +148,7 @@ func @materialize_write(%M: index, %N: index, %O: index, %P: index) { // CHECK-NEXT: affine.for %[[I1:.*]] = 0 to %{{.*}} step 4 { // CHECK-NEXT: affine.for %[[I2:.*]] = 0 to %{{.*}} { // CHECK-NEXT: affine.for %[[I3:.*]] = 0 to %{{.*}} step 5 { - // CHECK: %[[ALLOC:.*]] = alloc() {alignment = 128 : i64} : memref<5x4x3xf32> - // CHECK-NEXT: %[[VECTOR_VIEW:.*]] = vector.type_cast {{.*}} : memref<5x4x3xf32> + // CHECK-NEXT: %[[VECTOR_VIEW:.*]] = vector.type_cast {{.*}} : memref<5x4xvector<3xf32>> // CHECK: store %{{.*}}, {{.*}} : memref> // CHECK-NEXT: scf.for %[[I4:.*]] = %[[C0]] to %[[C3]] step %[[C1]] { // CHECK-NEXT: scf.for %[[I5:.*]] = %[[C0]] to %[[C4]] step %[[C1]] { @@ -177,13 +179,14 @@ func @materialize_write(%M: index, %N: index, %O: index, %P: index) { // CHECK-NEXT: {{.*}} = select {{.*}}, {{.*}}, {{.*}} : index // CHECK-NEXT: {{.*}} = cmpi "slt", {{.*}}, %[[C0]] : index // CHECK-NEXT: %[[S3:.*]] = select {{.*}}, %[[C0]], {{.*}} : index + // CHECK-NEXT: %[[VIDX:.*]] = index_cast %[[I4]] // - // CHECK-NEXT: {{.*}} = load {{.*}}[%[[I6]], %[[I5]], %[[I4]]] : memref<5x4x3xf32> - // CHECK: store {{.*}}, {{.*}}[%[[S0]], %[[S1]], %[[S2]], %[[S3]]] : memref + // CHECK-NEXT: %[[VEC:.*]] = load {{.*}}[%[[I6]], %[[I5]]] : memref<5x4xvector<3xf32>> + // CHECK-NEXT: %[[SCAL:.*]] = vector.extractelement %[[VEC]][%[[VIDX]] : i32] : vector<3xf32> + // CHECK: store %[[SCAL]], {{.*}}[%[[S0]], %[[S1]], %[[S2]], %[[S3]]] : memref // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: } - // CHECK-NEXT: dealloc {{.*}} : memref<5x4x3xf32> // CHECK-NEXT: } // CHECK-NEXT: } // CHECK-NEXT: } @@ -232,7 +235,7 @@ func @transfer_read_progressive(%A : memref, %base: index) -> vector<3x %f7 = constant 7.0: f32 // CHECK-DAG: %[[splat:.*]] = constant dense<7.000000e+00> : vector<15xf32> - // CHECK-DAG: %[[alloc:.*]] = alloca() {alignment = 128 : i64} : memref<3xvector<15xf32>> + // CHECK-DAG: %[[alloc:.*]] = alloca() : memref<3xvector<15xf32>> // CHECK-DAG: %[[C0:.*]] = constant 0 : index // CHECK-DAG: %[[dim:.*]] = dim %[[A]], %[[C0]] : memref // CHECK: affine.for %[[I:.*]] = 0 to 3 { @@ -307,7 +310,7 @@ func @transfer_read_progressive(%A : memref, %base: index) -> vector<3x // FULL-UNROLL-SAME: %[[vec:[a-zA-Z0-9]+]]: vector<3x15xf32> func @transfer_write_progressive(%A : memref, %base: index, %vec: vector<3x15xf32>) { // CHECK: %[[C0:.*]] = constant 0 : index - // CHECK: %[[alloc:.*]] = alloca() {alignment = 128 : i64} : memref<3xvector<15xf32>> + // CHECK: %[[alloc:.*]] = alloca() : memref<3xvector<15xf32>> // CHECK: %[[vmemref:.*]] = vector.type_cast %[[alloc]] : memref<3xvector<15xf32>> to memref> // CHECK: store %[[vec]], %[[vmemref]][] : memref> // CHECK: %[[dim:.*]] = dim %[[A]], %[[C0]] : memref @@ -363,7 +366,7 @@ func @transfer_write_progressive(%A : memref, %base: index, %vec: vecto // FULL-UNROLL-SAME: %[[vec:[a-zA-Z0-9]+]]: vector<3x15xf32> func @transfer_write_progressive_unmasked(%A : memref, %base: index, %vec: vector<3x15xf32>) { // CHECK-NOT: scf.if - // CHECK-NEXT: %[[alloc:.*]] = alloca() {alignment = 128 : i64} : memref<3xvector<15xf32>> + // CHECK-NEXT: %[[alloc:.*]] = alloca() : memref<3xvector<15xf32>> // CHECK-NEXT: %[[vmemref:.*]] = vector.type_cast %[[alloc]] : memref<3xvector<15xf32>> to memref> // CHECK-NEXT: store %[[vec]], %[[vmemref]][] : memref> // CHECK-NEXT: affine.for %[[I:.*]] = 0 to 3 { @@ -416,7 +419,7 @@ func @transfer_read_minor_identity(%A : memref) -> vector<3x3xf32> // CHECK: %[[cst:.*]] = constant 0.000000e+00 : f32 // CHECK: %[[c2:.*]] = constant 2 : index // CHECK: %[[cst0:.*]] = constant dense<0.000000e+00> : vector<3xf32> -// CHECK: %[[m:.*]] = alloca() {alignment = 128 : i64} : memref<3xvector<3xf32>> +// CHECK: %[[m:.*]] = alloca() : memref<3xvector<3xf32>> // CHECK: %[[d:.*]] = dim %[[A]], %[[c2]] : memref // CHECK: affine.for %[[arg1:.*]] = 0 to 3 { // CHECK: %[[cmp:.*]] = cmpi "slt", %[[arg1]], %[[d]] : index @@ -445,7 +448,7 @@ func @transfer_write_minor_identity(%A : vector<3x3xf32>, %B : memref) // CHECK: %[[c0:.*]] = constant 0 : index // CHECK: %[[c2:.*]] = constant 2 : index -// CHECK: %[[m:.*]] = alloca() {alignment = 128 : i64} : memref<3xvector<3xf32>> +// CHECK: %[[m:.*]] = alloca() : memref<3xvector<3xf32>> // CHECK: %[[cast:.*]] = vector.type_cast %[[m]] : memref<3xvector<3xf32>> to memref> // CHECK: store %[[A]], %[[cast]][] : memref> // CHECK: %[[d:.*]] = dim %[[B]], %[[c2]] : memref diff --git a/mlir/test/EDSC/builder-api-test.cpp b/mlir/test/EDSC/builder-api-test.cpp index 062e4b5912297a..4695090dacb52b 100644 --- a/mlir/test/EDSC/builder-api-test.cpp +++ b/mlir/test/EDSC/builder-api-test.cpp @@ -1089,7 +1089,7 @@ TEST_FUNC(vector_extractelement_op_i32) { ScopedContext scope(builder, f.getLoc()); auto i32Type = builder.getI32Type(); auto vectorType = VectorType::get(/*shape=*/{8}, i32Type); - vector_extractelement( + vector_extract_element( i32Type, std_constant(vectorType, builder.getI32VectorAttr({10})), std_constant_int(0, i32Type)); From 81aa66f65f504af18982baa078a5f3f7d2aa88fa Mon Sep 17 00:00:00 2001 From: Eduardo Caldas Date: Fri, 28 Aug 2020 11:52:54 +0000 Subject: [PATCH 22/36] Extract infrastructure to ignore intermediate expressions into `clang/AST/IgnoreExpr.h` Rationale: This allows users to use `IgnoreExprNodes` and `Ignore*SingleStep` outside of `clang/AST/Expr.cpp`. Minor: Rename `IgnoreImp...SingleStep` into `IgnoreImplicit...SingleStep`. Differential Revision: https://reviews.llvm.org/D86778 --- clang/include/clang/AST/IgnoreExpr.h | 61 ++++++++++++ clang/lib/AST/CMakeLists.txt | 1 + clang/lib/AST/Expr.cpp | 138 +-------------------------- clang/lib/AST/IgnoreExpr.cpp | 129 +++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 134 deletions(-) create mode 100644 clang/include/clang/AST/IgnoreExpr.h create mode 100644 clang/lib/AST/IgnoreExpr.cpp diff --git a/clang/include/clang/AST/IgnoreExpr.h b/clang/include/clang/AST/IgnoreExpr.h new file mode 100644 index 00000000000000..15d31f3af99546 --- /dev/null +++ b/clang/include/clang/AST/IgnoreExpr.h @@ -0,0 +1,61 @@ +//===--- IgnoreExpr.h - Ignore intermediate Expressions -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines common functions to ignore intermediate expression nodes +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_IGNOREEXPR_H +#define LLVM_CLANG_AST_IGNOREEXPR_H + +#include "clang/AST/Expr.h" + +namespace clang { +namespace detail { +/// Given an expression E and functions Fn_1,...,Fn_n : Expr * -> Expr *, +/// Return Fn_n(...(Fn_1(E))) +inline Expr *IgnoreExprNodesImpl(Expr *E) { return E; }; +template +Expr *IgnoreExprNodesImpl(Expr *E, FnTy &&Fn, FnTys &&... Fns) { + return IgnoreExprNodesImpl(Fn(E), std::forward(Fns)...); +} +} // namespace detail + +/// Given an expression E and functions Fn_1,...,Fn_n : Expr * -> Expr *, +/// Recursively apply each of the functions to E until reaching a fixed point. +/// Note that a null E is valid; in this case nothing is done. +template Expr *IgnoreExprNodes(Expr *E, FnTys &&... Fns) { + Expr *LastE = nullptr; + while (E != LastE) { + LastE = E; + E = detail::IgnoreExprNodesImpl(E, std::forward(Fns)...); + } + return E; +} + +Expr *IgnoreImplicitCastsSingleStep(Expr *E); + +Expr *IgnoreImplicitCastsExtraSingleStep(Expr *E); + +Expr *IgnoreCastsSingleStep(Expr *E); + +Expr *IgnoreLValueCastsSingleStep(Expr *E); + +Expr *IgnoreBaseCastsSingleStep(Expr *E); + +Expr *IgnoreImplicitSingleStep(Expr *E); + +Expr *IgnoreImplicitAsWrittenSingleStep(Expr *E); + +Expr *IgnoreParensOnlySingleStep(Expr *E); + +Expr *IgnoreParensSingleStep(Expr *E); + +} // namespace clang + +#endif // LLVM_CLANG_AST_IGNOREEXPR_H diff --git a/clang/lib/AST/CMakeLists.txt b/clang/lib/AST/CMakeLists.txt index 35099fd0dacf83..dfd26fd97bc6d8 100644 --- a/clang/lib/AST/CMakeLists.txt +++ b/clang/lib/AST/CMakeLists.txt @@ -55,6 +55,7 @@ add_clang_library(clangAST ExternalASTMerger.cpp ExternalASTSource.cpp FormatString.cpp + IgnoreExpr.cpp InheritViz.cpp Interp/ByteCodeEmitter.cpp Interp/ByteCodeExprGen.cpp diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 8efd6837c541bf..1029acbf68cd16 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -21,6 +21,7 @@ #include "clang/AST/DependenceFlags.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/IgnoreExpr.h" #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" @@ -2779,118 +2780,6 @@ QualType Expr::findBoundMemberType(const Expr *expr) { return QualType(); } -static Expr *IgnoreImpCastsSingleStep(Expr *E) { - if (auto *ICE = dyn_cast(E)) - return ICE->getSubExpr(); - - if (auto *FE = dyn_cast(E)) - return FE->getSubExpr(); - - return E; -} - -static Expr *IgnoreImpCastsExtraSingleStep(Expr *E) { - // FIXME: Skip MaterializeTemporaryExpr and SubstNonTypeTemplateParmExpr in - // addition to what IgnoreImpCasts() skips to account for the current - // behaviour of IgnoreParenImpCasts(). - Expr *SubE = IgnoreImpCastsSingleStep(E); - if (SubE != E) - return SubE; - - if (auto *MTE = dyn_cast(E)) - return MTE->getSubExpr(); - - if (auto *NTTP = dyn_cast(E)) - return NTTP->getReplacement(); - - return E; -} - -static Expr *IgnoreCastsSingleStep(Expr *E) { - if (auto *CE = dyn_cast(E)) - return CE->getSubExpr(); - - if (auto *FE = dyn_cast(E)) - return FE->getSubExpr(); - - if (auto *MTE = dyn_cast(E)) - return MTE->getSubExpr(); - - if (auto *NTTP = dyn_cast(E)) - return NTTP->getReplacement(); - - return E; -} - -static Expr *IgnoreLValueCastsSingleStep(Expr *E) { - // Skip what IgnoreCastsSingleStep skips, except that only - // lvalue-to-rvalue casts are skipped. - if (auto *CE = dyn_cast(E)) - if (CE->getCastKind() != CK_LValueToRValue) - return E; - - return IgnoreCastsSingleStep(E); -} - -static Expr *IgnoreBaseCastsSingleStep(Expr *E) { - if (auto *CE = dyn_cast(E)) - if (CE->getCastKind() == CK_DerivedToBase || - CE->getCastKind() == CK_UncheckedDerivedToBase || - CE->getCastKind() == CK_NoOp) - return CE->getSubExpr(); - - return E; -} - -static Expr *IgnoreImplicitSingleStep(Expr *E) { - Expr *SubE = IgnoreImpCastsSingleStep(E); - if (SubE != E) - return SubE; - - if (auto *MTE = dyn_cast(E)) - return MTE->getSubExpr(); - - if (auto *BTE = dyn_cast(E)) - return BTE->getSubExpr(); - - return E; -} - -static Expr *IgnoreImplicitAsWrittenSingleStep(Expr *E) { - if (auto *ICE = dyn_cast(E)) - return ICE->getSubExprAsWritten(); - - return IgnoreImplicitSingleStep(E); -} - -static Expr *IgnoreParensOnlySingleStep(Expr *E) { - if (auto *PE = dyn_cast(E)) - return PE->getSubExpr(); - return E; -} - -static Expr *IgnoreParensSingleStep(Expr *E) { - if (auto *PE = dyn_cast(E)) - return PE->getSubExpr(); - - if (auto *UO = dyn_cast(E)) { - if (UO->getOpcode() == UO_Extension) - return UO->getSubExpr(); - } - - else if (auto *GSE = dyn_cast(E)) { - if (!GSE->isResultDependent()) - return GSE->getResultExpr(); - } - - else if (auto *CE = dyn_cast(E)) { - if (!CE->isConditionDependent()) - return CE->getChosenSubExpr(); - } - - return E; -} - static Expr *IgnoreNoopCastsSingleStep(const ASTContext &Ctx, Expr *E) { if (auto *CE = dyn_cast(E)) { // We ignore integer <-> casts that are of the same width, ptr<->ptr and @@ -2914,27 +2803,8 @@ static Expr *IgnoreNoopCastsSingleStep(const ASTContext &Ctx, Expr *E) { return E; } -static Expr *IgnoreExprNodesImpl(Expr *E) { return E; } -template -static Expr *IgnoreExprNodesImpl(Expr *E, FnTy &&Fn, FnTys &&... Fns) { - return IgnoreExprNodesImpl(Fn(E), std::forward(Fns)...); -} - -/// Given an expression E and functions Fn_1,...,Fn_n : Expr * -> Expr *, -/// Recursively apply each of the functions to E until reaching a fixed point. -/// Note that a null E is valid; in this case nothing is done. -template -static Expr *IgnoreExprNodes(Expr *E, FnTys &&... Fns) { - Expr *LastE = nullptr; - while (E != LastE) { - LastE = E; - E = IgnoreExprNodesImpl(E, std::forward(Fns)...); - } - return E; -} - Expr *Expr::IgnoreImpCasts() { - return IgnoreExprNodes(this, IgnoreImpCastsSingleStep); + return IgnoreExprNodes(this, IgnoreImplicitCastsSingleStep); } Expr *Expr::IgnoreCasts() { @@ -2955,7 +2825,7 @@ Expr *Expr::IgnoreParens() { Expr *Expr::IgnoreParenImpCasts() { return IgnoreExprNodes(this, IgnoreParensSingleStep, - IgnoreImpCastsExtraSingleStep); + IgnoreImplicitCastsExtraSingleStep); } Expr *Expr::IgnoreParenCasts() { @@ -2993,7 +2863,7 @@ Expr *Expr::IgnoreUnlessSpelledInSource() { while (E != LastE) { LastE = E; E = IgnoreExprNodes(E, IgnoreImplicitSingleStep, - IgnoreImpCastsExtraSingleStep, + IgnoreImplicitCastsExtraSingleStep, IgnoreParensOnlySingleStep); auto SR = E->getSourceRange(); diff --git a/clang/lib/AST/IgnoreExpr.cpp b/clang/lib/AST/IgnoreExpr.cpp new file mode 100644 index 00000000000000..65aaaeb6a1ed00 --- /dev/null +++ b/clang/lib/AST/IgnoreExpr.cpp @@ -0,0 +1,129 @@ +//===--- IgnoreExpr.cpp - Ignore intermediate Expressions -----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements common functions to ignore intermediate expression nodes +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/IgnoreExpr.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" + +using namespace clang; + +Expr *clang::IgnoreImplicitCastsSingleStep(Expr *E) { + if (auto *ICE = dyn_cast(E)) + return ICE->getSubExpr(); + + if (auto *FE = dyn_cast(E)) + return FE->getSubExpr(); + + return E; +} + +Expr *clang::IgnoreImplicitCastsExtraSingleStep(Expr *E) { + // FIXME: Skip MaterializeTemporaryExpr and SubstNonTypeTemplateParmExpr in + // addition to what IgnoreImpCasts() skips to account for the current + // behaviour of IgnoreParenImpCasts(). + Expr *SubE = IgnoreImplicitCastsSingleStep(E); + if (SubE != E) + return SubE; + + if (auto *MTE = dyn_cast(E)) + return MTE->getSubExpr(); + + if (auto *NTTP = dyn_cast(E)) + return NTTP->getReplacement(); + + return E; +} + +Expr *clang::IgnoreCastsSingleStep(Expr *E) { + if (auto *CE = dyn_cast(E)) + return CE->getSubExpr(); + + if (auto *FE = dyn_cast(E)) + return FE->getSubExpr(); + + if (auto *MTE = dyn_cast(E)) + return MTE->getSubExpr(); + + if (auto *NTTP = dyn_cast(E)) + return NTTP->getReplacement(); + + return E; +} + +Expr *clang::IgnoreLValueCastsSingleStep(Expr *E) { + // Skip what IgnoreCastsSingleStep skips, except that only + // lvalue-to-rvalue casts are skipped. + if (auto *CE = dyn_cast(E)) + if (CE->getCastKind() != CK_LValueToRValue) + return E; + + return IgnoreCastsSingleStep(E); +} + +Expr *clang::IgnoreBaseCastsSingleStep(Expr *E) { + if (auto *CE = dyn_cast(E)) + if (CE->getCastKind() == CK_DerivedToBase || + CE->getCastKind() == CK_UncheckedDerivedToBase || + CE->getCastKind() == CK_NoOp) + return CE->getSubExpr(); + + return E; +} + +Expr *clang::IgnoreImplicitSingleStep(Expr *E) { + Expr *SubE = IgnoreImplicitCastsSingleStep(E); + if (SubE != E) + return SubE; + + if (auto *MTE = dyn_cast(E)) + return MTE->getSubExpr(); + + if (auto *BTE = dyn_cast(E)) + return BTE->getSubExpr(); + + return E; +} + +Expr *clang::IgnoreImplicitAsWrittenSingleStep(Expr *E) { + if (auto *ICE = dyn_cast(E)) + return ICE->getSubExprAsWritten(); + + return IgnoreImplicitSingleStep(E); +} + +Expr *clang::IgnoreParensOnlySingleStep(Expr *E) { + if (auto *PE = dyn_cast(E)) + return PE->getSubExpr(); + return E; +} + +Expr *clang::IgnoreParensSingleStep(Expr *E) { + if (auto *PE = dyn_cast(E)) + return PE->getSubExpr(); + + if (auto *UO = dyn_cast(E)) { + if (UO->getOpcode() == UO_Extension) + return UO->getSubExpr(); + } + + else if (auto *GSE = dyn_cast(E)) { + if (!GSE->isResultDependent()) + return GSE->getResultExpr(); + } + + else if (auto *CE = dyn_cast(E)) { + if (!CE->isConditionDependent()) + return CE->getChosenSubExpr(); + } + + return E; +} From 1a7a2cd7474e6d321120ffe7ca9c52163eb228f0 Mon Sep 17 00:00:00 2001 From: Eduardo Caldas Date: Mon, 31 Aug 2020 16:03:31 +0000 Subject: [PATCH 23/36] [Ignore Expressions][NFC] Refactor to better use `IgnoreExpr.h` and nits This change groups * Rename: `ignoreParenBaseCasts` -> `IgnoreParenBaseCasts` for uniformity * Rename: `IgnoreConversionOperator` -> `IgnoreConversionOperatorSingleStep` for uniformity * Inline `IgnoreNoopCastsSingleStep` into a lambda inside `IgnoreNoopCasts` * Refactor `IgnoreUnlessSpelledInSource` to make adequate use of `IgnoreExprNodes` Differential Revision: https://reviews.llvm.org/D86880 --- .../clang-tidy/modernize/UseAutoCheck.cpp | 2 +- .../readability/SimplifyBooleanExprCheck.cpp | 2 +- clang/include/clang/AST/Expr.h | 12 +-- clang/lib/AST/Expr.cpp | 97 +++++++++---------- clang/lib/CodeGen/CGExprCXX.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 2 +- clang/lib/StaticAnalyzer/Core/CallEvent.cpp | 2 +- 7 files changed, 55 insertions(+), 64 deletions(-) diff --git a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp index 04dc61f02df1ec..44ae380b63b2e4 100644 --- a/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp +++ b/clang-tools-extra/clang-tidy/modernize/UseAutoCheck.cpp @@ -338,7 +338,7 @@ void UseAutoCheck::replaceIterators(const DeclStmt *D, ASTContext *Context) { // Drill down to the as-written initializer. const Expr *E = (*Construct->arg_begin())->IgnoreParenImpCasts(); - if (E != E->IgnoreConversionOperator()) { + if (E != E->IgnoreConversionOperatorSingleStep()) { // We hit a conversion operator. Early-out now as they imply an implicit // conversion from a different type. Could also mean an explicit // conversion from the same type but that's pretty rare. diff --git a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp index 9dcb10b9d20c47..7e8ba4eb90c652 100644 --- a/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp +++ b/clang-tools-extra/clang-tidy/readability/SimplifyBooleanExprCheck.cpp @@ -205,7 +205,7 @@ std::string compareExpressionToZero(const MatchFinder::MatchResult &Result, std::string replacementExpression(const MatchFinder::MatchResult &Result, bool Negated, const Expr *E) { - E = E->ignoreParenBaseCasts(); + E = E->IgnoreParenBaseCasts(); if (const auto *EC = dyn_cast(E)) E = EC->getSubExpr(); diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h index 5edca259378960..26e52ad367f816 100644 --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -867,9 +867,9 @@ class Expr : public ValueStmt { /// Skip conversion operators. If this Expr is a call to a conversion /// operator, return the argument. - Expr *IgnoreConversionOperator() LLVM_READONLY; - const Expr *IgnoreConversionOperator() const { - return const_cast(this)->IgnoreConversionOperator(); + Expr *IgnoreConversionOperatorSingleStep() LLVM_READONLY; + const Expr *IgnoreConversionOperatorSingleStep() const { + return const_cast(this)->IgnoreConversionOperatorSingleStep(); } /// Skip past any parentheses and lvalue casts which might surround this @@ -901,9 +901,9 @@ class Expr : public ValueStmt { /// * What IgnoreParens() skips /// * CastExpr which represent a derived-to-base cast (CK_DerivedToBase, /// CK_UncheckedDerivedToBase and CK_NoOp) - Expr *ignoreParenBaseCasts() LLVM_READONLY; - const Expr *ignoreParenBaseCasts() const { - return const_cast(this)->ignoreParenBaseCasts(); + Expr *IgnoreParenBaseCasts() LLVM_READONLY; + const Expr *IgnoreParenBaseCasts() const { + return const_cast(this)->IgnoreParenBaseCasts(); } /// Determine whether this expression is a default function argument. diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index 1029acbf68cd16..15f3df0fd21685 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -40,7 +40,7 @@ using namespace clang; const Expr *Expr::getBestDynamicClassTypeExpr() const { const Expr *E = this; while (true) { - E = E->ignoreParenBaseCasts(); + E = E->IgnoreParenBaseCasts(); // Follow the RHS of a comma operator. if (auto *BO = dyn_cast(E)) { @@ -2780,29 +2780,6 @@ QualType Expr::findBoundMemberType(const Expr *expr) { return QualType(); } -static Expr *IgnoreNoopCastsSingleStep(const ASTContext &Ctx, Expr *E) { - if (auto *CE = dyn_cast(E)) { - // We ignore integer <-> casts that are of the same width, ptr<->ptr and - // ptr<->int casts of the same width. We also ignore all identity casts. - Expr *SubExpr = CE->getSubExpr(); - bool IsIdentityCast = - Ctx.hasSameUnqualifiedType(E->getType(), SubExpr->getType()); - bool IsSameWidthCast = - (E->getType()->isPointerType() || E->getType()->isIntegralType(Ctx)) && - (SubExpr->getType()->isPointerType() || - SubExpr->getType()->isIntegralType(Ctx)) && - (Ctx.getTypeSize(E->getType()) == Ctx.getTypeSize(SubExpr->getType())); - - if (IsIdentityCast || IsSameWidthCast) - return SubExpr; - } - - else if (auto *NTTP = dyn_cast(E)) - return NTTP->getReplacement(); - - return E; -} - Expr *Expr::IgnoreImpCasts() { return IgnoreExprNodes(this, IgnoreImplicitCastsSingleStep); } @@ -2832,7 +2809,7 @@ Expr *Expr::IgnoreParenCasts() { return IgnoreExprNodes(this, IgnoreParensSingleStep, IgnoreCastsSingleStep); } -Expr *Expr::IgnoreConversionOperator() { +Expr *Expr::IgnoreConversionOperatorSingleStep() { if (auto *MCE = dyn_cast(this)) { if (MCE->getMethodDecl() && isa(MCE->getMethodDecl())) return MCE->getImplicitObjectArgument(); @@ -2845,58 +2822,72 @@ Expr *Expr::IgnoreParenLValueCasts() { IgnoreLValueCastsSingleStep); } -Expr *Expr::ignoreParenBaseCasts() { +Expr *Expr::IgnoreParenBaseCasts() { return IgnoreExprNodes(this, IgnoreParensSingleStep, IgnoreBaseCastsSingleStep); } Expr *Expr::IgnoreParenNoopCasts(const ASTContext &Ctx) { - return IgnoreExprNodes(this, IgnoreParensSingleStep, [&Ctx](Expr *E) { - return IgnoreNoopCastsSingleStep(Ctx, E); - }); + auto IgnoreNoopCastsSingleStep = [&Ctx](Expr *E) { + if (auto *CE = dyn_cast(E)) { + // We ignore integer <-> casts that are of the same width, ptr<->ptr and + // ptr<->int casts of the same width. We also ignore all identity casts. + Expr *SubExpr = CE->getSubExpr(); + bool IsIdentityCast = + Ctx.hasSameUnqualifiedType(E->getType(), SubExpr->getType()); + bool IsSameWidthCast = (E->getType()->isPointerType() || + E->getType()->isIntegralType(Ctx)) && + (SubExpr->getType()->isPointerType() || + SubExpr->getType()->isIntegralType(Ctx)) && + (Ctx.getTypeSize(E->getType()) == + Ctx.getTypeSize(SubExpr->getType())); + + if (IsIdentityCast || IsSameWidthCast) + return SubExpr; + } else if (auto *NTTP = dyn_cast(E)) + return NTTP->getReplacement(); + + return E; + }; + return IgnoreExprNodes(this, IgnoreParensSingleStep, + IgnoreNoopCastsSingleStep); } Expr *Expr::IgnoreUnlessSpelledInSource() { - Expr *E = this; - - Expr *LastE = nullptr; - while (E != LastE) { - LastE = E; - E = IgnoreExprNodes(E, IgnoreImplicitSingleStep, - IgnoreImplicitCastsExtraSingleStep, - IgnoreParensOnlySingleStep); - - auto SR = E->getSourceRange(); - + auto IgnoreImplicitConstructorSingleStep = [](Expr *E) { if (auto *C = dyn_cast(E)) { auto NumArgs = C->getNumArgs(); if (NumArgs == 1 || (NumArgs > 1 && isa(C->getArg(1)))) { Expr *A = C->getArg(0); - if (A->getSourceRange() == SR || !isa(C)) - E = A; + if (A->getSourceRange() == E->getSourceRange() || + !isa(C)) + return A; } } - + return E; + }; + auto IgnoreImplicitMemberCallSingleStep = [](Expr *E) { if (auto *C = dyn_cast(E)) { Expr *ExprNode = C->getImplicitObjectArgument(); - if (ExprNode->getSourceRange() == SR) { - E = ExprNode; - continue; + if (ExprNode->getSourceRange() == E->getSourceRange()) { + return ExprNode; } if (auto *PE = dyn_cast(ExprNode)) { if (PE->getSourceRange() == C->getSourceRange()) { - E = PE; - continue; + return cast(PE); } } ExprNode = ExprNode->IgnoreParenImpCasts(); - if (ExprNode->getSourceRange() == SR) - E = ExprNode; + if (ExprNode->getSourceRange() == E->getSourceRange()) + return ExprNode; } - } - - return E; + return E; + }; + return IgnoreExprNodes( + this, IgnoreImplicitSingleStep, IgnoreImplicitCastsExtraSingleStep, + IgnoreParensOnlySingleStep, IgnoreImplicitConstructorSingleStep, + IgnoreImplicitMemberCallSingleStep); } bool Expr::isDefaultArgument() const { diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index d0e0c7d6c0603f..50b6079bd80bfc 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -220,7 +220,7 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorMemberCallExpr( DevirtualizedMethod = MD->getCorrespondingMethodInClass(BestDynamicDecl); assert(DevirtualizedMethod); const CXXRecordDecl *DevirtualizedClass = DevirtualizedMethod->getParent(); - const Expr *Inner = Base->ignoreParenBaseCasts(); + const Expr *Inner = Base->IgnoreParenBaseCasts(); if (DevirtualizedMethod->getReturnType().getCanonicalType() != MD->getReturnType().getCanonicalType()) // If the return types are not the same, this might be a case where more diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index cd71ce70c70efa..d6f0a12106fe04 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -8372,7 +8372,7 @@ static bool IsArithmeticBinaryExpr(Expr *E, BinaryOperatorKind *Opcode, Expr **RHSExprs) { // Don't strip parenthesis: we should not warn if E is in parenthesis. E = E->IgnoreImpCasts(); - E = E->IgnoreConversionOperator(); + E = E->IgnoreConversionOperatorSingleStep(); E = E->IgnoreImpCasts(); if (auto *MTE = dyn_cast(E)) { E = MTE->getSubExpr(); diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp index 78d13ddfb773ca..a55d9302ca5877 100644 --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -687,7 +687,7 @@ void CXXInstanceCall::getExtraInvalidatedValues( // base class decl, rather than the class of the instance which needs to be // checked for mutable fields. // TODO: We might as well look at the dynamic type of the object. - const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts(); + const Expr *Ex = getCXXThisExpr()->IgnoreParenBaseCasts(); QualType T = Ex->getType(); if (T->isPointerType()) // Arrow or implicit-this syntax? T = T->getPointeeType(); From 0dbe2504af81fc8ac7438d490b98370740442805 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Mon, 7 Sep 2020 10:28:01 +0100 Subject: [PATCH 24/36] [X86] Use Register instead of unsigned. NFCI. Fixes llvm-prefer-register-over-unsigned clang-tidy warning. --- llvm/lib/Target/X86/X86AsmPrinter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/X86/X86AsmPrinter.cpp b/llvm/lib/Target/X86/X86AsmPrinter.cpp index aa03217d155d5e..75b2368ce1850d 100644 --- a/llvm/lib/Target/X86/X86AsmPrinter.cpp +++ b/llvm/lib/Target/X86/X86AsmPrinter.cpp @@ -448,7 +448,7 @@ static bool printAsmMRegister(X86AsmPrinter &P, const MachineOperand &MO, static bool printAsmVRegister(X86AsmPrinter &P, const MachineOperand &MO, char Mode, raw_ostream &O) { - unsigned Reg = MO.getReg(); + Register Reg = MO.getReg(); bool EmitPercent = MO.getParent()->getInlineAsmDialect() == InlineAsm::AD_ATT; unsigned Index; From 22fa6b20d92efe796ad881aafe6e689960fe6e7d Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Mon, 7 Sep 2020 10:30:53 +0100 Subject: [PATCH 25/36] [X86] Use Register instead of unsigned. NFCI. Fixes llvm-prefer-register-over-unsigned clang-tidy warnings. --- llvm/lib/Target/X86/X86FrameLowering.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index c7ca6fb2a4fcf3..d7a377e0c6ba85 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -490,7 +490,7 @@ void X86FrameLowering::emitCalleeSavedFrameMoves( } const MachineModuleInfo &MMI = MF.getMMI(); const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo(); - const unsigned FramePtr = TRI->getFrameRegister(MF); + const Register FramePtr = TRI->getFrameRegister(MF); const unsigned MachineFramePtr = STI.isTarget64BitILP32() ? unsigned(getX86SubSuperRegister(FramePtr, 64)) : FramePtr; @@ -1788,7 +1788,7 @@ void X86FrameLowering::emitEpilogue(MachineFunction &MF, // standard x86_64 and NaCl use 64-bit frame/stack pointers, x32 - 32-bit. const bool Is64BitILP32 = STI.isTarget64BitILP32(); Register FramePtr = TRI->getFrameRegister(MF); - unsigned MachineFramePtr = + Register MachineFramePtr = Is64BitILP32 ? Register(getX86SubSuperRegister(FramePtr, 64)) : FramePtr; bool IsWin64Prologue = MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); From ee68b66d94b50d8c9ff14d3217a77c66b0e2c32f Mon Sep 17 00:00:00 2001 From: Esme-Yi Date: Mon, 7 Sep 2020 09:45:47 +0000 Subject: [PATCH 26/36] [NFC][PowerPC] Add tests for `mul` with big constants. --- llvm/test/CodeGen/PowerPC/mulli.ll | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 llvm/test/CodeGen/PowerPC/mulli.ll diff --git a/llvm/test/CodeGen/PowerPC/mulli.ll b/llvm/test/CodeGen/PowerPC/mulli.ll new file mode 100644 index 00000000000000..3e417f9720a842 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/mulli.ll @@ -0,0 +1,70 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --extra_scrub +; RUN: llc -verify-machineinstrs -mcpu=pwr9 -mtriple=powerpc64le-unknown-linux-gnu < %s | FileCheck %s + +define i64 @test1(i64 %x) { +; CHECK-LABEL: test1: +; CHECK: # %bb.0: +; CHECK-NEXT: li 4, 625 +; CHECK-NEXT: sldi 4, 4, 36 +; CHECK-NEXT: mulld 3, 3, 4 +; CHECK-NEXT: blr + %y = mul i64 %x, 42949672960000 + ret i64 %y +} + +define i64 @test2(i64 %x) { +; CHECK-LABEL: test2: +; CHECK: # %bb.0: +; CHECK-NEXT: li 4, -625 +; CHECK-NEXT: sldi 4, 4, 36 +; CHECK-NEXT: mulld 3, 3, 4 +; CHECK-NEXT: blr + %y = mul i64 %x, -42949672960000 + ret i64 %y +} + +define i64 @test3(i64 %x) { +; CHECK-LABEL: test3: +; CHECK: # %bb.0: +; CHECK-NEXT: lis 4, 74 +; CHECK-NEXT: ori 4, 4, 16384 +; CHECK-NEXT: mulld 3, 3, 4 +; CHECK-NEXT: blr + %y = mul i64 %x, 4866048 + ret i64 %y +} + +define i64 @test4(i64 %x) { +; CHECK-LABEL: test4: +; CHECK: # %bb.0: +; CHECK-NEXT: lis 4, -75 +; CHECK-NEXT: ori 4, 4, 49152 +; CHECK-NEXT: mulld 3, 3, 4 +; CHECK-NEXT: blr + %y = mul i64 %x, -4866048 + ret i64 %y +} + +define i64 @test5(i64 %x) { +; CHECK-LABEL: test5: +; CHECK: # %bb.0: +; CHECK-NEXT: lis 4, 16 +; CHECK-NEXT: ori 4, 4, 1 +; CHECK-NEXT: sldi 4, 4, 12 +; CHECK-NEXT: mulld 3, 3, 4 +; CHECK-NEXT: blr + %y = mul i64 %x, 4294971392 + ret i64 %y +} + +define i64 @test6(i64 %x) { +; CHECK-LABEL: test6: +; CHECK: # %bb.0: +; CHECK-NEXT: lis 4, -17 +; CHECK-NEXT: ori 4, 4, 65535 +; CHECK-NEXT: sldi 4, 4, 12 +; CHECK-NEXT: mulld 3, 3, 4 +; CHECK-NEXT: blr + %y = mul i64 %x, -4294971392 + ret i64 %y +} From 9ad261540da6e66a666e48fed95455bc27fa995b Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Mon, 7 Sep 2020 10:49:29 +0100 Subject: [PATCH 27/36] [X86] Use Register instead of unsigned. NFCI. Fixes llvm-prefer-register-over-unsigned clang-tidy warnings. --- llvm/lib/Target/X86/X86InstrInfo.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Target/X86/X86InstrInfo.cpp b/llvm/lib/Target/X86/X86InstrInfo.cpp index 3c24f51ba36b11..5aac29e21d6f98 100644 --- a/llvm/lib/Target/X86/X86InstrInfo.cpp +++ b/llvm/lib/Target/X86/X86InstrInfo.cpp @@ -6712,7 +6712,7 @@ unsigned X86InstrInfo::getGlobalBaseReg(MachineFunction *MF) const { "X86-64 PIC uses RIP relative addressing"); X86MachineFunctionInfo *X86FI = MF->getInfo(); - unsigned GlobalBaseReg = X86FI->getGlobalBaseReg(); + Register GlobalBaseReg = X86FI->getGlobalBaseReg(); if (GlobalBaseReg != 0) return GlobalBaseReg; @@ -8268,7 +8268,7 @@ describeMOVrrLoadedValue(const MachineInstr &MI, Register DescribedReg, // If the described register is a sub-register of the destination register, // then pick out the source register's corresponding sub-register. if (unsigned SubRegIdx = TRI->getSubRegIndex(DestReg, DescribedReg)) { - unsigned SrcSubReg = TRI->getSubReg(SrcReg, SubRegIdx); + Register SrcSubReg = TRI->getSubReg(SrcReg, SubRegIdx); return ParamLoadedValue(MachineOperand::CreateReg(SrcSubReg, false), Expr); } @@ -8532,7 +8532,7 @@ namespace { return false; X86MachineFunctionInfo *X86FI = MF.getInfo(); - unsigned GlobalBaseReg = X86FI->getGlobalBaseReg(); + Register GlobalBaseReg = X86FI->getGlobalBaseReg(); // If we didn't need a GlobalBaseReg, don't insert code. if (GlobalBaseReg == 0) @@ -8545,7 +8545,7 @@ namespace { MachineRegisterInfo &RegInfo = MF.getRegInfo(); const X86InstrInfo *TII = STI.getInstrInfo(); - unsigned PC; + Register PC; if (STI.isPICStyleGOT()) PC = RegInfo.createVirtualRegister(&X86::GR32RegClass); else @@ -8615,7 +8615,7 @@ namespace { MachineFunctionPass::getAnalysisUsage(AU); } }; -} +} // namespace char CGBR::ID = 0; FunctionPass* From aa3fcb967110f2d448d241358cadc048954e6134 Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Mon, 7 Sep 2020 10:58:53 +0100 Subject: [PATCH 28/36] [X86][AVX] Add extra vperm2f128+vpermilvar combine coverage The existing test /should/ reduce to a vmovaps (concat xmm with zero upper). --- .../CodeGen/X86/vector-shuffle-combining-avx.ll | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll b/llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll index 6ffbe095c39baf..d4ef76a2a9cff5 100644 --- a/llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll +++ b/llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll @@ -142,8 +142,19 @@ define <8 x float> @combine_vpermilvar_vperm2f128_zero_8f32(<8 x float> %a0) { ret <8 x float> %3 } -define <4 x double> @combine_vperm2f128_vpermilvar_as_vpblendpd(<4 x double> %a0) { -; CHECK-LABEL: combine_vperm2f128_vpermilvar_as_vpblendpd: +define <4 x double> @combine_vperm2f128_vpermilvar_as_vperm2f128(<4 x double> %a0) { +; CHECK-LABEL: combine_vperm2f128_vpermilvar_as_vperm2f128: +; CHECK: # %bb.0: +; CHECK-NEXT: vperm2f128 {{.*#+}} ymm0 = zero,zero,ymm0[0,1] +; CHECK-NEXT: ret{{[l|q]}} + %1 = tail call <4 x double> @llvm.x86.avx.vpermilvar.pd.256(<4 x double> %a0, <4 x i64> ) + %2 = shufflevector <4 x double> %1, <4 x double> zeroinitializer, <4 x i32> + %3 = tail call <4 x double> @llvm.x86.avx.vpermilvar.pd.256(<4 x double> %2, <4 x i64> ) + ret <4 x double> %3 +} + +define <4 x double> @combine_vperm2f128_vpermilvar_as_vmovaps(<4 x double> %a0) { +; CHECK-LABEL: combine_vperm2f128_vpermilvar_as_vmovaps: ; CHECK: # %bb.0: ; CHECK-NEXT: vpermilpd {{.*#+}} xmm0 = xmm0[1,0] ; CHECK-NEXT: vpermilpd {{.*#+}} ymm0 = ymm0[1,0,3,2] From 71dfdbe2c73afcc319bfd96c9e73407ea9245e3a Mon Sep 17 00:00:00 2001 From: Simon Pilgrim Date: Mon, 7 Sep 2020 11:10:40 +0100 Subject: [PATCH 29/36] [X86] getFauxShuffleMask - handle insert_subvector(zero, sub, C) Directly use SM_SentinelZero elements if we're (widening)inserting into a zero vector. --- llvm/lib/Target/X86/X86ISelLowering.cpp | 7 +++++-- llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll | 3 +-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp index 99d35f0c91ffaa..09855fd0eb925f 100644 --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -7452,8 +7452,11 @@ static bool getFauxShuffleMask(SDValue N, const APInt &DemandedElts, } Ops.push_back(Src); Ops.append(SubInputs.begin(), SubInputs.end()); - for (int i = 0; i != (int)NumElts; ++i) - Mask.push_back(i); + if (ISD::isBuildVectorAllZeros(Src.getNode())) + Mask.append(NumElts, SM_SentinelZero); + else + for (int i = 0; i != (int)NumElts; ++i) + Mask.push_back(i); for (int i = 0; i != (int)NumSubElts; ++i) { int M = SubMask[i]; if (0 <= M) { diff --git a/llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll b/llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll index d4ef76a2a9cff5..e744dbd1033621 100644 --- a/llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll +++ b/llvm/test/CodeGen/X86/vector-shuffle-combining-avx.ll @@ -156,8 +156,7 @@ define <4 x double> @combine_vperm2f128_vpermilvar_as_vperm2f128(<4 x double> %a define <4 x double> @combine_vperm2f128_vpermilvar_as_vmovaps(<4 x double> %a0) { ; CHECK-LABEL: combine_vperm2f128_vpermilvar_as_vmovaps: ; CHECK: # %bb.0: -; CHECK-NEXT: vpermilpd {{.*#+}} xmm0 = xmm0[1,0] -; CHECK-NEXT: vpermilpd {{.*#+}} ymm0 = ymm0[1,0,3,2] +; CHECK-NEXT: vmovaps %xmm0, %xmm0 ; CHECK-NEXT: ret{{[l|q]}} %1 = tail call <4 x double> @llvm.x86.avx.vpermilvar.pd.256(<4 x double> %a0, <4 x i64> ) %2 = shufflevector <4 x double> %1, <4 x double> zeroinitializer, <4 x i32> From 7ba0f81934ca5f4baa1d81ac0032f2e4ff6614ec Mon Sep 17 00:00:00 2001 From: Benjamin Kramer Date: Mon, 7 Sep 2020 12:24:30 +0200 Subject: [PATCH 30/36] [X86] Unbreak the build after 22fa6b20d92e --- llvm/lib/Target/X86/X86FrameLowering.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/X86/X86FrameLowering.cpp b/llvm/lib/Target/X86/X86FrameLowering.cpp index d7a377e0c6ba85..7437c2e978af28 100644 --- a/llvm/lib/Target/X86/X86FrameLowering.cpp +++ b/llvm/lib/Target/X86/X86FrameLowering.cpp @@ -491,8 +491,8 @@ void X86FrameLowering::emitCalleeSavedFrameMoves( const MachineModuleInfo &MMI = MF.getMMI(); const MCRegisterInfo *MRI = MMI.getContext().getRegisterInfo(); const Register FramePtr = TRI->getFrameRegister(MF); - const unsigned MachineFramePtr = - STI.isTarget64BitILP32() ? unsigned(getX86SubSuperRegister(FramePtr, 64)) + const Register MachineFramePtr = + STI.isTarget64BitILP32() ? Register(getX86SubSuperRegister(FramePtr, 64)) : FramePtr; unsigned DwarfReg = MRI->getDwarfRegNum(MachineFramePtr, true); // Offset = space for return address + size of the frame pointer itself. From 56d1f3138b532f4e195a5aaba9ea65a8bcb8adb4 Mon Sep 17 00:00:00 2001 From: LLVM GN Syncbot Date: Mon, 7 Sep 2020 10:25:26 +0000 Subject: [PATCH 31/36] [gn build] Port 81aa66f65f5 --- llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn index 4d645799dbf655..bb3d69d046bef1 100644 --- a/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/AST/BUILD.gn @@ -81,6 +81,7 @@ static_library("AST") { "ExternalASTMerger.cpp", "ExternalASTSource.cpp", "FormatString.cpp", + "IgnoreExpr.cpp", "InheritViz.cpp", "Interp/ByteCodeEmitter.cpp", "Interp/ByteCodeExprGen.cpp", From 0478720157f6413fad7595b8eff9c70d2d99b637 Mon Sep 17 00:00:00 2001 From: Raphael Isemann Date: Mon, 7 Sep 2020 11:23:39 +0200 Subject: [PATCH 32/36] [clang] Prevent that Decl::dump on a CXXRecordDecl deserialises further declarations. Decl::dump is primarily used for debugging to visualise the current state of a declaration. Usually Decl::dump just displays the current state of the Decl and doesn't actually change any of its state, however since commit 457226e02a6e8533eaaa864a3fd7c8eeccd2bf58 the method actually started loading additional declarations from the ExternalASTSource. This causes that calling Decl::dump during a debugging session now actually does permanent changes to the AST and will cause the debugged program run to deviate from the original run. The change that caused this behaviour is the addition of `hasConstexprDestructor` (which is called from the TextNodeDumper) which performs a lookup into the current CXXRecordDecl to find the destructor. All other similar methods just return their respective bit in the DefinitionData (which obviously doesn't have such side effects). This just changes the node printer to emit "unknown_constexpr" in case a CXXRecordDecl is dumped that could potentially call into the ExternalASTSource instead of the usually empty string/"constexpr". For CXXRecordDecls that can safely be dumped the old behaviour is preserved Reviewed By: bruno Differential Revision: https://reviews.llvm.org/D80878 --- clang/lib/AST/TextNodeDumper.cpp | 6 +- clang/test/AST/ast-dump-lambda.cpp | 32 +++---- clang/test/AST/ast-dump-records.cpp | 22 ++--- clang/unittests/AST/ASTDumpTest.cpp | 140 ++++++++++++++++++++++++++++ clang/unittests/AST/CMakeLists.txt | 1 + 5 files changed, 173 insertions(+), 28 deletions(-) create mode 100644 clang/unittests/AST/ASTDumpTest.cpp diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 16c4c3736a4a33..19b7b4c801d553 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -1960,7 +1960,11 @@ void TextNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *D) { FLAG(hasTrivialDestructor, trivial); FLAG(hasNonTrivialDestructor, non_trivial); FLAG(hasUserDeclaredDestructor, user_declared); - FLAG(hasConstexprDestructor, constexpr); + // Avoid calls to the external source. + if (!D->hasExternalVisibleStorage()) { + FLAG(hasConstexprDestructor, constexpr); + } else + OS << " maybe_constexpr"; FLAG(needsImplicitDestructor, needs_implicit); FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution); if (!D->needsOverloadResolutionForDestructor()) diff --git a/clang/test/AST/ast-dump-lambda.cpp b/clang/test/AST/ast-dump-lambda.cpp index 37fb62ef9930e4..302b93734459b2 100644 --- a/clang/test/AST/ast-dump-lambda.cpp +++ b/clang/test/AST/ast-dump-lambda.cpp @@ -48,7 +48,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | |-MoveAssignment exists simple trivial needs_implicit -// CHECK-NEXT: | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | |-CXXRecordDecl {{.*}} col:10{{( imported)?}} implicit struct V // CHECK-NEXT: | `-CXXMethodDecl {{.*}} line:17:10{{( imported)?}} f 'void ()' // CHECK-NEXT: | `-CompoundStmt {{.*}} @@ -60,7 +60,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | | |-MoveAssignment -// CHECK-NEXT: | | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | | |-CXXMethodDecl {{.*}} col:7{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | | `-FieldDecl {{.*}} col:8{{( imported)?}} implicit 'V *' @@ -75,7 +75,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:7{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-FieldDecl {{.*}} col:8{{( imported)?}} implicit 'V' @@ -94,7 +94,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-CXXConversionDecl {{.*}} col:3{{( imported)?}} implicit constexpr operator auto (*)() 'auto (*() const noexcept)()' inline @@ -108,7 +108,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto (int, ...) const' inline // CHECK-NEXT: | | | |-ParmVarDecl {{.*}} col:10{{( imported)?}} a 'int' // CHECK-NEXT: | | | `-CompoundStmt {{.*}} @@ -124,7 +124,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-FieldDecl {{.*}} col:4{{( imported)?}} implicit 'Ts...' @@ -139,7 +139,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | `-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | `-CompoundStmt {{.*}} @@ -151,7 +151,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | `-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-ReturnStmt {{.*}} @@ -167,7 +167,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | `-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | `-CompoundStmt {{.*}} @@ -179,7 +179,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | `-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-ReturnStmt {{.*}} @@ -195,7 +195,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | | `-ReturnStmt {{.*}} @@ -224,7 +224,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> auto' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-FieldDecl {{.*}} col:4{{( imported)?}} implicit 'Ts...' @@ -241,7 +241,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} constexpr operator() 'auto () const' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-CXXConversionDecl {{.*}} col:3{{( imported)?}} implicit constexpr operator auto (*)() 'auto (*() const noexcept)()' inline @@ -255,7 +255,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto ()' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-CXXConversionDecl {{.*}} col:3{{( imported)?}} implicit constexpr operator auto (*)() 'auto (*() const noexcept)()' inline @@ -269,7 +269,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | | |-MoveAssignment -// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const noexcept' inline // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | |-CXXConversionDecl {{.*}} col:3{{( imported)?}} implicit constexpr operator auto (*)() noexcept 'auto (*() const noexcept)() noexcept' inline @@ -283,7 +283,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | |-MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: | | |-MoveAssignment -// CHECK-NEXT: | | `-Destructor simple irrelevant trivial needs_implicit +// CHECK-NEXT: | | `-Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: | |-CXXMethodDecl {{.*}} col:3{{( imported)?}} operator() 'auto () const -> int' inline // CHECK-NEXT: | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-ReturnStmt {{.*}} diff --git a/clang/test/AST/ast-dump-records.cpp b/clang/test/AST/ast-dump-records.cpp index cb7ac832043126..cdaa2ef16eba8e 100644 --- a/clang/test/AST/ast-dump-records.cpp +++ b/clang/test/AST/ast-dump-records.cpp @@ -22,7 +22,7 @@ struct A { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:8 implicit struct A int a; @@ -57,7 +57,7 @@ struct C { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:8 implicit struct C struct { @@ -68,7 +68,7 @@ struct C { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int a; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 a 'int' } b; @@ -82,7 +82,7 @@ struct C { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int c; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 c 'int' float d; @@ -104,7 +104,7 @@ struct C { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int e, f; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 e 'int' // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:12 f 'int' @@ -126,7 +126,7 @@ struct D { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:8 implicit struct D int a; @@ -151,7 +151,7 @@ union E { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:7 implicit union E int a; @@ -186,7 +186,7 @@ union G { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit // CHECK-NEXT: CXXRecordDecl 0x{{[^ ]*}} col:7 implicit union G struct { @@ -197,7 +197,7 @@ union G { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int a; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 a 'int' @@ -214,7 +214,7 @@ union G { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int c; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 c 'int' @@ -237,7 +237,7 @@ union G { // CHECK-NEXT: MoveConstructor exists simple trivial needs_implicit // CHECK-NEXT: CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param // CHECK-NEXT: MoveAssignment exists simple trivial needs_implicit - // CHECK-NEXT: Destructor simple irrelevant trivial needs_implicit + // CHECK-NEXT: Destructor simple irrelevant trivial{{( maybe_constexpr)?}} needs_implicit int e, f; // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:9 e 'int' diff --git a/clang/unittests/AST/ASTDumpTest.cpp b/clang/unittests/AST/ASTDumpTest.cpp new file mode 100644 index 00000000000000..45884dfd11d053 --- /dev/null +++ b/clang/unittests/AST/ASTDumpTest.cpp @@ -0,0 +1,140 @@ +//===- unittests/AST/ASTDumpTest.cpp --- Declaration tests ----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Tests Decl::dump(). +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "gtest/gtest.h" + +using namespace clang; + +namespace clang { +namespace ast { + +namespace { +/// An ExternalASTSource that asserts if it is queried for information about +/// any declaration. +class TrappingExternalASTSource : public ExternalASTSource { + ~TrappingExternalASTSource() override = default; + bool FindExternalVisibleDeclsByName(const DeclContext *, + DeclarationName) override { + assert(false && "Unexpected call to FindExternalVisibleDeclsByName"); + return true; + } + + void FindExternalLexicalDecls(const DeclContext *, + llvm::function_ref, + SmallVectorImpl &) override { + assert(false && "Unexpected call to FindExternalLexicalDecls"); + } + + void completeVisibleDeclsMap(const DeclContext *) override { + assert(false && "Unexpected call to completeVisibleDeclsMap"); + } + + void CompleteRedeclChain(const Decl *) override { + assert(false && "Unexpected call to CompleteRedeclChain"); + } + + void CompleteType(TagDecl *) override { + assert(false && "Unexpected call to CompleteType(Tag Decl*)"); + } + + void CompleteType(ObjCInterfaceDecl *) override { + assert(false && "Unexpected call to CompleteType(ObjCInterfaceDecl *)"); + } +}; + +/// Tests that Decl::dump doesn't load additional declarations from the +/// ExternalASTSource. +class ExternalASTSourceDumpTest : public ::testing::Test { +protected: + ExternalASTSourceDumpTest() + : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()), + Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()), + SourceMgr(Diags, FileMgr), Idents(LangOpts, nullptr), + Ctxt(LangOpts, SourceMgr, Idents, Sels, Builtins) { + Ctxt.setExternalSource(new TrappingExternalASTSource()); + } + + FileSystemOptions FileMgrOpts; + FileManager FileMgr; + IntrusiveRefCntPtr DiagID; + DiagnosticsEngine Diags; + SourceManager SourceMgr; + LangOptions LangOpts; + IdentifierTable Idents; + SelectorTable Sels; + Builtin::Context Builtins; + ASTContext Ctxt; +}; +} // unnamed namespace + +/// Set all flags that activate queries to the ExternalASTSource. +static void setExternalStorageFlags(DeclContext *DC) { + DC->setHasExternalLexicalStorage(); + DC->setHasExternalVisibleStorage(); + DC->setMustBuildLookupTable(); +} + +/// Dumps the given Decl. +static void dumpDecl(Decl *D) { + // Try dumping the decl which shouldn't trigger any calls to the + // ExternalASTSource. + + std::string Out; + llvm::raw_string_ostream OS(Out); + D->dump(OS); +} + +TEST_F(ExternalASTSourceDumpTest, DumpObjCInterfaceDecl) { + // Define an Objective-C interface. + ObjCInterfaceDecl *I = ObjCInterfaceDecl::Create( + Ctxt, Ctxt.getTranslationUnitDecl(), SourceLocation(), + &Ctxt.Idents.get("c"), nullptr, nullptr); + Ctxt.getTranslationUnitDecl()->addDecl(I); + + setExternalStorageFlags(I); + dumpDecl(I); +} + +TEST_F(ExternalASTSourceDumpTest, DumpRecordDecl) { + // Define a struct. + RecordDecl *R = RecordDecl::Create( + Ctxt, TagDecl::TagKind::TTK_Class, Ctxt.getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), &Ctxt.Idents.get("c")); + R->startDefinition(); + R->completeDefinition(); + Ctxt.getTranslationUnitDecl()->addDecl(R); + + setExternalStorageFlags(R); + dumpDecl(R); +} + +TEST_F(ExternalASTSourceDumpTest, DumpCXXRecordDecl) { + // Define a class. + CXXRecordDecl *R = CXXRecordDecl::Create( + Ctxt, TagDecl::TagKind::TTK_Class, Ctxt.getTranslationUnitDecl(), + SourceLocation(), SourceLocation(), &Ctxt.Idents.get("c")); + R->startDefinition(); + R->completeDefinition(); + Ctxt.getTranslationUnitDecl()->addDecl(R); + + setExternalStorageFlags(R); + dumpDecl(R); +} + +} // end namespace ast +} // end namespace clang diff --git a/clang/unittests/AST/CMakeLists.txt b/clang/unittests/AST/CMakeLists.txt index 2d5d0172afedc2..9e0a33fd762fd4 100644 --- a/clang/unittests/AST/CMakeLists.txt +++ b/clang/unittests/AST/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS add_clang_unittest(ASTTests ASTContextParentMapTest.cpp + ASTDumpTest.cpp ASTImporterFixtures.cpp ASTImporterTest.cpp ASTImporterGenericRedeclTest.cpp From 9764eb9212c598f165e9d7dfeb273b74f7777a41 Mon Sep 17 00:00:00 2001 From: LLVM GN Syncbot Date: Mon, 7 Sep 2020 10:32:22 +0000 Subject: [PATCH 33/36] [gn build] Port 0478720157f --- llvm/utils/gn/secondary/clang/unittests/AST/BUILD.gn | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/utils/gn/secondary/clang/unittests/AST/BUILD.gn b/llvm/utils/gn/secondary/clang/unittests/AST/BUILD.gn index f25ead00165c06..fd24f89aa187f9 100644 --- a/llvm/utils/gn/secondary/clang/unittests/AST/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/unittests/AST/BUILD.gn @@ -15,6 +15,7 @@ unittest("ASTTests") { ] sources = [ "ASTContextParentMapTest.cpp", + "ASTDumpTest.cpp", "ASTImporterFixtures.cpp", "ASTImporterGenericRedeclTest.cpp", "ASTImporterODRStrategiesTest.cpp", From 928c4b4b4988b4d633a96afa4c7f4584bc0009e5 Mon Sep 17 00:00:00 2001 From: Sam Parker Date: Mon, 7 Sep 2020 11:54:05 +0100 Subject: [PATCH 34/36] [SCEV] Refactor isHighCostExpansionHelper To enable the cost of constants, the helper function has been reorganised: - A struct has been introduced to hold SCEV operand information so that we know the user of the operand, as well as the operand index. The Worklist now uses instead instead of a bare SCEV. - The costing of each SCEV, and collection of its operands, is now performed in a helper function. Differential Revision: https://reviews.llvm.org/D86050 --- .../Utils/ScalarEvolutionExpander.h | 33 +- .../Utils/ScalarEvolutionExpander.cpp | 289 +++++++++--------- 2 files changed, 167 insertions(+), 155 deletions(-) diff --git a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h b/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h index 78ae38288c0c3b..77360cb2671d87 100644 --- a/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h +++ b/llvm/include/llvm/Transforms/Utils/ScalarEvolutionExpander.h @@ -39,6 +39,19 @@ bool isSafeToExpand(const SCEV *S, ScalarEvolution &SE); bool isSafeToExpandAt(const SCEV *S, const Instruction *InsertionPoint, ScalarEvolution &SE); +/// struct for holding enough information to help calculate the cost of the +/// given SCEV when expanded into IR. +struct SCEVOperand { + explicit SCEVOperand(unsigned Opc, int Idx, const SCEV *S) : + ParentOpcode(Opc), OperandIdx(Idx), S(S) { } + /// LLVM instruction opcode that uses the operand. + unsigned ParentOpcode; + /// The use index of an expanded instruction. + int OperandIdx; + /// The SCEV operand to be costed. + const SCEV* S; +}; + /// This class uses information about analyze scalars to rewrite expressions /// in canonical form. /// @@ -220,14 +233,14 @@ class SCEVExpander : public SCEVVisitor { assert(At && "This function requires At instruction to be provided."); if (!TTI) // In assert-less builds, avoid crashing return true; // by always claiming to be high-cost. - SmallVector Worklist; + SmallVector Worklist; SmallPtrSet Processed; int BudgetRemaining = Budget * TargetTransformInfo::TCC_Basic; - Worklist.emplace_back(Expr); + Worklist.emplace_back(-1, -1, Expr); while (!Worklist.empty()) { - const SCEV *S = Worklist.pop_back_val(); - if (isHighCostExpansionHelper(S, L, *At, BudgetRemaining, *TTI, Processed, - Worklist)) + const SCEVOperand WorkItem = Worklist.pop_back_val(); + if (isHighCostExpansionHelper(WorkItem, L, *At, BudgetRemaining, + *TTI, Processed, Worklist)) return true; } assert(BudgetRemaining >= 0 && "Should have returned from inner loop."); @@ -394,11 +407,11 @@ class SCEVExpander : public SCEVVisitor { Value *expandCodeForImpl(const SCEV *SH, Type *Ty, Instruction *I, bool Root); /// Recursive helper function for isHighCostExpansion. - bool isHighCostExpansionHelper(const SCEV *S, Loop *L, const Instruction &At, - int &BudgetRemaining, - const TargetTransformInfo &TTI, - SmallPtrSetImpl &Processed, - SmallVectorImpl &Worklist); + bool isHighCostExpansionHelper( + const SCEVOperand &WorkItem, Loop *L, const Instruction &At, + int &BudgetRemaining, const TargetTransformInfo &TTI, + SmallPtrSetImpl &Processed, + SmallVectorImpl &Worklist); /// Insert the specified binary operator, doing a small amount of work to /// avoid inserting an obviously redundant operation, and hoisting to an diff --git a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp index 1e8b11d6ac5fe3..1bb827cd3057b4 100644 --- a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp +++ b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp @@ -2177,13 +2177,133 @@ SCEVExpander::getRelatedExistingExpansion(const SCEV *S, const Instruction *At, return None; } +template static int costAndCollectOperands( + const SCEVOperand &WorkItem, const TargetTransformInfo &TTI, + TargetTransformInfo::TargetCostKind CostKind, + SmallVectorImpl &Worklist) { + + const T *S = cast(WorkItem.S); + int Cost = 0; + // Collect the opcodes of all the instructions that will be needed to expand + // the SCEVExpr. This is so that when we come to cost the operands, we know + // what the generated user(s) will be. + SmallVector Opcodes; + + auto CastCost = [&](unsigned Opcode) { + Opcodes.push_back(Opcode); + return TTI.getCastInstrCost(Opcode, S->getType(), + S->getOperand(0)->getType(), + TTI::CastContextHint::None, CostKind); + }; + + auto ArithCost = [&](unsigned Opcode, unsigned NumRequired) { + Opcodes.push_back(Opcode); + return NumRequired * + TTI.getArithmeticInstrCost(Opcode, S->getType(), CostKind); + }; + + auto CmpSelCost = [&](unsigned Opcode, unsigned NumRequired) { + Opcodes.push_back(Opcode); + Type *OpType = S->getOperand(0)->getType(); + return NumRequired * + TTI.getCmpSelInstrCost(Opcode, OpType, + CmpInst::makeCmpResultType(OpType), CostKind); + }; + + switch (S->getSCEVType()) { + default: + llvm_unreachable("No other scev expressions possible."); + case scUnknown: + case scConstant: + return 0; + case scTruncate: + Cost = CastCost(Instruction::Trunc); + break; + case scZeroExtend: + Cost = CastCost(Instruction::ZExt); + break; + case scSignExtend: + Cost = CastCost(Instruction::SExt); + break; + case scUDivExpr: { + unsigned Opcode = Instruction::UDiv; + if (auto *SC = dyn_cast(S->getOperand(1))) + if (SC->getAPInt().isPowerOf2()) + Opcode = Instruction::LShr; + Cost = ArithCost(Opcode, 1); + break; + } + case scAddExpr: + Cost = ArithCost(Instruction::Add, S->getNumOperands() - 1); + break; + case scMulExpr: + // TODO: this is a very pessimistic cost modelling for Mul, + // because of Bin Pow algorithm actually used by the expander, + // see SCEVExpander::visitMulExpr(), ExpandOpBinPowN(). + Cost = ArithCost(Instruction::Mul, S->getNumOperands() - 1); + break; + case scSMaxExpr: + case scUMaxExpr: + case scSMinExpr: + case scUMinExpr: { + Cost += CmpSelCost(Instruction::ICmp, S->getNumOperands() - 1); + Cost += CmpSelCost(Instruction::Select, S->getNumOperands() - 1); + break; + } + case scAddRecExpr: { + // In this polynominal, we may have some zero operands, and we shouldn't + // really charge for those. So how many non-zero coeffients are there? + int NumTerms = llvm::count_if(S->operands(), [](const SCEV *Op) { + return !Op->isZero(); + }); + + assert(NumTerms >= 1 && "Polynominal should have at least one term."); + assert(!(*std::prev(S->operands().end()))->isZero() && + "Last operand should not be zero"); + + // Ignoring constant term (operand 0), how many of the coeffients are u> 1? + int NumNonZeroDegreeNonOneTerms = + llvm::count_if(S->operands(), [](const SCEV *Op) { + auto *SConst = dyn_cast(Op); + return !SConst || SConst->getAPInt().ugt(1); + }); + + // Much like with normal add expr, the polynominal will require + // one less addition than the number of it's terms. + int AddCost = ArithCost(Instruction::Add, NumTerms - 1); + // Here, *each* one of those will require a multiplication. + int MulCost = ArithCost(Instruction::Mul, NumNonZeroDegreeNonOneTerms); + Cost = AddCost + MulCost; + + // What is the degree of this polynominal? + int PolyDegree = S->getNumOperands() - 1; + assert(PolyDegree >= 1 && "Should be at least affine."); + + // The final term will be: + // Op_{PolyDegree} * x ^ {PolyDegree} + // Where x ^ {PolyDegree} will again require PolyDegree-1 mul operations. + // Note that x ^ {PolyDegree} = x * x ^ {PolyDegree-1} so charging for + // x ^ {PolyDegree} will give us x ^ {2} .. x ^ {PolyDegree-1} for free. + // FIXME: this is conservatively correct, but might be overly pessimistic. + Cost += MulCost * (PolyDegree - 1); + } + } + + for (unsigned Opc : Opcodes) + for (auto I : enumerate(S->operands())) + Worklist.emplace_back(Opc, I.index(), I.value()); + return Cost; +} + bool SCEVExpander::isHighCostExpansionHelper( - const SCEV *S, Loop *L, const Instruction &At, int &BudgetRemaining, - const TargetTransformInfo &TTI, SmallPtrSetImpl &Processed, - SmallVectorImpl &Worklist) { + const SCEVOperand &WorkItem, Loop *L, const Instruction &At, + int &BudgetRemaining, const TargetTransformInfo &TTI, + SmallPtrSetImpl &Processed, + SmallVectorImpl &Worklist) { if (BudgetRemaining < 0) return true; // Already run out of budget, give up. + const SCEV *S = WorkItem.S; // Was the cost of expansion of this expression already accounted for? if (!Processed.insert(S).second) return false; // We have already accounted for this expression. @@ -2202,44 +2322,12 @@ bool SCEVExpander::isHighCostExpansionHelper( TargetTransformInfo::TargetCostKind CostKind = TargetTransformInfo::TCK_RecipThroughput; - if (auto *CastExpr = dyn_cast(S)) { - unsigned Opcode; - switch (S->getSCEVType()) { - case scTruncate: - Opcode = Instruction::Trunc; - break; - case scZeroExtend: - Opcode = Instruction::ZExt; - break; - case scSignExtend: - Opcode = Instruction::SExt; - break; - default: - llvm_unreachable("There are no other cast types."); - } - const SCEV *Op = CastExpr->getOperand(); - BudgetRemaining -= TTI.getCastInstrCost( - Opcode, /*Dst=*/S->getType(), - /*Src=*/Op->getType(), TTI::CastContextHint::None, CostKind); - Worklist.emplace_back(Op); + if (isa(S)) { + int Cost = + costAndCollectOperands(WorkItem, TTI, CostKind, Worklist); + BudgetRemaining -= Cost; return false; // Will answer upon next entry into this function. - } - - if (auto *UDivExpr = dyn_cast(S)) { - // If the divisor is a power of two count this as a logical right-shift. - if (auto *SC = dyn_cast(UDivExpr->getRHS())) { - if (SC->getAPInt().isPowerOf2()) { - BudgetRemaining -= - TTI.getArithmeticInstrCost(Instruction::LShr, S->getType(), - CostKind); - // Note that we don't count the cost of RHS, because it is a constant, - // and we consider those to be free. But if that changes, we would need - // to log2() it first before calling isHighCostExpansionHelper(). - Worklist.emplace_back(UDivExpr->getLHS()); - return false; // Will answer upon next entry into this function. - } - } - + } else if (isa(S)) { // UDivExpr is very likely a UDiv that ScalarEvolution's HowFarToZero or // HowManyLessThans produced to compute a precise expression, rather than a // UDiv from the user's code. If we can't find a UDiv in the code with some @@ -2252,117 +2340,28 @@ bool SCEVExpander::isHighCostExpansionHelper( SE.getAddExpr(S, SE.getConstant(S->getType(), 1)), &At, L)) return false; // Consider it to be free. + int Cost = + costAndCollectOperands(WorkItem, TTI, CostKind, Worklist); // Need to count the cost of this UDiv. - BudgetRemaining -= - TTI.getArithmeticInstrCost(Instruction::UDiv, S->getType(), - CostKind); - Worklist.insert(Worklist.end(), {UDivExpr->getLHS(), UDivExpr->getRHS()}); + BudgetRemaining -= Cost; return false; // Will answer upon next entry into this function. - } - - if (const auto *NAry = dyn_cast(S)) { - Type *OpType = NAry->getType(); - - assert(NAry->getNumOperands() >= 2 && - "Polynomial should be at least linear"); - - int AddCost = - TTI.getArithmeticInstrCost(Instruction::Add, OpType, CostKind); - int MulCost = - TTI.getArithmeticInstrCost(Instruction::Mul, OpType, CostKind); - - // In this polynominal, we may have some zero operands, and we shouldn't - // really charge for those. So how many non-zero coeffients are there? - int NumTerms = llvm::count_if(NAry->operands(), - [](const SCEV *S) { return !S->isZero(); }); - assert(NumTerms >= 1 && "Polynominal should have at least one term."); - assert(!(*std::prev(NAry->operands().end()))->isZero() && - "Last operand should not be zero"); - - // Much like with normal add expr, the polynominal will require - // one less addition than the number of it's terms. - BudgetRemaining -= AddCost * (NumTerms - 1); - if (BudgetRemaining < 0) - return true; - - // Ignoring constant term (operand 0), how many of the coeffients are u> 1? - int NumNonZeroDegreeNonOneTerms = - llvm::count_if(make_range(std::next(NAry->op_begin()), NAry->op_end()), - [](const SCEV *S) { - auto *SConst = dyn_cast(S); - return !SConst || SConst->getAPInt().ugt(1); - }); - // Here, *each* one of those will require a multiplication. - BudgetRemaining -= MulCost * NumNonZeroDegreeNonOneTerms; - if (BudgetRemaining < 0) - return true; - - // What is the degree of this polynominal? - int PolyDegree = NAry->getNumOperands() - 1; - assert(PolyDegree >= 1 && "Should be at least affine."); - - // The final term will be: - // Op_{PolyDegree} * x ^ {PolyDegree} - // Where x ^ {PolyDegree} will again require PolyDegree-1 mul operations. - // Note that x ^ {PolyDegree} = x * x ^ {PolyDegree-1} so charging for - // x ^ {PolyDegree} will give us x ^ {2} .. x ^ {PolyDegree-1} for free. - // FIXME: this is conservatively correct, but might be overly pessimistic. - BudgetRemaining -= MulCost * (PolyDegree - 1); - if (BudgetRemaining < 0) - return true; - - // And finally, the operands themselves should fit within the budget. - Worklist.insert(Worklist.end(), NAry->operands().begin(), - NAry->operands().end()); - return false; // So far so good, though ops may be too costly? - } - - if (const SCEVNAryExpr *NAry = dyn_cast(S)) { - Type *OpType = NAry->getType(); - - int PairCost; - switch (S->getSCEVType()) { - case scAddExpr: - PairCost = - TTI.getArithmeticInstrCost(Instruction::Add, OpType, CostKind); - break; - case scMulExpr: - // TODO: this is a very pessimistic cost modelling for Mul, - // because of Bin Pow algorithm actually used by the expander, - // see SCEVExpander::visitMulExpr(), ExpandOpBinPowN(). - PairCost = - TTI.getArithmeticInstrCost(Instruction::Mul, OpType, CostKind); - break; - case scSMaxExpr: - case scUMaxExpr: - case scSMinExpr: - case scUMinExpr: - PairCost = TTI.getCmpSelInstrCost(Instruction::ICmp, OpType, - CmpInst::makeCmpResultType(OpType), - CostKind) + - TTI.getCmpSelInstrCost(Instruction::Select, OpType, - CmpInst::makeCmpResultType(OpType), - CostKind); - break; - default: - llvm_unreachable("There are no other variants here."); - } - + } else if (const SCEVNAryExpr *NAry = dyn_cast(S)) { assert(NAry->getNumOperands() > 1 && "Nary expr should have more than 1 operand."); // The simple nary expr will require one less op (or pair of ops) // than the number of it's terms. - BudgetRemaining -= PairCost * (NAry->getNumOperands() - 1); - if (BudgetRemaining < 0) - return true; - - // And finally, the operands themselves should fit within the budget. - Worklist.insert(Worklist.end(), NAry->operands().begin(), - NAry->operands().end()); - return false; // So far so good, though ops may be too costly? - } - - llvm_unreachable("No other scev expressions possible."); + int Cost = + costAndCollectOperands(WorkItem, TTI, CostKind, Worklist); + BudgetRemaining -= Cost; + return BudgetRemaining < 0; + } else if (const auto *NAry = dyn_cast(S)) { + assert(NAry->getNumOperands() >= 2 && + "Polynomial should be at least linear"); + BudgetRemaining -= costAndCollectOperands( + WorkItem, TTI, CostKind, Worklist); + return BudgetRemaining < 0; + } else + llvm_unreachable("No other scev expressions possible."); } Value *SCEVExpander::expandCodeForPredicate(const SCEVPredicate *Pred, From 136eb79a8846c4e8ff6ba5ccfc0c470ab351fb13 Mon Sep 17 00:00:00 2001 From: Frederik Gossen Date: Mon, 7 Sep 2020 11:41:27 +0000 Subject: [PATCH 35/36] [MLIR][Standard] Add `dynamic_tensor_from_elements` operation With `dynamic_tensor_from_elements` tensor values of dynamic size can be created. The body of the operation essentially maps the index space to tensor elements. Declare SCF operations in the `scf` namespace to avoid name clash with the new `std.yield` operation. Resolve ambiguities between `linalg/shape/std/scf.yield` operations. Differential Revision: https://reviews.llvm.org/D86276 --- mlir/include/mlir/Dialect/SCF/SCFOps.td | 8 +-- .../mlir/Dialect/StandardOps/IR/Ops.td | 49 ++++++++++++++ .../Conversion/LinalgToLLVM/LinalgToLLVM.cpp | 3 +- .../SCFToStandard/SCFToStandard.cpp | 4 +- mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp | 7 +- mlir/lib/Dialect/Linalg/Transforms/Fusion.cpp | 2 +- mlir/lib/Dialect/Linalg/Transforms/Loops.cpp | 2 +- .../Linalg/Transforms/Vectorization.cpp | 16 ++--- mlir/lib/Dialect/SCF/SCF.cpp | 8 ++- mlir/lib/Dialect/Shape/IR/Shape.cpp | 2 +- .../Shape/Transforms/ShapeToShapeLowering.cpp | 2 +- mlir/lib/Dialect/StandardOps/IR/Ops.cpp | 62 ++++++++++++++++- mlir/test/Dialect/Standard/invalid.mlir | 66 +++++++++++++++++++ mlir/test/Dialect/Standard/ops.mlir | 14 +++- 14 files changed, 219 insertions(+), 26 deletions(-) diff --git a/mlir/include/mlir/Dialect/SCF/SCFOps.td b/mlir/include/mlir/Dialect/SCF/SCFOps.td index 78aefec00bf764..59ba50fbe2322b 100644 --- a/mlir/include/mlir/Dialect/SCF/SCFOps.td +++ b/mlir/include/mlir/Dialect/SCF/SCFOps.td @@ -19,7 +19,7 @@ include "mlir/Interfaces/SideEffectInterfaces.td" def SCF_Dialect : Dialect { let name = "scf"; - let cppNamespace = ""; + let cppNamespace = "scf"; } // Base class for SCF dialect ops. @@ -39,7 +39,7 @@ class SCF_Op traits = []> : def ForOp : SCF_Op<"for", [DeclareOpInterfaceMethods, DeclareOpInterfaceMethods, - SingleBlockImplicitTerminator<"YieldOp">, + SingleBlockImplicitTerminator<"scf::YieldOp">, RecursiveSideEffects]> { let summary = "for operation"; let description = [{ @@ -183,7 +183,7 @@ def ForOp : SCF_Op<"for", def IfOp : SCF_Op<"if", [DeclareOpInterfaceMethods, - SingleBlockImplicitTerminator<"YieldOp">, RecursiveSideEffects, + SingleBlockImplicitTerminator<"scf::YieldOp">, RecursiveSideEffects, NoRegionArguments]> { let summary = "if-then-else operation"; let description = [{ @@ -271,7 +271,7 @@ def ParallelOp : SCF_Op<"parallel", [AttrSizedOperandSegments, DeclareOpInterfaceMethods, RecursiveSideEffects, - SingleBlockImplicitTerminator<"YieldOp">]> { + SingleBlockImplicitTerminator<"scf::YieldOp">]> { let summary = "parallel for operation"; let description = [{ The "scf.parallel" operation represents a loop nest taking 4 groups of SSA diff --git a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td index ae951e824e001e..f326ae55786500 100644 --- a/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td +++ b/mlir/include/mlir/Dialect/StandardOps/IR/Ops.td @@ -1475,6 +1475,37 @@ def DivFOp : FloatArithmeticOp<"divf"> { let summary = "floating point division operation"; } +//===----------------------------------------------------------------------===// +// DynamicTensorFromElementsOp +//===----------------------------------------------------------------------===// + +def DynamicTensorFromElementsOp : Std_Op<"dynamic_tensor_from_elements", + [RecursiveSideEffects, SingleBlockImplicitTerminator<"YieldOp">]> { + string summary = "Creates a dynamically sized tensor from elements"; + string description = [{ + This operation creates a dynamically sized tensor with elements of any type. + It expects one index operand per dynamic extent of the result tensor. + + The body region defines the tensor's elements. It takes index operands as + its region arguments that span the index space. The element at the given + position is yielded with the `yield` operation (see `YieldOp`). + + Example: + + ```mlir + %tnsr = dynamic_tensor_from_elements %m, %n { + ^bb0(%i : index, %j : index, %k : index): + ... + yield %elem : f32 + } : tensor + ``` + }]; + + let arguments = (ins Variadic:$dynamicExtents); + let results = (outs AnyRankedTensor:$result); + let regions = (region SizedRegion<1>:$body); +} + //===----------------------------------------------------------------------===// // ExpOp //===----------------------------------------------------------------------===// @@ -3252,6 +3283,24 @@ def ViewOp : Std_Op<"view", [ let hasCanonicalizer = 1; } +//===----------------------------------------------------------------------===// +// YieldOp +//===----------------------------------------------------------------------===// + +def YieldOp : Std_Op<"yield", [NoSideEffect, ReturnLike, Terminator, + HasParent<"DynamicTensorFromElementsOp">]> { + let summary = "Yield a value from a region"; + let description = [{ + This operation is used to yield a single value from a within a region. It + is used to create dynamically sized tensors + (see `DynamicTensorFromElementsOp`). + }]; + + let arguments = (ins AnyType:$value); + let assemblyFormat = "$value attr-dict `:` type($value)"; + let verifier = ?; +} + //===----------------------------------------------------------------------===// // XOrOp //===----------------------------------------------------------------------===// diff --git a/mlir/lib/Conversion/LinalgToLLVM/LinalgToLLVM.cpp b/mlir/lib/Conversion/LinalgToLLVM/LinalgToLLVM.cpp index 0460d98b44a470..f38eabb9465d50 100644 --- a/mlir/lib/Conversion/LinalgToLLVM/LinalgToLLVM.cpp +++ b/mlir/lib/Conversion/LinalgToLLVM/LinalgToLLVM.cpp @@ -339,7 +339,8 @@ class TransposeOpConversion : public ConvertToLLVMPattern { class YieldOpConversion : public ConvertToLLVMPattern { public: explicit YieldOpConversion(MLIRContext *context, LLVMTypeConverter &lowering_) - : ConvertToLLVMPattern(YieldOp::getOperationName(), context, lowering_) {} + : ConvertToLLVMPattern(linalg::YieldOp::getOperationName(), context, + lowering_) {} LogicalResult matchAndRewrite(Operation *op, ArrayRef operands, diff --git a/mlir/lib/Conversion/SCFToStandard/SCFToStandard.cpp b/mlir/lib/Conversion/SCFToStandard/SCFToStandard.cpp index 34ee48758e9e6e..14f365f95ee5a5 100644 --- a/mlir/lib/Conversion/SCFToStandard/SCFToStandard.cpp +++ b/mlir/lib/Conversion/SCFToStandard/SCFToStandard.cpp @@ -356,7 +356,7 @@ ParallelLowering::matchAndRewrite(ParallelOp parallelOp, // A loop is constructed with an empty "yield" terminator if there are // no results. rewriter.setInsertionPointToEnd(rewriter.getInsertionBlock()); - rewriter.create(loc, forOp.getResults()); + rewriter.create(loc, forOp.getResults()); } rewriter.setInsertionPointToStart(forOp.getBody()); @@ -391,7 +391,7 @@ ParallelLowering::matchAndRewrite(ParallelOp parallelOp, if (!yieldOperands.empty()) { rewriter.setInsertionPointToEnd(rewriter.getInsertionBlock()); - rewriter.create(loc, yieldOperands); + rewriter.create(loc, yieldOperands); } rewriter.replaceOp(parallelOp, loopResults); diff --git a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp index fa45997ae801a2..c9b05f89f30b13 100644 --- a/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp +++ b/mlir/lib/Dialect/Linalg/IR/LinalgOps.cpp @@ -905,7 +905,7 @@ static ParseResult parseTransposeOp(OpAsmParser &parser, // YieldOp //===----------------------------------------------------------------------===// -static void print(OpAsmPrinter &p, YieldOp op) { +static void print(OpAsmPrinter &p, linalg::YieldOp op) { p << op.getOperationName(); if (op.getNumOperands() > 0) p << ' ' << op.getOperands(); @@ -926,7 +926,8 @@ static ParseResult parseYieldOp(OpAsmParser &parser, OperationState &result) { // Check the operand number and types must match the element types of the // LinalgOp interface's shaped operands. -static LogicalResult verifyYield(YieldOp op, LinalgOp linalgOpInterface) { +static LogicalResult verifyYield(linalg::YieldOp op, + LinalgOp linalgOpInterface) { auto nOutputs = linalgOpInterface.getNumOutputs(); if (op.getNumOperands() != nOutputs) return op.emitOpError("expected number of yield values (") @@ -946,7 +947,7 @@ static LogicalResult verifyYield(YieldOp op, LinalgOp linalgOpInterface) { return success(); } -static LogicalResult verify(YieldOp op) { +static LogicalResult verify(linalg::YieldOp op) { auto *parentOp = op.getParentOp(); if (parentOp->getNumRegions() != 1 || parentOp->getRegion(0).empty()) return op.emitOpError("expected single non-empty parent region"); diff --git a/mlir/lib/Dialect/Linalg/Transforms/Fusion.cpp b/mlir/lib/Dialect/Linalg/Transforms/Fusion.cpp index 6c0c841451dae6..adbf4a7b80454c 100644 --- a/mlir/lib/Dialect/Linalg/Transforms/Fusion.cpp +++ b/mlir/lib/Dialect/Linalg/Transforms/Fusion.cpp @@ -659,7 +659,7 @@ struct FuseGenericOpsOnTensors { // Add operations from producer (except the yield operation) to the fused // op. for (auto &op : producerBlock.getOperations()) { - if (auto yieldOp = dyn_cast(op)) { + if (auto yieldOp = dyn_cast(op)) { // Lookup the value the yield operation is mapped to. Value yieldVal = yieldOp.getOperand(0); if (Value clonedVal = mapper.lookupOrNull(yieldVal)) diff --git a/mlir/lib/Dialect/Linalg/Transforms/Loops.cpp b/mlir/lib/Dialect/Linalg/Transforms/Loops.cpp index 281edd9a91f642..d4d1d108be71a2 100644 --- a/mlir/lib/Dialect/Linalg/Transforms/Loops.cpp +++ b/mlir/lib/Dialect/Linalg/Transforms/Loops.cpp @@ -147,7 +147,7 @@ static void inlineRegionAndEmitStore(OpType op, ArrayRef indexedValues, } Operation &terminator = block.back(); - assert(isa(terminator) && + assert(isa(terminator) && "expected a yield op in the end of the region"); for (unsigned i = 0, e = terminator.getNumOperands(); i < e; ++i) { IndexedValueType O(outputBuffers[i]); diff --git a/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp b/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp index c8e20ce57842ba..ada89f1c82b5c8 100644 --- a/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp +++ b/mlir/lib/Dialect/Linalg/Transforms/Vectorization.cpp @@ -48,14 +48,14 @@ static bool hasMultiplyAddBody(Region &r) { auto c = m_Val(r.getArgument(2)); // TODO: Update this detection once we have matcher support for specifying // that any permutation of operands matches. - auto pattern1 = m_Op(m_Op(m_Op(a, b), c)); - auto pattern2 = m_Op(m_Op(c, m_Op(a, b))); - auto pattern3 = m_Op(m_Op(m_Op(b, a), c)); - auto pattern4 = m_Op(m_Op(c, m_Op(b, a))); - auto pattern5 = m_Op(m_Op(m_Op(a, b), c)); - auto pattern6 = m_Op(m_Op(c, m_Op(a, b))); - auto pattern7 = m_Op(m_Op(m_Op(b, a), c)); - auto pattern8 = m_Op(m_Op(c, m_Op(b, a))); + auto pattern1 = m_Op(m_Op(m_Op(a, b), c)); + auto pattern2 = m_Op(m_Op(c, m_Op(a, b))); + auto pattern3 = m_Op(m_Op(m_Op(b, a), c)); + auto pattern4 = m_Op(m_Op(c, m_Op(b, a))); + auto pattern5 = m_Op(m_Op(m_Op(a, b), c)); + auto pattern6 = m_Op(m_Op(c, m_Op(a, b))); + auto pattern7 = m_Op(m_Op(m_Op(b, a), c)); + auto pattern8 = m_Op(m_Op(c, m_Op(b, a))); return pattern1.match(&r.front().back()) || pattern2.match(&r.front().back()) || pattern3.match(&r.front().back()) || diff --git a/mlir/lib/Dialect/SCF/SCF.cpp b/mlir/lib/Dialect/SCF/SCF.cpp index 6f3f1e4dc0d151..498246315d6428 100644 --- a/mlir/lib/Dialect/SCF/SCF.cpp +++ b/mlir/lib/Dialect/SCF/SCF.cpp @@ -38,7 +38,7 @@ struct SCFInlinerInterface : public DialectInlinerInterface { // as necessary. Required when the region has only one block. void handleTerminator(Operation *op, ArrayRef valuesToRepl) const final { - auto retValOp = dyn_cast(op); + auto retValOp = dyn_cast(op); if (!retValOp) return; @@ -889,7 +889,7 @@ static ParseResult parseYieldOp(OpAsmParser &parser, OperationState &result) { return success(); } -static void print(OpAsmPrinter &p, YieldOp op) { +static void print(OpAsmPrinter &p, scf::YieldOp op) { p << op.getOperationName(); if (op.getNumOperands() != 0) p << ' ' << op.getOperands() << " : " << op.getOperandTypes(); @@ -899,5 +899,9 @@ static void print(OpAsmPrinter &p, YieldOp op) { // TableGen'd op method definitions //===----------------------------------------------------------------------===// +namespace mlir { +namespace scf { #define GET_OP_CLASSES #include "mlir/Dialect/SCF/SCFOps.cpp.inc" +} // namespace scf +} // namespace mlir diff --git a/mlir/lib/Dialect/Shape/IR/Shape.cpp b/mlir/lib/Dialect/Shape/IR/Shape.cpp index 511ec9bf2b4e1c..bcfaa896f63d22 100644 --- a/mlir/lib/Dialect/Shape/IR/Shape.cpp +++ b/mlir/lib/Dialect/Shape/IR/Shape.cpp @@ -779,7 +779,7 @@ void SizeToIndexOp::getCanonicalizationPatterns( // YieldOp //===----------------------------------------------------------------------===// -static LogicalResult verify(YieldOp op) { +static LogicalResult verify(shape::YieldOp op) { auto *parentOp = op.getParentOp(); auto results = parentOp->getResults(); auto operands = op.getOperands(); diff --git a/mlir/lib/Dialect/Shape/Transforms/ShapeToShapeLowering.cpp b/mlir/lib/Dialect/Shape/Transforms/ShapeToShapeLowering.cpp index a84fad1f946020..ff74ce069e407e 100644 --- a/mlir/lib/Dialect/Shape/Transforms/ShapeToShapeLowering.cpp +++ b/mlir/lib/Dialect/Shape/Transforms/ShapeToShapeLowering.cpp @@ -45,7 +45,7 @@ NumElementsOpConverter::matchAndRewrite(NumElementsOp op, OpBuilder b = OpBuilder::atBlockEnd(body); Value product = b.create(loc, valueType, body->getArgument(1), body->getArgument(2)); - b.create(loc, product); + b.create(loc, product); rewriter.replaceOp(op, reduce.result()); return success(); diff --git a/mlir/lib/Dialect/StandardOps/IR/Ops.cpp b/mlir/lib/Dialect/StandardOps/IR/Ops.cpp index b34257791d78ed..65f8b83d9a7187 100644 --- a/mlir/lib/Dialect/StandardOps/IR/Ops.cpp +++ b/mlir/lib/Dialect/StandardOps/IR/Ops.cpp @@ -1312,7 +1312,6 @@ Optional DimOp::getConstantIndex() { } static LogicalResult verify(DimOp op) { - // Assume unknown index to be in range. Optional index = op.getConstantIndex(); if (!index.hasValue()) @@ -1634,6 +1633,67 @@ LogicalResult DmaWaitOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// DynamicTensorFromElementsOp +//===----------------------------------------------------------------------===// + +static ParseResult parseDynamicTensorFromElementsOp(OpAsmParser &parser, + OperationState &result) { + // Parse operands. + SmallVector dynamicExtents; + Type indexTy = parser.getBuilder().getIndexType(); + if (parser.parseOperandList(dynamicExtents) || + parser.resolveOperands(dynamicExtents, indexTy, result.operands)) + return failure(); + + // Parse body. + Region *body = result.addRegion(); + if (parser.parseRegion(*body, {}, {})) + return failure(); + + // Parse result type. + Type resultType; + if (parser.parseOptionalAttrDict(result.attributes) || + parser.parseColonType(resultType)) + return failure(); + result.addTypes(resultType); + + return success(); +} + +static void print(OpAsmPrinter &p, DynamicTensorFromElementsOp op) { + p << "dynamic_tensor_from_elements " << op.dynamicExtents(); + p.printRegion(op.body()); + p.printOptionalAttrDict(op.getAttrs()); + p << " : " << op.getType(); +} + +static LogicalResult verify(DynamicTensorFromElementsOp op) { + // Ensure that the tensor type has as many dynamic dimensions as are specified + // by the operands. + RankedTensorType resultTy = op.getType().cast(); + if (op.getNumOperands() != resultTy.getNumDynamicDims()) + return op.emitError("must have as many index operands as dynamic extents " + "in the result type"); + + // Ensure that region arguments span the index space. + if (!llvm::all_of(op.body().getArgumentTypes(), + [](Type ty) { return ty.isIndex(); })) + return op.emitError("all body arguments must be index"); + if (op.body().getNumArguments() != resultTy.getRank()) + return op.emitError("must have one body argument per input dimension"); + + // Ensure that the region yields an element of the right type. + auto yieldOp = + llvm::cast(op.body().getBlocks().front().getTerminator()); + if (yieldOp.value().getType() != resultTy.getElementType()) + return op.emitOpError( + "body must be terminated with a `yield` operation of the tensor " + "element type"); + + return success(); +} + //===----------------------------------------------------------------------===// // ExtractElementOp //===----------------------------------------------------------------------===// diff --git a/mlir/test/Dialect/Standard/invalid.mlir b/mlir/test/Dialect/Standard/invalid.mlir index f2b71f634cd3d9..7f9c564e74f3fe 100644 --- a/mlir/test/Dialect/Standard/invalid.mlir +++ b/mlir/test/Dialect/Standard/invalid.mlir @@ -15,3 +15,69 @@ func @test_index_cast_tensor_error(%arg0 : tensor) -> i64 { %0 = index_cast %arg0 : tensor to i64 return %0 : i64 } + +// ----- + +func @dynamic_tensor_from_elements(%m : index) + -> tensor { + // expected-error @+1 {{must have as many index operands as dynamic extents in the result type}} + %tnsr = dynamic_tensor_from_elements %m { + ^bb0(%i : index, %j : index, %k : index): + %elem = constant 8.0 : f32 + yield %elem : f32 + } : tensor + return %tnsr : tensor +} + +// ----- + +func @dynamic_tensor_from_elements(%m : index, %n : index) + -> tensor { + // expected-error @+1 {{must have one body argument per input dimension}} + %tnsr = dynamic_tensor_from_elements %m, %n { + ^bb0(%i : index, %j : index): + %elem = constant 8.0 : f32 + yield %elem : f32 + } : tensor + return %tnsr : tensor +} + +// ----- + +func @dynamic_tensor_from_elements(%m : index, %n : index) + -> tensor { + // expected-error @+1 {{all body arguments must be index}} + %tnsr = dynamic_tensor_from_elements %m, %n { + ^bb0(%i : index, %j : index, %k : i64): + %elem = constant 8.0 : f32 + yield %elem : f32 + } : tensor + return %tnsr : tensor +} + +// ----- + +func @dynamic_tensor_from_elements(%m : index, %n : index) + -> tensor { + // expected-error @+2 {{op expects regions to end with 'std.yield', found 'std.return'}} + // expected-note @+1 {{in custom textual format, the absence of terminator implies 'std.yield'}} + %tnsr = dynamic_tensor_from_elements %m, %n { + ^bb0(%i : index, %j : index, %k : index): + %elem = constant 8.0 : f32 + return %elem : f32 + } : tensor + return %tnsr : tensor +} + +// ----- + +func @dynamic_tensor_from_elements(%m : index, %n : index) + -> tensor { + // expected-error @+1 {{body must be terminated with a `yield` operation of the tensor element type}} + %tnsr = dynamic_tensor_from_elements %m, %n { + ^bb0(%i : index, %j : index, %k : index): + %elem = constant 8 : i32 + yield %elem : i32 + } : tensor + return %tnsr : tensor +} diff --git a/mlir/test/Dialect/Standard/ops.mlir b/mlir/test/Dialect/Standard/ops.mlir index 24da04eebaaa62..a765acb9657b57 100644 --- a/mlir/test/Dialect/Standard/ops.mlir +++ b/mlir/test/Dialect/Standard/ops.mlir @@ -1,4 +1,5 @@ -// RUN: mlir-opt -split-input-file %s | FileCheck %s +// RUN: mlir-opt %s | mlir-opt | FileCheck %s +// RUN: mlir-opt %s --mlir-print-op-generic | mlir-opt | FileCheck %s // CHECK-LABEL: test_index_cast func @test_index_cast(%arg0 : index) -> i64 { @@ -22,3 +23,14 @@ func @assert(%arg : i1) { assert %arg, "Some message in case this assertion fails." return } + +func @dynamic_tensor_from_elements(%m : index, %n : index) + -> tensor { + %tnsr = dynamic_tensor_from_elements %m, %n { + ^bb0(%i : index, %j : index, %k : index): + %elem = constant 8.0 : f32 + yield %elem : f32 + } : tensor + return %tnsr : tensor +} + From 3097427f93dde9a49f729e995b8d52d91cc30d4c Mon Sep 17 00:00:00 2001 From: Xing GUO Date: Mon, 7 Sep 2020 19:44:46 +0800 Subject: [PATCH 36/36] [obj2yaml] Add support for dumping the .debug_str section. This patch adds support for dumping the .debug_str section to obj2yaml. Reviewed By: jhenderson Differential Revision: https://reviews.llvm.org/D86867 --- .../tools/obj2yaml/ELF/DWARF/debug-str.yaml | 101 ++++++++++++++++++ llvm/tools/obj2yaml/elf2yaml.cpp | 33 ++++-- 2 files changed, 125 insertions(+), 9 deletions(-) create mode 100644 llvm/test/tools/obj2yaml/ELF/DWARF/debug-str.yaml diff --git a/llvm/test/tools/obj2yaml/ELF/DWARF/debug-str.yaml b/llvm/test/tools/obj2yaml/ELF/DWARF/debug-str.yaml new file mode 100644 index 00000000000000..e058642877243b --- /dev/null +++ b/llvm/test/tools/obj2yaml/ELF/DWARF/debug-str.yaml @@ -0,0 +1,101 @@ +## Test how we dump the .debug_str section. + +## a) Test dumping a .debug_str section with a default section header. + +# RUN: yaml2obj --docnum=1 %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefix=BASIC --implicit-check-not='Name: .debug_str' + +## b) Test dumping a .debug_str section whose section header properties are overridden. + +## Override the sh_type field. +# RUN: yaml2obj --docnum=1 -DTYPE=STRTAB %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefixes=BASIC,COMMON \ +# RUN: -DTYPE=STRTAB -DFLAGS="[ SHF_MERGE, SHF_STRINGS ]" -D#%x,ADDRALIGN=1 + +## Override the sh_flags field. +# RUN: yaml2obj --docnum=1 -DFLAGS=[SHF_ALLOC] %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefixes=BASIC,COMMON \ +# RUN: -DTYPE=PROGBITS -DFLAGS="[ SHF_ALLOC ]" -D#%x,ADDRALIGN=1 + +## Override the sh_link field. +# RUN: yaml2obj --docnum=1 -DLINK=.sec %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefixes=BASIC,COMMON,LINK \ +# RUN: -DTYPE=PROGBITS -DFLAGS="[ SHF_MERGE, SHF_STRINGS ]" -DLINK=.sec -D#%x,ADDRALIGN=1 + +## Override the sh_addr field. +# RUN: yaml2obj --docnum=1 -DADDRESS=0x2020 %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefixes=BASIC,COMMON,ADDRESS \ +# RUN: -DTYPE=PROGBITS -DFLAGS="[ SHF_MERGE, SHF_STRINGS ]" -D#%x,ADDRALIGN=1 -D#%x,ADDRESS=0x2020 + +## Override the sh_addralign field. +# RUN: yaml2obj --docnum=1 -DADDRALIGN=3 %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefixes=BASIC,COMMON \ +# RUN: -DTYPE=PROGBITS -DFLAGS="[ SHF_MERGE, SHF_STRINGS ]" -D#%x,ADDRALIGN=3 + +## Override the sh_entsize field (sh_entsize=3). +# RUN: yaml2obj --docnum=1 -DENTSIZE=3 %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefixes=BASIC,COMMON,ENTSIZE \ +# RUN: -DTYPE=PROGBITS -DFLAGS="[ SHF_MERGE, SHF_STRINGS ]" -D#%x,ADDRALIGN=1 -D#%x,ENTSIZE=3 + +## Override the sh_entsize field (sh_entsize=0). +# RUN: yaml2obj --docnum=1 -DENTSIZE=0 %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefixes=BASIC,COMMON,ENTSIZE \ +# RUN: -DTYPE=PROGBITS -DFLAGS="[ SHF_MERGE, SHF_STRINGS ]" -D#%x,ADDRALIGN=1 -D#%x,ENTSIZE=0 + +## Override the sh_info field. +# RUN: yaml2obj --docnum=1 -DINFO=3 %s | obj2yaml | \ +# RUN: FileCheck %s --check-prefixes=BASIC,COMMON,INFO \ +# RUN: -DTYPE=PROGBITS -DFLAGS="[ SHF_MERGE, SHF_STRINGS ]" -D#%x,INFO=3 -D#%x,ADDRALIGN=1 -D#%x,ENTSIZE=1 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +Sections: + - Name: .debug_str + Type: SHT_[[TYPE=PROGBITS]] + Flags: [[FLAGS=]] + Link: [[LINK='']] + EntSize: [[ENTSIZE=1]] + Info: [[INFO=]] + AddressAlign: [[ADDRALIGN=1]] + Address: [[ADDRESS=]] + - Name: .sec + Type: SHT_PROGBITS +DWARF: + debug_str: + - a + - b + - abc + +# COMMON: - Name: .debug_str +# COMMON-NEXT: Type: SHT_[[TYPE]] +# COMMON-NEXT: Flags: [[FLAGS]] +# LINK-NEXT: Link: .sec +# ADDRESS-NEXT: Address: 0x[[#%.16x,ADDRESS]] +# COMMON-NEXT: AddressAlign: 0x[[#%.16x,ADDRALIGN]] +# ENTSIZE-NEXT: EntSize: 0x[[#%.16x,ENTSIZE]] +# INFO-NEXT: Info: 0x[[#%.16x,INFO]] +# BASIC: DWARF: +# BASIC-NEXT: debug_str: +# BASIC-NEXT: - a +# BASIC-NEXT: - b +# BASIC-NEXT: - abc +# BASIC-NEXT: ... + +## c) Test dumping an empty .debug_str section. + +# RUN: yaml2obj --docnum=2 %s | obj2yaml | FileCheck %s --check-prefix=EMPTY --implicit-check-not=Sections + +# EMPTY: DWARF: +# EMPTY-NEXT: debug_str: [] +# EMPTY-NEXT: ... + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC +DWARF: + debug_str: [] diff --git a/llvm/tools/obj2yaml/elf2yaml.cpp b/llvm/tools/obj2yaml/elf2yaml.cpp index 632ec1bc9af416..9f524479bb04c3 100644 --- a/llvm/tools/obj2yaml/elf2yaml.cpp +++ b/llvm/tools/obj2yaml/elf2yaml.cpp @@ -197,12 +197,22 @@ bool ELFDumper::shouldPrintSection(const ELFYAML::Section &S, // entry but their section headers may have special flags, entry size, address // alignment, etc. We will preserve the header for them under such // circumstances. - if (DWARF && DWARF->getNonEmptySectionNames().count(S.Name.substr(1))) { + StringRef SecName = S.Name.substr(1); + if (DWARF && DWARF->getNonEmptySectionNames().count(SecName)) { if (const ELFYAML::RawContentSection *RawSec = - dyn_cast(&S)) - return RawSec->Type != ELF::SHT_PROGBITS || RawSec->Flags || - !RawSec->Link.empty() || RawSec->Info || - RawSec->AddressAlign != 1 || RawSec->EntSize; + dyn_cast(&S)) { + if (RawSec->Type != ELF::SHT_PROGBITS || !RawSec->Link.empty() || + RawSec->Info || RawSec->AddressAlign != 1 || RawSec->Address || + RawSec->EntSize) + return true; + + ELFYAML::ELF_SHF ShFlags = RawSec->Flags.getValueOr(ELFYAML::ELF_SHF(0)); + + if (SecName == "debug_str") + return ShFlags != ELFYAML::ELF_SHF(ELF::SHF_MERGE | ELF::SHF_STRINGS); + + return ShFlags != 0; + } } // Normally we use "Symbols:" and "DynamicSymbols:" to describe contents of @@ -404,6 +414,8 @@ Optional ELFDumper::dumpDWARFSections( if (RawSec->Name == ".debug_aranges") Err = dumpDebugARanges(*DWARFCtx.get(), DWARF); + else if (RawSec->Name == ".debug_str") + dumpDebugStrings(*DWARFCtx.get(), DWARF); // If the DWARF section cannot be successfully parsed, emit raw content // instead of an entry in the DWARF section of the YAML. @@ -622,7 +634,8 @@ Error ELFDumper::dumpRelocation(const RelT *Rel, const Elf_Shdr *SymTab, } template -static unsigned getDefaultShEntSize(ELFYAML::ELF_SHT SecType) { +static unsigned getDefaultShEntSize(ELFYAML::ELF_SHT SecType, + StringRef SecName) { switch (SecType) { case ELF::SHT_REL: return sizeof(typename ELFT::Rel); @@ -633,6 +646,8 @@ static unsigned getDefaultShEntSize(ELFYAML::ELF_SHT SecType) { case ELF::SHT_DYNAMIC: return sizeof(typename ELFT::Dyn); default: + if (SecName == ".debug_str") + return 1; return 0; } } @@ -649,9 +664,6 @@ Error ELFDumper::dumpCommonSection(const Elf_Shdr *Shdr, S.Address = static_cast(Shdr->sh_addr); S.AddressAlign = Shdr->sh_addralign; - if (Shdr->sh_entsize != getDefaultShEntSize(S.Type)) - S.EntSize = static_cast(Shdr->sh_entsize); - S.OriginalSecNdx = Shdr - &Sections[0]; auto NameOrErr = getUniquedSectionName(Shdr); @@ -659,6 +671,9 @@ Error ELFDumper::dumpCommonSection(const Elf_Shdr *Shdr, return NameOrErr.takeError(); S.Name = NameOrErr.get(); + if (Shdr->sh_entsize != getDefaultShEntSize(S.Type, S.Name)) + S.EntSize = static_cast(Shdr->sh_entsize); + if (Shdr->sh_link != ELF::SHN_UNDEF) { auto LinkSection = Obj.getSection(Shdr->sh_link); if (!LinkSection)