From 2ec85713bd910c5b22ce090798ca00f742d5eb14 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Wed, 15 May 2024 11:57:48 -0500 Subject: [PATCH 001/403] [OpenMP] Add back in `ENABLE_LIBOMPTARGET' definition Summary: Even though we moved `libomptarget` this is still present in `omp.h` and can't be removed. --- openmp/CMakeLists.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/openmp/CMakeLists.txt b/openmp/CMakeLists.txt index 33bfdc8630ef..9097ca562300 100644 --- a/openmp/CMakeLists.txt +++ b/openmp/CMakeLists.txt @@ -97,6 +97,18 @@ set(OPENMP_TEST_FLAGS "" CACHE STRING set(OPENMP_TEST_OPENMP_FLAGS ${OPENMP_TEST_COMPILER_OPENMP_FLAGS} CACHE STRING "OpenMP compiler flag to use for testing OpenMP runtime libraries.") +set(ENABLE_LIBOMPTARGET ON) +# Currently libomptarget cannot be compiled on Windows or MacOS X. +# Since the device plugins are only supported on Linux anyway, +# there is no point in trying to compile libomptarget on other OSes. +# 32-bit systems are not supported either. +if (APPLE OR WIN32 OR WASM OR NOT "cxx_std_17" IN_LIST CMAKE_CXX_COMPILE_FEATURES + OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8 OR ${CMAKE_SYSTEM_NAME} MATCHES "AIX") + set(ENABLE_LIBOMPTARGET OFF) +endif() + +option(OPENMP_ENABLE_LIBOMPTARGET "Enable building libomptarget for offloading." + ${ENABLE_LIBOMPTARGET}) option(OPENMP_ENABLE_LIBOMP_PROFILING "Enable time profiling for libomp." OFF) # Header install location -- GitLab From 4525f442fadb7cc44cc2eaede2c8ac6ba15bdf78 Mon Sep 17 00:00:00 2001 From: Krzysztof Parzyszek Date: Wed, 15 May 2024 12:01:16 -0500 Subject: [PATCH 002/403] [flang][OpenMP] Don't pass clauses to op-generating functions anymore (#90108) Remove parameter `const List &clauses` from functions that take construct queue. The clauses should now be accessed from the construct queue. --- flang/lib/Lower/OpenMP/OpenMP.cpp | 232 +++++++++++++----------------- 1 file changed, 103 insertions(+), 129 deletions(-) diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index f21acdd64d7c..f05cf1f5120f 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -1233,8 +1233,7 @@ genCriticalOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item, + const ConstructQueue &queue, ConstructQueue::iterator item, const std::optional &name) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); mlir::FlatSymbolRefAttr nameAttr; @@ -1245,8 +1244,8 @@ genCriticalOp(Fortran::lower::AbstractConverter &converter, auto global = mod.lookupSymbol(nameStr); if (!global) { mlir::omp::CriticalClauseOps clauseOps; - genCriticalDeclareClauses(converter, semaCtx, clauses, loc, clauseOps, - nameStr); + genCriticalDeclareClauses(converter, semaCtx, item->clauses, loc, + clauseOps, nameStr); mlir::OpBuilder modBuilder(mod.getBodyRegion()); global = modBuilder.create(loc, clauseOps); @@ -1266,8 +1265,7 @@ genDistributeOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { TODO(loc, "Distribute construct"); return nullptr; } @@ -1277,10 +1275,11 @@ genFlushOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const ObjectList &objects, const List &clauses, - const ConstructQueue &queue, ConstructQueue::iterator item) { + const ObjectList &objects, const ConstructQueue &queue, + ConstructQueue::iterator item) { llvm::SmallVector operandRange; - genFlushClauses(converter, semaCtx, objects, clauses, loc, operandRange); + genFlushClauses(converter, semaCtx, objects, item->clauses, loc, + operandRange); return converter.getFirOpBuilder().create( converter.getCurrentLocation(), operandRange); @@ -1291,8 +1290,7 @@ genMasterOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { return genOpWithBody( OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, llvm::omp::Directive::OMPD_master), @@ -1304,8 +1302,7 @@ genOrderedOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { TODO(loc, "OMPD_ordered"); return nullptr; } @@ -1315,10 +1312,9 @@ genOrderedRegionOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { mlir::omp::OrderedRegionClauseOps clauseOps; - genOrderedRegionClauses(converter, semaCtx, clauses, loc, clauseOps); + genOrderedRegionClauses(converter, semaCtx, item->clauses, loc, clauseOps); return genOpWithBody( OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, @@ -1331,15 +1327,15 @@ genParallelOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item, bool outerCombined = false) { + const ConstructQueue &queue, ConstructQueue::iterator item, + bool outerCombined = false) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); Fortran::lower::StatementContext stmtCtx; mlir::omp::ParallelClauseOps clauseOps; llvm::SmallVector privateSyms; llvm::SmallVector reductionTypes; llvm::SmallVector reductionSyms; - genParallelClauses(converter, semaCtx, stmtCtx, clauses, loc, + genParallelClauses(converter, semaCtx, stmtCtx, item->clauses, loc, /*processReduction=*/!outerCombined, clauseOps, reductionTypes, reductionSyms); @@ -1352,7 +1348,7 @@ genParallelOp(Fortran::lower::AbstractConverter &converter, OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, llvm::omp::Directive::OMPD_parallel) .setOuterCombined(outerCombined) - .setClauses(&clauses) + .setClauses(&item->clauses) .setReductions(&reductionSyms, &reductionTypes) .setGenRegionEntryCb(reductionCallback); @@ -1361,7 +1357,7 @@ genParallelOp(Fortran::lower::AbstractConverter &converter, clauseOps); bool privatize = !outerCombined; - DataSharingProcessor dsp(converter, semaCtx, clauses, eval, + DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval, /*useDelayedPrivatization=*/true, &symTable); if (privatize) @@ -1414,14 +1410,13 @@ genSectionOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { // Currently only private/firstprivate clause is handled, and // all privatization is done within `omp.section` operations. return genOpWithBody( OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, llvm::omp::Directive::OMPD_section) - .setClauses(&clauses), + .setClauses(&item->clauses), queue, item); } @@ -1430,22 +1425,21 @@ genSectionsOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { mlir::omp::SectionsClauseOps clauseOps; - genSectionsClauses(converter, semaCtx, clauses, loc, clauseOps); + genSectionsClauses(converter, semaCtx, item->clauses, loc, clauseOps); auto &builder = converter.getFirOpBuilder(); // Insert privatizations before SECTIONS symTable.pushScope(); - DataSharingProcessor dsp(converter, semaCtx, clauses, eval); + DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval); dsp.processStep1(); List nonDsaClauses; List lastprivates; - for (const Clause &clause : clauses) { + for (const Clause &clause : item->clauses) { if (clause.id == llvm::omp::Clause::OMPC_lastprivate) { lastprivates.push_back(&std::get(clause.u)); } else { @@ -1508,18 +1502,18 @@ genSimdOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); - DataSharingProcessor dsp(converter, semaCtx, clauses, eval); + DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval); dsp.processStep1(); Fortran::lower::StatementContext stmtCtx; mlir::omp::LoopNestClauseOps loopClauseOps; mlir::omp::SimdClauseOps simdClauseOps; llvm::SmallVector iv; - genLoopNestClauses(converter, semaCtx, eval, clauses, loc, loopClauseOps, iv); - genSimdClauses(converter, semaCtx, clauses, loc, simdClauseOps); + genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc, + loopClauseOps, iv); + genSimdClauses(converter, semaCtx, item->clauses, loc, simdClauseOps); // Create omp.simd wrapper. auto simdOp = firOpBuilder.create(loc, simdClauseOps); @@ -1532,7 +1526,8 @@ genSimdOp(Fortran::lower::AbstractConverter &converter, // Create nested omp.loop_nest and fill body with loop contents. auto loopOp = firOpBuilder.create(loc, loopClauseOps); - auto *nestedEval = getCollapsedLoopEval(eval, getCollapseValue(clauses)); + auto *nestedEval = + getCollapsedLoopEval(eval, getCollapseValue(item->clauses)); auto ivCallback = [&](mlir::Operation *op) { genLoopVars(op, converter, loc, iv); @@ -1542,7 +1537,7 @@ genSimdOp(Fortran::lower::AbstractConverter &converter, createBodyOfOp(*loopOp, OpWithBodyGenInfo(converter, symTable, semaCtx, loc, *nestedEval, llvm::omp::Directive::OMPD_simd) - .setClauses(&clauses) + .setClauses(&item->clauses) .setDataSharingProcessor(&dsp) .setGenRegionEntryCb(ivCallback), queue, item); @@ -1555,15 +1550,14 @@ genSingleOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { mlir::omp::SingleClauseOps clauseOps; - genSingleClauses(converter, semaCtx, clauses, loc, clauseOps); + genSingleClauses(converter, semaCtx, item->clauses, loc, clauseOps); return genOpWithBody( OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, llvm::omp::Directive::OMPD_single) - .setClauses(&clauses), + .setClauses(&item->clauses), queue, item, clauseOps); } @@ -1572,8 +1566,8 @@ genTargetOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item, bool outerCombined = false) { + const ConstructQueue &queue, ConstructQueue::iterator item, + bool outerCombined = false) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); Fortran::lower::StatementContext stmtCtx; @@ -1586,7 +1580,7 @@ genTargetOp(Fortran::lower::AbstractConverter &converter, deviceAddrSyms; llvm::SmallVector mapLocs, devicePtrLocs, deviceAddrLocs; llvm::SmallVector mapTypes, devicePtrTypes, deviceAddrTypes; - genTargetClauses(converter, semaCtx, stmtCtx, clauses, loc, + genTargetClauses(converter, semaCtx, stmtCtx, item->clauses, loc, processHostOnlyClauses, /*processReduction=*/outerCombined, clauseOps, mapSyms, mapLocs, mapTypes, deviceAddrSyms, deviceAddrLocs, deviceAddrTypes, devicePtrSyms, @@ -1690,15 +1684,14 @@ genTargetDataOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { Fortran::lower::StatementContext stmtCtx; mlir::omp::TargetDataClauseOps clauseOps; llvm::SmallVector useDeviceTypes; llvm::SmallVector useDeviceLocs; llvm::SmallVector useDeviceSyms; - genTargetDataClauses(converter, semaCtx, stmtCtx, clauses, loc, clauseOps, - useDeviceTypes, useDeviceLocs, useDeviceSyms); + genTargetDataClauses(converter, semaCtx, stmtCtx, item->clauses, loc, + clauseOps, useDeviceTypes, useDeviceLocs, useDeviceSyms); auto targetDataOp = converter.getFirOpBuilder().create(loc, @@ -1714,8 +1707,7 @@ static OpTy genTargetEnterExitUpdateDataOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, - mlir::Location loc, const List &clauses, - const ConstructQueue &queue, + mlir::Location loc, const ConstructQueue &queue, ConstructQueue::iterator item) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); Fortran::lower::StatementContext stmtCtx; @@ -1733,8 +1725,8 @@ genTargetEnterExitUpdateDataOp(Fortran::lower::AbstractConverter &converter, } mlir::omp::TargetEnterExitUpdateDataClauseOps clauseOps; - genTargetEnterExitUpdateDataClauses(converter, semaCtx, stmtCtx, clauses, loc, - directive, clauseOps); + genTargetEnterExitUpdateDataClauses(converter, semaCtx, stmtCtx, + item->clauses, loc, directive, clauseOps); return firOpBuilder.create(loc, clauseOps); } @@ -1744,16 +1736,15 @@ genTaskOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { Fortran::lower::StatementContext stmtCtx; mlir::omp::TaskClauseOps clauseOps; - genTaskClauses(converter, semaCtx, stmtCtx, clauses, loc, clauseOps); + genTaskClauses(converter, semaCtx, stmtCtx, item->clauses, loc, clauseOps); return genOpWithBody( OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, llvm::omp::Directive::OMPD_task) - .setClauses(&clauses), + .setClauses(&item->clauses), queue, item, clauseOps); } @@ -1762,15 +1753,14 @@ genTaskgroupOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { mlir::omp::TaskgroupClauseOps clauseOps; - genTaskgroupClauses(converter, semaCtx, clauses, loc, clauseOps); + genTaskgroupClauses(converter, semaCtx, item->clauses, loc, clauseOps); return genOpWithBody( OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, llvm::omp::Directive::OMPD_taskgroup) - .setClauses(&clauses), + .setClauses(&item->clauses), queue, item, clauseOps); } @@ -1779,8 +1769,7 @@ genTaskloopOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { TODO(loc, "Taskloop construct"); } @@ -1789,10 +1778,9 @@ genTaskwaitOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { mlir::omp::TaskwaitClauseOps clauseOps; - genTaskwaitClauses(converter, semaCtx, clauses, loc, clauseOps); + genTaskwaitClauses(converter, semaCtx, item->clauses, loc, clauseOps); return converter.getFirOpBuilder().create(loc, clauseOps); } @@ -1811,17 +1799,17 @@ genTeamsOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item, bool outerCombined = false) { + const ConstructQueue &queue, ConstructQueue::iterator item, + bool outerCombined = false) { Fortran::lower::StatementContext stmtCtx; mlir::omp::TeamsClauseOps clauseOps; - genTeamsClauses(converter, semaCtx, stmtCtx, clauses, loc, clauseOps); + genTeamsClauses(converter, semaCtx, stmtCtx, item->clauses, loc, clauseOps); return genOpWithBody( OpWithBodyGenInfo(converter, symTable, semaCtx, loc, eval, llvm::omp::Directive::OMPD_teams) .setOuterCombined(outerCombined) - .setClauses(&clauses), + .setClauses(&item->clauses), queue, item, clauseOps); } @@ -1830,10 +1818,9 @@ genWsloopOp(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder(); - DataSharingProcessor dsp(converter, semaCtx, clauses, eval); + DataSharingProcessor dsp(converter, semaCtx, item->clauses, eval); dsp.processStep1(); Fortran::lower::StatementContext stmtCtx; @@ -1842,8 +1829,9 @@ genWsloopOp(Fortran::lower::AbstractConverter &converter, llvm::SmallVector iv; llvm::SmallVector reductionTypes; llvm::SmallVector reductionSyms; - genLoopNestClauses(converter, semaCtx, eval, clauses, loc, loopClauseOps, iv); - genWsloopClauses(converter, semaCtx, stmtCtx, clauses, loc, wsClauseOps, + genLoopNestClauses(converter, semaCtx, eval, item->clauses, loc, + loopClauseOps, iv); + genWsloopClauses(converter, semaCtx, stmtCtx, item->clauses, loc, wsClauseOps, reductionTypes, reductionSyms); // Create omp.wsloop wrapper and populate entry block arguments with reduction @@ -1858,7 +1846,8 @@ genWsloopOp(Fortran::lower::AbstractConverter &converter, // Create nested omp.loop_nest and fill body with loop contents. auto loopOp = firOpBuilder.create(loc, loopClauseOps); - auto *nestedEval = getCollapsedLoopEval(eval, getCollapseValue(clauses)); + auto *nestedEval = + getCollapsedLoopEval(eval, getCollapseValue(item->clauses)); auto ivCallback = [&](mlir::Operation *op) { genLoopVars(op, converter, loc, iv, reductionSyms, @@ -1869,7 +1858,7 @@ genWsloopOp(Fortran::lower::AbstractConverter &converter, createBodyOfOp(*loopOp, OpWithBodyGenInfo(converter, symTable, semaCtx, loc, *nestedEval, llvm::omp::Directive::OMPD_do) - .setClauses(&clauses) + .setClauses(&item->clauses) .setDataSharingProcessor(&dsp) .setReductions(&reductionSyms, &reductionTypes) .setGenRegionEntryCb(ivCallback), @@ -1886,8 +1875,7 @@ static void genCompositeDistributeParallelDo( Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { TODO(loc, "Composite DISTRIBUTE PARALLEL DO"); } @@ -1896,8 +1884,7 @@ static void genCompositeDistributeParallelDoSimd( Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, mlir::Location loc, - const List &clauses, const ConstructQueue &queue, - ConstructQueue::iterator item) { + const ConstructQueue &queue, ConstructQueue::iterator item) { TODO(loc, "Composite DISTRIBUTE PARALLEL DO SIMD"); } @@ -1906,8 +1893,7 @@ genCompositeDistributeSimd(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, - mlir::Location loc, const List &clauses, - const ConstructQueue &queue, + mlir::Location loc, const ConstructQueue &queue, ConstructQueue::iterator item) { TODO(loc, "Composite DISTRIBUTE SIMD"); } @@ -1916,10 +1902,9 @@ static void genCompositeDoSimd(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, - mlir::Location loc, const List &clauses, - const ConstructQueue &queue, + mlir::Location loc, const ConstructQueue &queue, ConstructQueue::iterator item) { - ClauseProcessor cp(converter, semaCtx, clauses); + ClauseProcessor cp(converter, semaCtx, item->clauses); cp.processTODO( loc, llvm::omp::OMPD_do_simd); @@ -1931,7 +1916,7 @@ static void genCompositeDoSimd(Fortran::lower::AbstractConverter &converter, // When support for vectorization is enabled, then we need to add handling of // if clause. Currently if clause can be skipped because we always assume // SIMD length = 1. - genWsloopOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item); + genWsloopOp(converter, symTable, semaCtx, eval, loc, queue, item); } static void @@ -1939,8 +1924,7 @@ genCompositeTaskloopSimd(Fortran::lower::AbstractConverter &converter, Fortran::lower::SymMap &symTable, Fortran::semantics::SemanticsContext &semaCtx, Fortran::lower::pft::Evaluation &eval, - mlir::Location loc, const List &clauses, - const ConstructQueue &queue, + mlir::Location loc, const ConstructQueue &queue, ConstructQueue::iterator item) { TODO(loc, "Composite TASKLOOP SIMD"); } @@ -1956,18 +1940,16 @@ static void genOMPDispatch(Fortran::lower::AbstractConverter &converter, mlir::Location loc, const ConstructQueue &queue, ConstructQueue::iterator item) { assert(item != queue.end()); - const List &clauses = item->clauses; switch (llvm::omp::Directive dir = item->id) { case llvm::omp::Directive::OMPD_barrier: genBarrierOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_distribute: - genDistributeOp(converter, symTable, semaCtx, eval, loc, clauses, queue, - item); + genDistributeOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_do: - genWsloopOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item); + genWsloopOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_loop: case llvm::omp::Directive::OMPD_masked: @@ -1975,71 +1957,64 @@ static void genOMPDispatch(Fortran::lower::AbstractConverter &converter, llvm::omp::getOpenMPDirectiveName(dir) + ")"); break; case llvm::omp::Directive::OMPD_master: - genMasterOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item); + genMasterOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_ordered: // Block-associated "ordered" construct. - genOrderedRegionOp(converter, symTable, semaCtx, eval, loc, clauses, queue, - item); + genOrderedRegionOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_parallel: - genParallelOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item, + genParallelOp(converter, symTable, semaCtx, eval, loc, queue, item, /*outerCombined=*/false); break; case llvm::omp::Directive::OMPD_section: - genSectionOp(converter, symTable, semaCtx, eval, loc, /*clauses=*/{}, queue, - item); + genSectionOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_sections: - genSectionsOp(converter, symTable, semaCtx, eval, loc, clauses, queue, - item); + genSectionsOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_simd: - genSimdOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item); + genSimdOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_single: - genSingleOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item); + genSingleOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_target: - genTargetOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item, + genTargetOp(converter, symTable, semaCtx, eval, loc, queue, item, /*outerCombined=*/false); break; case llvm::omp::Directive::OMPD_target_data: - genTargetDataOp(converter, symTable, semaCtx, eval, loc, clauses, queue, - item); + genTargetDataOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_target_enter_data: genTargetEnterExitUpdateDataOp( - converter, symTable, semaCtx, loc, clauses, queue, item); + converter, symTable, semaCtx, loc, queue, item); break; case llvm::omp::Directive::OMPD_target_exit_data: genTargetEnterExitUpdateDataOp( - converter, symTable, semaCtx, loc, clauses, queue, item); + converter, symTable, semaCtx, loc, queue, item); break; case llvm::omp::Directive::OMPD_target_update: genTargetEnterExitUpdateDataOp( - converter, symTable, semaCtx, loc, clauses, queue, item); + converter, symTable, semaCtx, loc, queue, item); break; case llvm::omp::Directive::OMPD_task: - genTaskOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item); + genTaskOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_taskgroup: - genTaskgroupOp(converter, symTable, semaCtx, eval, loc, clauses, queue, - item); + genTaskgroupOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_taskloop: - genTaskloopOp(converter, symTable, semaCtx, eval, loc, clauses, queue, - item); + genTaskloopOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_taskwait: - genTaskwaitOp(converter, symTable, semaCtx, eval, loc, clauses, queue, - item); + genTaskwaitOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_taskyield: genTaskyieldOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_teams: - genTeamsOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item); + genTeamsOp(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_tile: case llvm::omp::Directive::OMPD_unroll: @@ -2050,29 +2025,28 @@ static void genOMPDispatch(Fortran::lower::AbstractConverter &converter, // FIXME: Workshare is not a commonly used OpenMP construct, an // implementation for this feature will come later. For the codes // that use this construct, add a single construct for now. - genSingleOp(converter, symTable, semaCtx, eval, loc, clauses, queue, item); + genSingleOp(converter, symTable, semaCtx, eval, loc, queue, item); break; // Composite constructs case llvm::omp::Directive::OMPD_distribute_parallel_do: genCompositeDistributeParallelDo(converter, symTable, semaCtx, eval, loc, - clauses, queue, item); + queue, item); break; case llvm::omp::Directive::OMPD_distribute_parallel_do_simd: genCompositeDistributeParallelDoSimd(converter, symTable, semaCtx, eval, - loc, clauses, queue, item); + loc, queue, item); break; case llvm::omp::Directive::OMPD_distribute_simd: - genCompositeDistributeSimd(converter, symTable, semaCtx, eval, loc, clauses, - queue, item); + genCompositeDistributeSimd(converter, symTable, semaCtx, eval, loc, queue, + item); break; case llvm::omp::Directive::OMPD_do_simd: - genCompositeDoSimd(converter, symTable, semaCtx, eval, loc, clauses, queue, - item); + genCompositeDoSimd(converter, symTable, semaCtx, eval, loc, queue, item); break; case llvm::omp::Directive::OMPD_taskloop_simd: - genCompositeTaskloopSimd(converter, symTable, semaCtx, eval, loc, clauses, - queue, item); + genCompositeTaskloopSimd(converter, symTable, semaCtx, eval, loc, queue, + item); break; default: break; @@ -2194,8 +2168,8 @@ static void genOMP(Fortran::lower::AbstractConverter &converter, eval, directive.source, directive.v, clauses)}; if (directive.v == llvm::omp::Directive::OMPD_ordered) { // Standalone "ordered" directive. - genOrderedOp(converter, symTable, semaCtx, eval, currentLocation, clauses, - queue, queue.begin()); + genOrderedOp(converter, symTable, semaCtx, eval, currentLocation, queue, + queue.begin()); } else { // Dispatch handles the "block-associated" variant of "ordered". genOMPDispatch(converter, symTable, semaCtx, eval, currentLocation, queue, @@ -2227,7 +2201,7 @@ genOMP(Fortran::lower::AbstractConverter &converter, converter.getFirOpBuilder().getModule(), semaCtx, eval, verbatim.source, llvm::omp::Directive::OMPD_flush, clauses)}; genFlushOp(converter, symTable, semaCtx, eval, currentLocation, objects, - clauses, queue, queue.begin()); + queue, queue.begin()); } static void @@ -2399,8 +2373,8 @@ genOMP(Fortran::lower::AbstractConverter &converter, const auto &name = std::get>(cd.t); mlir::Location currentLocation = converter.getCurrentLocation(); - genCriticalOp(converter, symTable, semaCtx, eval, currentLocation, clauses, - queue, queue.begin(), name); + genCriticalOp(converter, symTable, semaCtx, eval, currentLocation, queue, + queue.begin(), name); } static void -- GitLab From eb822dc25853299ea81166f9bb8a43436ab8b0c8 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilyev Date: Wed, 15 May 2024 21:03:15 +0400 Subject: [PATCH 003/403] [lldb] Fixed the TestCompletion test running on a remote target (#92281) Install the image to the remote target if necessary. --- .../functionalities/completion/TestCompletion.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lldb/test/API/functionalities/completion/TestCompletion.py b/lldb/test/API/functionalities/completion/TestCompletion.py index 0d6907e0c3d2..63842487fc33 100644 --- a/lldb/test/API/functionalities/completion/TestCompletion.py +++ b/lldb/test/API/functionalities/completion/TestCompletion.py @@ -107,9 +107,16 @@ class CommandLineCompletionTestCase(TestBase): self, "// Break here", lldb.SBFileSpec("main.cpp") ) err = lldb.SBError() - self.process().LoadImage( - lldb.SBFileSpec(self.getBuildArtifact("libshared.so")), err + local_spec = lldb.SBFileSpec(self.getBuildArtifact("libshared.so")) + remote_spec = ( + lldb.SBFileSpec( + lldbutil.append_to_process_working_directory(self, "libshared.so"), + False, + ) + if lldb.remote_platform + else lldb.SBFileSpec() ) + self.process().LoadImage(local_spec, remote_spec, err) self.assertSuccess(err) self.complete_from_to("process unload ", "process unload 0") @@ -473,7 +480,7 @@ class CommandLineCompletionTestCase(TestBase): self.complete_from_to("my_test_cmd main.cp", ["main.cpp"]) self.expect("my_test_cmd main.cpp", substrs=["main.cpp"]) - @skipIfWindows + @skipIf(hostoslist=["windows"]) def test_completion_target_create_from_root_dir(self): """Tests source file completion by completing .""" root_dir = os.path.abspath(os.sep) -- GitLab From 7645269710493c188d1d270b9e4e085b3e92b9b0 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilyev Date: Wed, 15 May 2024 21:06:30 +0400 Subject: [PATCH 004/403] [lldb] Fixed the TestNetBSDCore test (#92285) TestNetBSDCore.py contains 3 classes with the same test names test_aarch64 and test_amd64. It causes conflicts because the same build dir. Add suffixes to avoid conflicts. --- .../postmortem/netbsd-core/TestNetBSDCore.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lldb/test/API/functionalities/postmortem/netbsd-core/TestNetBSDCore.py b/lldb/test/API/functionalities/postmortem/netbsd-core/TestNetBSDCore.py index 756f4d1e81ca..ff1ef21e02e3 100644 --- a/lldb/test/API/functionalities/postmortem/netbsd-core/TestNetBSDCore.py +++ b/lldb/test/API/functionalities/postmortem/netbsd-core/TestNetBSDCore.py @@ -147,12 +147,12 @@ class NetBSD1LWPCoreTestCase(NetBSDCoreCommonTestCase): self.check_backtrace(thread, filename, backtrace) @skipIfLLVMTargetMissing("AArch64") - def test_aarch64(self): + def test_aarch64_single_threaded(self): """Test single-threaded aarch64 core dump.""" self.do_test("1lwp_SIGSEGV.aarch64", pid=8339, region_count=32) @skipIfLLVMTargetMissing("X86") - def test_amd64(self): + def test_amd64_single_threaded(self): """Test single-threaded amd64 core dump.""" self.do_test("1lwp_SIGSEGV.amd64", pid=693, region_count=21) @@ -177,12 +177,12 @@ class NetBSD2LWPT2CoreTestCase(NetBSDCoreCommonTestCase): self.assertEqual(thread.GetStopReasonDataAtIndex(0), 0) @skipIfLLVMTargetMissing("AArch64") - def test_aarch64(self): + def test_aarch64_thread_signaled(self): """Test double-threaded aarch64 core dump where thread 2 is signalled.""" self.do_test("2lwp_t2_SIGSEGV.aarch64", pid=14142, region_count=31) @skipIfLLVMTargetMissing("X86") - def test_amd64(self): + def test_amd64_thread_signaled(self): """Test double-threaded amd64 core dump where thread 2 is signalled.""" self.do_test("2lwp_t2_SIGSEGV.amd64", pid=622, region_count=24) @@ -207,11 +207,11 @@ class NetBSD2LWPProcessSigCoreTestCase(NetBSDCoreCommonTestCase): self.assertEqual(thread.GetStopReasonDataAtIndex(0), signal.SIGSEGV) @skipIfLLVMTargetMissing("AArch64") - def test_aarch64(self): + def test_aarch64_process_signaled(self): """Test double-threaded aarch64 core dump where process is signalled.""" self.do_test("2lwp_process_SIGSEGV.aarch64", pid=1403, region_count=30) @skipIfLLVMTargetMissing("X86") - def test_amd64(self): + def test_amd64_process_signaled(self): """Test double-threaded amd64 core dump where process is signalled.""" self.do_test("2lwp_process_SIGSEGV.amd64", pid=665, region_count=24) -- GitLab From d92c67784f21063d6334a009dbf4f9e0f8217b41 Mon Sep 17 00:00:00 2001 From: Dmitry Vasilyev Date: Wed, 15 May 2024 21:08:35 +0400 Subject: [PATCH 005/403] [lldb][Windows] Fixed the TestIOHandlerResizeNoEditline test (#92286) This test caused python crash on Windows x86_64 host with the exit code 0xC0000409 (STATUS_STACK_BUFFER_OVERRUN). Close the input stream before exit to avoid this crash. --- lldb/test/API/iohandler/resize/TestIOHandlerResizeNoEditline.py | 1 + 1 file changed, 1 insertion(+) diff --git a/lldb/test/API/iohandler/resize/TestIOHandlerResizeNoEditline.py b/lldb/test/API/iohandler/resize/TestIOHandlerResizeNoEditline.py index 3c07554f6caf..bbc2dcbe4e30 100644 --- a/lldb/test/API/iohandler/resize/TestIOHandlerResizeNoEditline.py +++ b/lldb/test/API/iohandler/resize/TestIOHandlerResizeNoEditline.py @@ -18,3 +18,4 @@ class TestCase(TestBase): dbg.RunCommandInterpreter(True, True, opts, 0, False, False) # Try resizing the terminal which shouldn't crash. dbg.SetTerminalWidth(47) + dbg.GetInputFile().Close() -- GitLab From 217668f641e82f901645f428ae0d07a3c01e9a8a Mon Sep 17 00:00:00 2001 From: Mircea Trofin Date: Wed, 15 May 2024 10:34:47 -0700 Subject: [PATCH 006/403] [nfc] Allow forwarding `Error` returns from `Expected` callers (#92208) On a few compilers (clang 11/12 for example [1]), the following does not result in a copy elision, and since `Error`'s copy dtor is elided, results in a compile error: ``` Expect foobar() { ... if (Error E = aCallReturningError()) return E; ... } ``` Moving `E` would, conversely, result in the pessimizing-move warning on more recent clangs ("moving a local object in a return statement prevents copy elision") We just need to make the `Expected` ctor taking an `Error` take it as a r-value reference. [1] https://lab.llvm.org/buildbot/#/builders/54/builds/10505 --- llvm/include/llvm/Support/Error.h | 2 +- llvm/lib/Bitstream/Reader/BitstreamReader.cpp | 12 ++++++------ llvm/lib/Object/COFFObjectFile.cpp | 6 +++--- llvm/lib/Object/WindowsResource.cpp | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h index 894b6484336a..217130ce293a 100644 --- a/llvm/include/llvm/Support/Error.h +++ b/llvm/include/llvm/Support/Error.h @@ -493,7 +493,7 @@ private: public: /// Create an Expected error value from the given Error. - Expected(Error Err) + Expected(Error &&Err) : HasError(true) #if LLVM_ENABLE_ABI_BREAKING_CHECKS // Expected is unchecked upon construction in Debug builds. diff --git a/llvm/lib/Bitstream/Reader/BitstreamReader.cpp b/llvm/lib/Bitstream/Reader/BitstreamReader.cpp index 3cc9dfdf7b85..5b2c76350029 100644 --- a/llvm/lib/Bitstream/Reader/BitstreamReader.cpp +++ b/llvm/lib/Bitstream/Reader/BitstreamReader.cpp @@ -167,7 +167,7 @@ Expected BitstreamCursor::skipRecord(unsigned AbbrevID) { if (Error Err = JumpToBit(GetCurrentBitNo() + static_cast(NumElts) * EltEnc.getEncodingData())) - return std::move(Err); + return Err; break; case BitCodeAbbrevOp::VBR: assert((unsigned)EltEnc.getEncodingData() <= MaxChunkSize); @@ -180,7 +180,7 @@ Expected BitstreamCursor::skipRecord(unsigned AbbrevID) { break; case BitCodeAbbrevOp::Char6: if (Error Err = JumpToBit(GetCurrentBitNo() + NumElts * 6)) - return std::move(Err); + return Err; break; } continue; @@ -206,7 +206,7 @@ Expected BitstreamCursor::skipRecord(unsigned AbbrevID) { // Skip over the blob. if (Error Err = JumpToBit(NewEnd)) - return std::move(Err); + return Err; } return Code; } @@ -344,7 +344,7 @@ Expected BitstreamCursor::readRecord(unsigned AbbrevID, // over tail padding first, in case jumping to NewEnd invalidates the Blob // pointer. if (Error Err = JumpToBit(NewEnd)) - return std::move(Err); + return Err; const char *Ptr = (const char *)getPointerToBit(CurBitPos, NumElts); // If we can return a reference to the data, do so to avoid copying it. @@ -421,7 +421,7 @@ Error BitstreamCursor::ReadAbbrevRecord() { Expected> BitstreamCursor::ReadBlockInfoBlock(bool ReadBlockInfoNames) { if (llvm::Error Err = EnterSubBlock(bitc::BLOCKINFO_BLOCK_ID)) - return std::move(Err); + return Err; BitstreamBlockInfo NewBlockInfo; @@ -452,7 +452,7 @@ BitstreamCursor::ReadBlockInfoBlock(bool ReadBlockInfoNames) { if (!CurBlockInfo) return std::nullopt; if (Error Err = ReadAbbrevRecord()) - return std::move(Err); + return Err; // ReadAbbrevRecord installs the abbrev in CurAbbrevs. Move it to the // appropriate BlockInfo. diff --git a/llvm/lib/Object/COFFObjectFile.cpp b/llvm/lib/Object/COFFObjectFile.cpp index 18506f39f6b5..5a85b8e00c63 100644 --- a/llvm/lib/Object/COFFObjectFile.cpp +++ b/llvm/lib/Object/COFFObjectFile.cpp @@ -294,7 +294,7 @@ COFFObjectFile::getSectionContents(DataRefImpl Ref) const { const coff_section *Sec = toSec(Ref); ArrayRef Res; if (Error E = getSectionContents(Sec, Res)) - return std::move(E); + return E; return Res; } @@ -807,7 +807,7 @@ Expected> COFFObjectFile::create(MemoryBufferRef Object) { std::unique_ptr Obj(new COFFObjectFile(std::move(Object))); if (Error E = Obj->initialize()) - return std::move(E); + return E; return std::move(Obj); } @@ -1959,7 +1959,7 @@ ResourceSectionRef::getContents(const coff_resource_data_entry &Entry) { uint64_t Offset = Entry.DataRVA + Sym->getValue(); ArrayRef Contents; if (Error E = Obj->getSectionContents(*Section, Contents)) - return std::move(E); + return E; if (Offset + Entry.DataSize > Contents.size()) return createStringError(object_error::parse_failed, "data outside of section"); diff --git a/llvm/lib/Object/WindowsResource.cpp b/llvm/lib/Object/WindowsResource.cpp index 983c8e30a942..306e8ec54206 100644 --- a/llvm/lib/Object/WindowsResource.cpp +++ b/llvm/lib/Object/WindowsResource.cpp @@ -80,7 +80,7 @@ Expected ResourceEntryRef::create(BinaryStreamRef BSR, const WindowsResource *Owner) { auto Ref = ResourceEntryRef(BSR, Owner); if (auto E = Ref.loadNext()) - return std::move(E); + return E; return Ref; } @@ -1006,7 +1006,7 @@ writeWindowsResourceCOFF(COFF::MachineTypes MachineType, Error E = Error::success(); WindowsResourceCOFFWriter Writer(MachineType, Parser, E); if (E) - return std::move(E); + return E; return Writer.write(TimeDateStamp); } -- GitLab From 0647d1035cb208195e002b38089b82004b6f7b92 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Wed, 15 May 2024 10:15:35 -0700 Subject: [PATCH 007/403] [RISCV] Remove unneeded casts from int64_t to uint64_t in RISCVMatInt.cpp. NFC Most of these were to avoid undefined behavior if a shift left changed the sign of the result. I don't think its possible to change the sign of the result here. We're shifting left by 12 after an arithmetic right shift by more than 12. The bits we are shifting out with the left shift are guaranteed to be sign bits. Also use SignExtend64<32> to force upper bits to all 1s instead of an Or. We know the value isUInt<32> && !isInt<32> which means bit 31 is set. --- .../Target/RISCV/MCTargetDesc/RISCVMatInt.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp index 0a857eb96935..fca3362f9a8b 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMatInt.cpp @@ -115,30 +115,29 @@ static void generateInstSeqImpl(int64_t Val, const MCSubtargetInfo &STI, Val >>= ShiftAmount; // If the remaining bits don't fit in 12 bits, we might be able to reduce - // the // shift amount in order to use LUI which will zero the lower 12 - // bits. + // the shift amount in order to use LUI which will zero the lower 12 bits. if (ShiftAmount > 12 && !isInt<12>(Val)) { - if (isInt<32>((uint64_t)Val << 12)) { + if (isInt<32>(Val << 12)) { // Reduce the shift amount and add zeros to the LSBs so it will match // LUI. ShiftAmount -= 12; - Val = (uint64_t)Val << 12; - } else if (isUInt<32>((uint64_t)Val << 12) && + Val = Val << 12; + } else if (isUInt<32>(Val << 12) && STI.hasFeature(RISCV::FeatureStdExtZba)) { // Reduce the shift amount and add zeros to the LSBs so it will match // LUI, then shift left with SLLI.UW to clear the upper 32 set bits. ShiftAmount -= 12; - Val = ((uint64_t)Val << 12) | (0xffffffffull << 32); + Val = SignExtend64<32>(Val << 12); Unsigned = true; } } // Try to use SLLI_UW for Val when it is uint32 but not int32. - if (isUInt<32>((uint64_t)Val) && !isInt<32>((uint64_t)Val) && + if (isUInt<32>(Val) && !isInt<32>(Val) && STI.hasFeature(RISCV::FeatureStdExtZba)) { - // Use LUI+ADDI or LUI to compose, then clear the upper 32 bits with + // Use LUI+ADDI(W) or LUI to compose, then clear the upper 32 bits with // SLLI_UW. - Val = ((uint64_t)Val) | (0xffffffffull << 32); + Val = SignExtend64<32>(Val); Unsigned = true; } } -- GitLab From ec36145f58d2cf93d86bc4e3be617ad7d7d8ace7 Mon Sep 17 00:00:00 2001 From: Florian Hahn Date: Wed, 15 May 2024 18:54:23 +0100 Subject: [PATCH 008/403] [LAA] Add tests with invariant dependences before strided ones. Add extra test coverage for loops with strided and invariant accesses to the same object. --- .../invariant-dependence-before.ll | 756 ++++++++++++++++++ 1 file changed, 756 insertions(+) create mode 100644 llvm/test/Analysis/LoopAccessAnalysis/invariant-dependence-before.ll diff --git a/llvm/test/Analysis/LoopAccessAnalysis/invariant-dependence-before.ll b/llvm/test/Analysis/LoopAccessAnalysis/invariant-dependence-before.ll new file mode 100644 index 000000000000..2a210a5a445b --- /dev/null +++ b/llvm/test/Analysis/LoopAccessAnalysis/invariant-dependence-before.ll @@ -0,0 +1,756 @@ +; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 3 +; RUN: opt -passes='print' -disable-output %s 2>&1 | FileCheck %s + +define void @test_invar_dependence_before_positive_strided_access_1(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_before_positive_strided_access_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %gep, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + store i32 %l, ptr %gep + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_before_positive_strided_access_2(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_before_positive_strided_access_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %gep, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %a, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %gep + store i32 %l, ptr %a + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_not_before_positive_strided_access_1(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_not_before_positive_strided_access_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %gep, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 3 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + store i32 %l, ptr %gep + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_not_before_positive_strided_access_2(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_not_before_positive_strided_access_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %gep, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %a, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 3 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %gep + store i32 %l, ptr %a + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_before_positive_strided_access_1_different_access_sizes(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_before_positive_strided_access_1_different_access_sizes' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i8 %t, ptr %gep, align 1 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + %t = trunc i32 %l to i8 + store i8 %t, ptr %gep + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_not_before_positive_strided_access_1_different_access_sizes(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_not_before_positive_strided_access_1_different_access_sizes' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i64, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %t, ptr %gep, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i64, ptr %a + %t = trunc i64 %l to i32 + store i32 %t, ptr %gep + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_before_negative_strided_access_1(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_before_negative_strided_access_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %gep, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i32, ptr %a, i32 100 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + store i32 %l, ptr %gep + %iv.next = sub i32 %iv, 1 + %ec = icmp eq i32 %iv.next, -100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_before_negative_strided_access_2(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_before_negative_strided_access_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %gep, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %a, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i32, ptr %a, i32 100 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %gep + store i32 %l, ptr %a + %iv.next = sub i32 %iv, 1 + %ec = icmp eq i32 %iv.next, -100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + + +define void @test_invar_dependence_not_before_negative_strided_access_1(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_not_before_negative_strided_access_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %gep, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i32, ptr %a, i32 99 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + store i32 %l, ptr %gep + %iv.next = sub i32 %iv, 1 + %ec = icmp eq i32 %iv.next, -100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_not_before_negative_strided_access_2(ptr %a) { +; CHECK-LABEL: 'test_invar_dependence_not_before_negative_strided_access_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %gep, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %a, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i32, ptr %a, i32 99 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %gep + store i32 %l, ptr %a + %iv.next = sub i32 %iv, 1 + %ec = icmp eq i32 %iv.next, -100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_both_invar_before_1(ptr %a) { +; CHECK-LABEL: 'test_both_invar_before_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %gep.off, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %l = load i32, ptr %a + store i32 %l, ptr %gep.off + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_both_invar_before_2(ptr %a) { +; CHECK-LABEL: 'test_both_invar_before_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %gep.off, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %a, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %l = load i32, ptr %gep.off + store i32 %l, ptr %a + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_both_invar_not_before_1(ptr %a) { +; CHECK-LABEL: 'test_both_invar_not_before_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %gep.off, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 3 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %l = load i32, ptr %a + store i32 %l, ptr %gep.off + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_both_invar_not_before_2(ptr %a) { +; CHECK-LABEL: 'test_both_invar_not_before_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %gep.off, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %a, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 3 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %l = load i32, ptr %gep.off + store i32 %l, ptr %a + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_before_via_loop_guard_positive_strided_access_1(ptr %a, i32 %off) { +; CHECK-LABEL: 'test_invar_dependence_before_via_loop_guard_positive_strided_access_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %gep, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 %off + %c = icmp sge i32 %off, 4 + br i1 %c, label %loop, label %exit + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + store i32 %l, ptr %gep + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_before_via_loop_guard_positive_strided_access_2(ptr %a, i32 %off) { +; CHECK-LABEL: 'test_invar_dependence_before_via_loop_guard_positive_strided_access_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %gep, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %a, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 %off + %c = icmp sge i32 %off, 4 + br i1 %c, label %loop, label %exit + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %gep + store i32 %l, ptr %a + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} +define void @test_invar_dependence_not_before_via_loop_guard_positive_strided_access_1(ptr %a, i32 %off) { +; CHECK-LABEL: 'test_invar_dependence_not_before_via_loop_guard_positive_strided_access_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %a, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %gep, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 %off + %c = icmp sge i32 %off, 3 + br i1 %c, label %loop, label %exit + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + store i32 %l, ptr %gep + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_not_before_via_loop_guard_positive_strided_access_2(ptr %a, i32 %off) { +; CHECK-LABEL: 'test_invar_dependence_not_before_via_loop_guard_positive_strided_access_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: unsafe dependent memory operations in loop. Use #pragma clang loop distribute(enable) to allow loop distribution to attempt to isolate the offending operations into a separate loop +; CHECK-NEXT: Unknown data dependence. +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Unknown: +; CHECK-NEXT: %l = load i32, ptr %gep, align 4 -> +; CHECK-NEXT: store i32 %l, ptr %a, align 4 +; CHECK-EMPTY: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 %off + %c = icmp sge i32 %off, 3 + br i1 %c, label %loop, label %exit + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %gep + store i32 %l, ptr %a + %iv.next = add i32 %iv, 1 + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_before_positive_strided_access_via_loop_guard_1(ptr %a, i32 %off) { +; CHECK-LABEL: 'test_invar_dependence_before_positive_strided_access_via_loop_guard_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: could not determine number of loop iterations +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + %c = icmp sge i32 %off, 0 + br i1 %c, label %loop, label %exit + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %gep + store i32 %l, ptr %a + %iv.next = add i32 %iv, %off + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_before_positive_strided_access_via_loop_guard_2(ptr %a, i32 %off) { +; CHECK-LABEL: 'test_invar_dependence_before_positive_strided_access_via_loop_guard_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: could not determine number of loop iterations +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + %c = icmp sge i32 %off, 0 + br i1 %c, label %loop, label %exit + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + store i32 %l, ptr %gep + %iv.next = add i32 %iv, %off + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_not_known_beforepositive_strided_access_not_known_via_loop_guard_1(ptr %a, i32 %off) { +; CHECK-LABEL: 'test_invar_dependence_not_known_beforepositive_strided_access_not_known_via_loop_guard_1' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: could not determine number of loop iterations +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %a + store i32 %l, ptr %gep + %iv.next = add i32 %iv, %off + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} + +define void @test_invar_dependence_not_known_beforepositive_strided_access_not_known_via_loop_guard_2(ptr %a, i32 %off) { +; CHECK-LABEL: 'test_invar_dependence_not_known_beforepositive_strided_access_not_known_via_loop_guard_2' +; CHECK-NEXT: loop: +; CHECK-NEXT: Report: could not determine number of loop iterations +; CHECK-NEXT: Dependences: +; CHECK-NEXT: Run-time memory checks: +; CHECK-NEXT: Grouped accesses: +; CHECK-EMPTY: +; CHECK-NEXT: Non vectorizable stores to invariant address were not found in loop. +; CHECK-NEXT: SCEV assumptions: +; CHECK-EMPTY: +; CHECK-NEXT: Expressions re-written: +; +entry: + %gep.off = getelementptr i8, ptr %a, i32 4 + br label %loop + +loop: + %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] + %gep = getelementptr i32, ptr %gep.off, i32 %iv + %l = load i32, ptr %gep + store i32 %l, ptr %a + %iv.next = add i32 %iv, %off + %ec = icmp eq i32 %iv.next, 100 + br i1 %ec, label %exit, label %loop + +exit: + ret void +} -- GitLab From c19f2c773b0e23fd623502888894add822079f63 Mon Sep 17 00:00:00 2001 From: Mircea Trofin Date: Tue, 14 May 2024 18:11:53 -0700 Subject: [PATCH 009/403] Reapply "[ctx_profile] Profile reader and writer" (#92199) This reverts commit 03c7458a3603396d2d0e1dee43399d3d1664a264. One of the problems was addressed in #92208 The other problem: needed to add `BitstreamReader` to the list of link deps of `LLVMProfileData` --- .../llvm/ProfileData/PGOCtxProfReader.h | 92 +++++++ .../llvm/ProfileData/PGOCtxProfWriter.h | 91 +++++++ llvm/lib/ProfileData/CMakeLists.txt | 3 + llvm/lib/ProfileData/PGOCtxProfReader.cpp | 173 ++++++++++++ llvm/lib/ProfileData/PGOCtxProfWriter.cpp | 49 ++++ llvm/unittests/ProfileData/CMakeLists.txt | 1 + .../PGOCtxProfReaderWriterTest.cpp | 255 ++++++++++++++++++ 7 files changed, 664 insertions(+) create mode 100644 llvm/include/llvm/ProfileData/PGOCtxProfReader.h create mode 100644 llvm/include/llvm/ProfileData/PGOCtxProfWriter.h create mode 100644 llvm/lib/ProfileData/PGOCtxProfReader.cpp create mode 100644 llvm/lib/ProfileData/PGOCtxProfWriter.cpp create mode 100644 llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h new file mode 100644 index 000000000000..a19b3f51d642 --- /dev/null +++ b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h @@ -0,0 +1,92 @@ +//===--- PGOCtxProfReader.h - Contextual profile reader ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Reader for contextual iFDO profile, which comes in bitstream format. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H +#define LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H + +#include "llvm/ADT/DenseSet.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/ProfileData/PGOCtxProfWriter.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { +/// The loaded contextual profile, suitable for mutation during IPO passes. We +/// generally expect a fraction of counters and of callsites to be populated. +/// We continue to model counters as vectors, but callsites are modeled as a map +/// of a map. The expectation is that, typically, there is a small number of +/// indirect targets (usually, 1 for direct calls); but potentially a large +/// number of callsites, and, as inlining progresses, the callsite count of a +/// caller will grow. +class PGOContextualProfile final { +public: + using CallTargetMapTy = std::map; + using CallsiteMapTy = DenseMap; + +private: + friend class PGOCtxProfileReader; + GlobalValue::GUID GUID = 0; + SmallVector Counters; + CallsiteMapTy Callsites; + + PGOContextualProfile(GlobalValue::GUID G, + SmallVectorImpl &&Counters) + : GUID(G), Counters(std::move(Counters)) {} + + Expected + getOrEmplace(uint32_t Index, GlobalValue::GUID G, + SmallVectorImpl &&Counters); + +public: + PGOContextualProfile(const PGOContextualProfile &) = delete; + PGOContextualProfile &operator=(const PGOContextualProfile &) = delete; + PGOContextualProfile(PGOContextualProfile &&) = default; + PGOContextualProfile &operator=(PGOContextualProfile &&) = default; + + GlobalValue::GUID guid() const { return GUID; } + const SmallVectorImpl &counters() const { return Counters; } + const CallsiteMapTy &callsites() const { return Callsites; } + CallsiteMapTy &callsites() { return Callsites; } + + bool hasCallsite(uint32_t I) const { + return Callsites.find(I) != Callsites.end(); + } + + const CallTargetMapTy &callsite(uint32_t I) const { + assert(hasCallsite(I) && "Callsite not found"); + return Callsites.find(I)->second; + } + void getContainedGuids(DenseSet &Guids) const; +}; + +class PGOCtxProfileReader final { + BitstreamCursor &Cursor; + Expected advance(); + Error readMetadata(); + Error wrongValue(const Twine &); + Error unsupported(const Twine &); + + Expected, PGOContextualProfile>> + readContext(bool ExpectIndex); + bool canReadContext(); + +public: + PGOCtxProfileReader(BitstreamCursor &Cursor) : Cursor(Cursor) {} + + Expected> loadContexts(); +}; +} // namespace llvm +#endif diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h new file mode 100644 index 000000000000..15578c51a495 --- /dev/null +++ b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h @@ -0,0 +1,91 @@ +//===- PGOCtxProfWriter.h - Contextual Profile Writer -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares a utility for writing a contextual profile to bitstream. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_ +#define LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_ + +#include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/ProfileData/CtxInstrContextNode.h" + +namespace llvm { +enum PGOCtxProfileRecords { Invalid = 0, Version, Guid, CalleeIndex, Counters }; + +enum PGOCtxProfileBlockIDs { + ProfileMetadataBlockID = 100, + ContextNodeBlockID = ProfileMetadataBlockID + 1 +}; + +/// Write one or more ContextNodes to the provided raw_fd_stream. +/// The caller must destroy the PGOCtxProfileWriter object before closing the +/// stream. +/// The design allows serializing a bunch of contexts embedded in some other +/// file. The overall format is: +/// +/// [... other data written to the stream...] +/// SubBlock(ProfileMetadataBlockID) +/// Version +/// SubBlock(ContextNodeBlockID) +/// [RECORDS] +/// SubBlock(ContextNodeBlockID) +/// [RECORDS] +/// [... more SubBlocks] +/// EndBlock +/// EndBlock +/// +/// The "RECORDS" are bitsream records. The IDs are in CtxProfileCodes (except) +/// for Version, which is just for metadata). All contexts will have Guid and +/// Counters, and all but the roots have CalleeIndex. The order in which the +/// records appear does not matter, but they must precede any subcontexts, +/// because that helps keep the reader code simpler. +/// +/// Subblock containment captures the context->subcontext relationship. The +/// "next()" relationship in the raw profile, between call targets of indirect +/// calls, are just modeled as peer subblocks where the callee index is the +/// same. +/// +/// Versioning: the writer may produce additional records not known by the +/// reader. The version number indicates a more structural change. +/// The current version, in particular, is set up to expect optional extensions +/// like value profiling - which would appear as additional records. For +/// example, value profiling would produce a new record with a new record ID, +/// containing the profiled values (much like the counters) +class PGOCtxProfileWriter final { + SmallVector Buff; + BitstreamWriter Writer; + + void writeCounters(const ctx_profile::ContextNode &Node); + void writeImpl(std::optional CallerIndex, + const ctx_profile::ContextNode &Node); + +public: + PGOCtxProfileWriter(raw_fd_stream &Out, + std::optional VersionOverride = std::nullopt) + : Writer(Buff, &Out, 0) { + Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, + CodeLen); + const auto Version = VersionOverride ? *VersionOverride : CurrentVersion; + Writer.EmitRecord(PGOCtxProfileRecords::Version, + SmallVector({Version})); + } + + ~PGOCtxProfileWriter() { Writer.ExitBlock(); } + + void write(const ctx_profile::ContextNode &); + + // constants used in writing which a reader may find useful. + static constexpr unsigned CodeLen = 2; + static constexpr uint32_t CurrentVersion = 1; + static constexpr unsigned VBREncodingBits = 6; +}; + +} // namespace llvm +#endif diff --git a/llvm/lib/ProfileData/CMakeLists.txt b/llvm/lib/ProfileData/CMakeLists.txt index 408f9ff01ec8..4fa1b76f0a06 100644 --- a/llvm/lib/ProfileData/CMakeLists.txt +++ b/llvm/lib/ProfileData/CMakeLists.txt @@ -7,6 +7,8 @@ add_llvm_component_library(LLVMProfileData ItaniumManglingCanonicalizer.cpp MemProf.cpp MemProfReader.cpp + PGOCtxProfReader.cpp + PGOCtxProfWriter.cpp ProfileSummaryBuilder.cpp SampleProf.cpp SampleProfReader.cpp @@ -20,6 +22,7 @@ add_llvm_component_library(LLVMProfileData intrinsics_gen LINK_COMPONENTS + BitstreamReader Core Object Support diff --git a/llvm/lib/ProfileData/PGOCtxProfReader.cpp b/llvm/lib/ProfileData/PGOCtxProfReader.cpp new file mode 100644 index 000000000000..3710f2e4b818 --- /dev/null +++ b/llvm/lib/ProfileData/PGOCtxProfReader.cpp @@ -0,0 +1,173 @@ +//===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Read a contextual profile into a datastructure suitable for maintenance +// throughout IPO +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/PGOCtxProfReader.h" +#include "llvm/Bitstream/BitCodeEnums.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/ProfileData/PGOCtxProfWriter.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +using namespace llvm; + +// FIXME(#92054) - these Error handling macros are (re-)invented in a few +// places. +#define EXPECT_OR_RET(LHS, RHS) \ + auto LHS = RHS; \ + if (!LHS) \ + return LHS.takeError(); + +#define RET_ON_ERR(EXPR) \ + if (auto Err = (EXPR)) \ + return Err; + +Expected +PGOContextualProfile::getOrEmplace(uint32_t Index, GlobalValue::GUID G, + SmallVectorImpl &&Counters) { + auto [Iter, Inserted] = Callsites[Index].insert( + {G, PGOContextualProfile(G, std::move(Counters))}); + if (!Inserted) + return make_error(instrprof_error::invalid_prof, + "Duplicate GUID for same callsite."); + return Iter->second; +} + +void PGOContextualProfile::getContainedGuids( + DenseSet &Guids) const { + Guids.insert(GUID); + for (const auto &[_, Callsite] : Callsites) + for (const auto &[_, Callee] : Callsite) + Callee.getContainedGuids(Guids); +} + +Expected PGOCtxProfileReader::advance() { + return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); +} + +Error PGOCtxProfileReader::wrongValue(const Twine &Msg) { + return make_error(instrprof_error::invalid_prof, Msg); +} + +Error PGOCtxProfileReader::unsupported(const Twine &Msg) { + return make_error(instrprof_error::unsupported_version, Msg); +} + +bool PGOCtxProfileReader::canReadContext() { + auto Blk = advance(); + if (!Blk) { + consumeError(Blk.takeError()); + return false; + } + return Blk->Kind == BitstreamEntry::SubBlock && + Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID; +} + +Expected, PGOContextualProfile>> +PGOCtxProfileReader::readContext(bool ExpectIndex) { + RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID)); + + std::optional Guid; + std::optional> Counters; + std::optional CallsiteIndex; + + SmallVector RecordValues; + + // We don't prescribe the order in which the records come in, and we are ok + // if other unsupported records appear. We seek in the current subblock until + // we get all we know. + auto GotAllWeNeed = [&]() { + return Guid.has_value() && Counters.has_value() && + (!ExpectIndex || CallsiteIndex.has_value()); + }; + while (!GotAllWeNeed()) { + RecordValues.clear(); + EXPECT_OR_RET(Entry, advance()); + if (Entry->Kind != BitstreamEntry::Record) + return wrongValue( + "Expected records before encountering more subcontexts"); + EXPECT_OR_RET(ReadRecord, + Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues)); + switch (*ReadRecord) { + case PGOCtxProfileRecords::Guid: + if (RecordValues.size() != 1) + return wrongValue("The GUID record should have exactly one value"); + Guid = RecordValues[0]; + break; + case PGOCtxProfileRecords::Counters: + Counters = std::move(RecordValues); + if (Counters->empty()) + return wrongValue("Empty counters. At least the entry counter (one " + "value) was expected"); + break; + case PGOCtxProfileRecords::CalleeIndex: + if (!ExpectIndex) + return wrongValue("The root context should not have a callee index"); + if (RecordValues.size() != 1) + return wrongValue("The callee index should have exactly one value"); + CallsiteIndex = RecordValues[0]; + break; + default: + // OK if we see records we do not understand, like records (profile + // components) introduced later. + break; + } + } + + PGOContextualProfile Ret(*Guid, std::move(*Counters)); + + while (canReadContext()) { + EXPECT_OR_RET(SC, readContext(true)); + auto &Targets = Ret.callsites()[*SC->first]; + auto [_, Inserted] = + Targets.insert({SC->second.guid(), std::move(SC->second)}); + if (!Inserted) + return wrongValue( + "Unexpected duplicate target (callee) at the same callsite."); + } + return std::make_pair(CallsiteIndex, std::move(Ret)); +} + +Error PGOCtxProfileReader::readMetadata() { + EXPECT_OR_RET(Blk, advance()); + if (Blk->Kind != BitstreamEntry::SubBlock) + return unsupported("Expected Version record"); + RET_ON_ERR( + Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID)); + EXPECT_OR_RET(MData, advance()); + if (MData->Kind != BitstreamEntry::Record) + return unsupported("Expected Version record"); + + SmallVector Ver; + EXPECT_OR_RET(Code, Cursor.readRecord(bitc::UNABBREV_RECORD, Ver)); + if (*Code != PGOCtxProfileRecords::Version) + return unsupported("Expected Version record"); + if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion) + return unsupported("Version " + Twine(*Code) + + " is higher than supported version " + + Twine(PGOCtxProfileWriter::CurrentVersion)); + return Error::success(); +} + +Expected> +PGOCtxProfileReader::loadContexts() { + std::map Ret; + RET_ON_ERR(readMetadata()); + while (canReadContext()) { + EXPECT_OR_RET(E, readContext(false)); + auto Key = E->second.guid(); + if (!Ret.insert({Key, std::move(E->second)}).second) + return wrongValue("Duplicate roots"); + } + return Ret; +} diff --git a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp new file mode 100644 index 000000000000..508179756446 --- /dev/null +++ b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp @@ -0,0 +1,49 @@ +//===- PGOCtxProfWriter.cpp - Contextual Instrumentation profile writer ---===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Write a contextual profile to bitstream. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/PGOCtxProfWriter.h" +#include "llvm/Bitstream/BitCodeEnums.h" + +using namespace llvm; +using namespace llvm::ctx_profile; + +void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) { + Writer.EmitCode(bitc::UNABBREV_RECORD); + Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits); + Writer.EmitVBR(Node.counters_size(), VBREncodingBits); + for (uint32_t I = 0U; I < Node.counters_size(); ++I) + Writer.EmitVBR64(Node.counters()[I], VBREncodingBits); +} + +// recursively write all the subcontexts. We do need to traverse depth first to +// model the context->subcontext implicitly, and since this captures call +// stacks, we don't really need to be worried about stack overflow and we can +// keep the implementation simple. +void PGOCtxProfileWriter::writeImpl(std::optional CallerIndex, + const ContextNode &Node) { + Writer.EnterSubblock(PGOCtxProfileBlockIDs::ContextNodeBlockID, CodeLen); + Writer.EmitRecord(PGOCtxProfileRecords::Guid, + SmallVector{Node.guid()}); + if (CallerIndex) + Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex, + SmallVector{*CallerIndex}); + writeCounters(Node); + for (uint32_t I = 0U; I < Node.callsites_size(); ++I) + for (const auto *Subcontext = Node.subContexts()[I]; Subcontext; + Subcontext = Subcontext->next()) + writeImpl(I, *Subcontext); + Writer.ExitBlock(); +} + +void PGOCtxProfileWriter::write(const ContextNode &RootNode) { + writeImpl(std::nullopt, RootNode); +} diff --git a/llvm/unittests/ProfileData/CMakeLists.txt b/llvm/unittests/ProfileData/CMakeLists.txt index ce3a0a45ccf1..c92642ded828 100644 --- a/llvm/unittests/ProfileData/CMakeLists.txt +++ b/llvm/unittests/ProfileData/CMakeLists.txt @@ -13,6 +13,7 @@ add_llvm_unittest(ProfileDataTests InstrProfTest.cpp ItaniumManglingCanonicalizerTest.cpp MemProfTest.cpp + PGOCtxProfReaderWriterTest.cpp SampleProfTest.cpp SymbolRemappingReaderTest.cpp ) diff --git a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp new file mode 100644 index 000000000000..d2cdbb28e2fc --- /dev/null +++ b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp @@ -0,0 +1,255 @@ +//===-------------- PGOCtxProfReadWriteTest.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/ProfileData/CtxInstrContextNode.h" +#include "llvm/ProfileData/PGOCtxProfReader.h" +#include "llvm/ProfileData/PGOCtxProfWriter.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Testing/Support/SupportHelpers.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::ctx_profile; + +class PGOCtxProfRWTest : public ::testing::Test { + std::vector> Nodes; + std::map Roots; + +public: + ContextNode *createNode(GUID Guid, uint32_t NrCounters, uint32_t NrCallsites, + ContextNode *Next = nullptr) { + auto AllocSize = ContextNode::getAllocSize(NrCounters, NrCallsites); + auto *Mem = Nodes.emplace_back(std::make_unique(AllocSize)).get(); + std::memset(Mem, 0, AllocSize); + auto *Ret = new (Mem) ContextNode(Guid, NrCounters, NrCallsites, Next); + return Ret; + } + + void SetUp() override { + // Root (guid 1) has 2 callsites, one used for an indirect call to either + // guid 2 or 4. + // guid 2 calls guid 5 + // guid 5 calls guid 2 + // there's also a second root, guid3. + auto *Root1 = createNode(1, 2, 2); + Root1->counters()[0] = 10; + Root1->counters()[1] = 11; + Roots.insert({1, Root1}); + auto *L1 = createNode(2, 1, 1); + L1->counters()[0] = 12; + Root1->subContexts()[1] = createNode(4, 3, 1, L1); + Root1->subContexts()[1]->counters()[0] = 13; + Root1->subContexts()[1]->counters()[1] = 14; + Root1->subContexts()[1]->counters()[2] = 15; + + auto *L3 = createNode(5, 6, 3); + for (auto I = 0; I < 6; ++I) + L3->counters()[I] = 16 + I; + L1->subContexts()[0] = L3; + L3->subContexts()[2] = createNode(2, 1, 1); + L3->subContexts()[2]->counters()[0] = 30; + auto *Root2 = createNode(3, 1, 0); + Root2->counters()[0] = 40; + Roots.insert({3, Root2}); + } + + const std::map &roots() const { return Roots; } +}; + +void checkSame(const ContextNode &Raw, const PGOContextualProfile &Profile) { + EXPECT_EQ(Raw.guid(), Profile.guid()); + ASSERT_EQ(Raw.counters_size(), Profile.counters().size()); + for (auto I = 0U; I < Raw.counters_size(); ++I) + EXPECT_EQ(Raw.counters()[I], Profile.counters()[I]); + + for (auto I = 0U; I < Raw.callsites_size(); ++I) { + if (Raw.subContexts()[I] == nullptr) + continue; + EXPECT_TRUE(Profile.hasCallsite(I)); + const auto &ProfileTargets = Profile.callsite(I); + + std::map Targets; + for (const auto *N = Raw.subContexts()[I]; N; N = N->next()) + EXPECT_TRUE(Targets.insert({N->guid(), N}).second); + + EXPECT_EQ(Targets.size(), ProfileTargets.size()); + for (auto It : Targets) { + auto PIt = ProfileTargets.find(It.second->guid()); + EXPECT_NE(PIt, ProfileTargets.end()); + checkSame(*It.second, PIt->second); + } + } +} + +TEST_F(PGOCtxProfRWTest, RoundTrip) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out); + for (auto &[_, R] : roots()) + Writer.write(*R); + } + } + { + ErrorOr> MB = + MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + ASSERT_TRUE(!!Expected); + auto &Ctxes = *Expected; + EXPECT_EQ(Ctxes.size(), roots().size()); + EXPECT_EQ(Ctxes.size(), 2U); + for (auto &[G, R] : roots()) + checkSame(*R, Ctxes.find(G)->second); + } +} + +TEST_F(PGOCtxProfRWTest, InvalidCounters) { + auto *R = createNode(1, 0, 1); + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out); + Writer.write(*R); + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); + } +} + +TEST_F(PGOCtxProfRWTest, Empty) { + BitstreamCursor Cursor(""); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); +} + +TEST_F(PGOCtxProfRWTest, Invalid) { + BitstreamCursor Cursor("Surely this is not valid"); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); +} + +TEST_F(PGOCtxProfRWTest, ValidButEmpty) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out); + // don't write anything - this will just produce the metadata subblock. + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_TRUE(!!Expected); + EXPECT_TRUE(Expected->empty()); + } +} + +TEST_F(PGOCtxProfRWTest, WrongVersion) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1); + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); + } +} + +TEST_F(PGOCtxProfRWTest, DuplicateRoots) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out); + Writer.write(*createNode(1, 1, 1)); + Writer.write(*createNode(1, 1, 1)); + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); + } +} + +TEST_F(PGOCtxProfRWTest, DuplicateTargets) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + auto *R = createNode(1, 1, 1); + auto *L1 = createNode(2, 1, 0); + auto *L2 = createNode(2, 1, 0, L1); + R->subContexts()[0] = L2; + PGOCtxProfileWriter Writer(Out); + Writer.write(*R); + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); + } +} -- GitLab From df5804aec48f99704ef26c740e19deaa4072fe27 Mon Sep 17 00:00:00 2001 From: LLVM GN Syncbot Date: Wed, 15 May 2024 18:08:23 +0000 Subject: [PATCH 010/403] [gn build] Port c19f2c773b0e --- llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn | 2 ++ llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn | 1 + 2 files changed, 3 insertions(+) diff --git a/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn index 9dbfe0f94c1d..c6fa142b3766 100644 --- a/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn @@ -17,6 +17,8 @@ static_library("ProfileData") { "ItaniumManglingCanonicalizer.cpp", "MemProf.cpp", "MemProfReader.cpp", + "PGOCtxProfReader.cpp", + "PGOCtxProfWriter.cpp", "ProfileSummaryBuilder.cpp", "SampleProf.cpp", "SampleProfReader.cpp", diff --git a/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn b/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn index 4919a8089209..f45542519173 100644 --- a/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn @@ -14,6 +14,7 @@ unittest("ProfileDataTests") { "InstrProfTest.cpp", "ItaniumManglingCanonicalizerTest.cpp", "MemProfTest.cpp", + "PGOCtxProfReaderWriterTest.cpp", "SampleProfTest.cpp", "SymbolRemappingReaderTest.cpp", ] -- GitLab From 468357114c64633651ebcc5ef17161990da25a78 Mon Sep 17 00:00:00 2001 From: Pete Steinfeld <47540744+psteinfeld@users.noreply.github.com> Date: Wed, 15 May 2024 11:30:30 -0700 Subject: [PATCH 011/403] =?UTF-8?q?Revert=20"[flang]=20Initial=20debug=20i?= =?UTF-8?q?nfo=20support=20for=20local=20variables.=20(#909=E2=80=A6=20(#9?= =?UTF-8?q?2302)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …05)" This reverts commit 61da6366d043792d7db280ce9edd2db62516e0e8. Update #90905 was causing many tests to fail. See comments in #90905. --- .../include/flang/Optimizer/CodeGen/CGOps.td | 34 ------- .../flang/Optimizer/CodeGen/CGPasses.td | 4 - .../include/flang/Optimizer/CodeGen/CodeGen.h | 6 +- flang/include/flang/Tools/CLOptions.inc | 11 +-- flang/lib/Optimizer/CodeGen/CGOps.cpp | 2 +- .../flang => lib}/Optimizer/CodeGen/CGOps.h | 1 - flang/lib/Optimizer/CodeGen/CodeGen.cpp | 50 +++------- flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp | 49 ++-------- .../lib/Optimizer/Transforms/AddDebugInfo.cpp | 56 +----------- .../Transforms/DebugTypeGenerator.cpp | 10 +- flang/test/Fir/declare-codegen.fir | 22 ++--- flang/test/Fir/dummy-scope-codegen.fir | 11 +-- flang/test/Transforms/debug-local-var-2.f90 | 91 ------------------- flang/test/Transforms/debug-local-var.f90 | 54 ----------- 14 files changed, 47 insertions(+), 354 deletions(-) rename flang/{include/flang => lib}/Optimizer/CodeGen/CGOps.h (94%) delete mode 100644 flang/test/Transforms/debug-local-var-2.f90 delete mode 100644 flang/test/Transforms/debug-local-var.f90 diff --git a/flang/include/flang/Optimizer/CodeGen/CGOps.td b/flang/include/flang/Optimizer/CodeGen/CGOps.td index c375edee1fa7..35e70fa2ffa3 100644 --- a/flang/include/flang/Optimizer/CodeGen/CGOps.td +++ b/flang/include/flang/Optimizer/CodeGen/CGOps.td @@ -16,8 +16,6 @@ include "mlir/IR/SymbolInterfaces.td" include "flang/Optimizer/Dialect/FIRTypes.td" -include "flang/Optimizer/Dialect/FIRAttr.td" -include "mlir/IR/BuiltinAttributes.td" def fircg_Dialect : Dialect { let name = "fircg"; @@ -204,36 +202,4 @@ def fircg_XArrayCoorOp : fircg_Op<"ext_array_coor", [AttrSizedOperandSegments]> }]; } -// Extended Declare operation. -def fircg_XDeclareOp : fircg_Op<"ext_declare", [AttrSizedOperandSegments]> { - let summary = "for internal conversion only"; - - let description = [{ - Prior to lowering to LLVM IR dialect, a DeclareOp will - be converted to an extended DeclareOp. - }]; - - let arguments = (ins - AnyRefOrBox:$memref, - Variadic:$shape, - Variadic:$shift, - Variadic:$typeparams, - Optional:$dummy_scope, - Builtin_StringAttr:$uniq_name - ); - let results = (outs AnyRefOrBox); - - let assemblyFormat = [{ - $memref (`(` $shape^ `)`)? (`origin` $shift^)? (`typeparams` $typeparams^)? - (`dummy_scope` $dummy_scope^)? - attr-dict `:` functional-type(operands, results) - }]; - - let extraClassDeclaration = [{ - // Shape is optional, but if it exists, it will be at offset 1. - unsigned shapeOffset() { return 1; } - unsigned shiftOffset() { return shapeOffset() + getShape().size(); } - }]; -} - #endif diff --git a/flang/include/flang/Optimizer/CodeGen/CGPasses.td b/flang/include/flang/Optimizer/CodeGen/CGPasses.td index 565920e55e6a..f524fb423734 100644 --- a/flang/include/flang/Optimizer/CodeGen/CGPasses.td +++ b/flang/include/flang/Optimizer/CodeGen/CGPasses.td @@ -47,10 +47,6 @@ def CodeGenRewrite : Pass<"cg-rewrite", "mlir::ModuleOp"> { let dependentDialects = [ "fir::FIROpsDialect", "fir::FIRCodeGenDialect" ]; - let options = [ - Option<"preserveDeclare", "preserve-declare", "bool", /*default=*/"false", - "Preserve DeclareOp during pre codegen re-write."> - ]; let statistics = [ Statistic<"numDCE", "num-dce'd", "Number of operations eliminated"> ]; diff --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h index 4d2b191b46d0..26097dabf56c 100644 --- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h +++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h @@ -30,8 +30,7 @@ struct NameUniquer; /// Prerequiste pass for code gen. Perform intermediate rewrites to perform /// the code gen (to LLVM-IR dialect) conversion. -std::unique_ptr createFirCodeGenRewritePass( - CodeGenRewriteOptions Options = CodeGenRewriteOptions{}); +std::unique_ptr createFirCodeGenRewritePass(); /// FirTargetRewritePass options. struct TargetRewriteOptions { @@ -89,8 +88,7 @@ void populateFIRToLLVMConversionPatterns(fir::LLVMTypeConverter &converter, fir::FIRToLLVMPassOptions &options); /// Populate the pattern set with the PreCGRewrite patterns. -void populatePreCGRewritePatterns(mlir::RewritePatternSet &patterns, - bool preserveDeclare); +void populatePreCGRewritePatterns(mlir::RewritePatternSet &patterns); // declarative passes #define GEN_PASS_REGISTRATION diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc index 761315e0abc8..cc3431d5b71d 100644 --- a/flang/include/flang/Tools/CLOptions.inc +++ b/flang/include/flang/Tools/CLOptions.inc @@ -169,11 +169,9 @@ inline void addMemoryAllocationOpt(mlir::PassManager &pm) { } #if !defined(FLANG_EXCLUDE_CODEGEN) -inline void addCodeGenRewritePass(mlir::PassManager &pm, bool preserveDeclare) { - fir::CodeGenRewriteOptions options; - options.preserveDeclare = preserveDeclare; - addPassConditionally(pm, disableCodeGenRewrite, - [&]() { return fir::createFirCodeGenRewritePass(options); }); +inline void addCodeGenRewritePass(mlir::PassManager &pm) { + addPassConditionally( + pm, disableCodeGenRewrite, fir::createFirCodeGenRewritePass); } inline void addTargetRewritePass(mlir::PassManager &pm) { @@ -355,8 +353,7 @@ inline void createDefaultFIRCodeGenPassPipeline(mlir::PassManager &pm, MLIRToLLVMPassPipelineConfig config, llvm::StringRef inputFilename = {}) { fir::addBoxedProcedurePass(pm); addNestedPassToAllTopLevelOperations(pm, fir::createAbstractResultOpt); - fir::addCodeGenRewritePass( - pm, (config.DebugInfo != llvm::codegenoptions::NoDebugInfo)); + fir::addCodeGenRewritePass(pm); fir::addTargetRewritePass(pm); fir::addExternalNameConversionPass(pm, config.Underscoring); fir::createDebugPasses(pm, config.DebugInfo, config.OptLevel, inputFilename); diff --git a/flang/lib/Optimizer/CodeGen/CGOps.cpp b/flang/lib/Optimizer/CodeGen/CGOps.cpp index 6b8ba7452555..44d07d26dd2b 100644 --- a/flang/lib/Optimizer/CodeGen/CGOps.cpp +++ b/flang/lib/Optimizer/CodeGen/CGOps.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#include "flang/Optimizer/CodeGen/CGOps.h" +#include "CGOps.h" #include "flang/Optimizer/Dialect/FIRDialect.h" #include "flang/Optimizer/Dialect/FIROps.h" #include "flang/Optimizer/Dialect/FIRType.h" diff --git a/flang/include/flang/Optimizer/CodeGen/CGOps.h b/flang/lib/Optimizer/CodeGen/CGOps.h similarity index 94% rename from flang/include/flang/Optimizer/CodeGen/CGOps.h rename to flang/lib/Optimizer/CodeGen/CGOps.h index df909d9ee81c..b5a6d5bb9a9e 100644 --- a/flang/include/flang/Optimizer/CodeGen/CGOps.h +++ b/flang/lib/Optimizer/CodeGen/CGOps.h @@ -13,7 +13,6 @@ #ifndef OPTIMIZER_CODEGEN_CGOPS_H #define OPTIMIZER_CODEGEN_CGOPS_H -#include "flang/Optimizer/Dialect/FIRAttr.h" #include "flang/Optimizer/Dialect/FIRType.h" #include "mlir/Dialect/Func/IR/FuncOps.h" diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp index 72172f63888e..21154902d23f 100644 --- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp +++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp @@ -12,7 +12,7 @@ #include "flang/Optimizer/CodeGen/CodeGen.h" -#include "flang/Optimizer/CodeGen/CGOps.h" +#include "CGOps.h" #include "flang/Optimizer/CodeGen/CodeGenOpenMP.h" #include "flang/Optimizer/CodeGen/FIROpPatterns.h" #include "flang/Optimizer/CodeGen/TypeConverter.h" @@ -170,28 +170,6 @@ genAllocationScaleSize(OP op, mlir::Type ity, return nullptr; } -namespace { -struct DeclareOpConversion : public fir::FIROpConversion { -public: - using FIROpConversion::FIROpConversion; - mlir::LogicalResult - matchAndRewrite(fir::cg::XDeclareOp declareOp, OpAdaptor adaptor, - mlir::ConversionPatternRewriter &rewriter) const override { - auto memRef = adaptor.getOperands()[0]; - if (auto fusedLoc = mlir::dyn_cast(declareOp.getLoc())) { - if (auto varAttr = - mlir::dyn_cast_or_null( - fusedLoc.getMetadata())) { - rewriter.create(memRef.getLoc(), memRef, - varAttr, nullptr); - } - } - rewriter.replaceOp(declareOp, memRef); - return mlir::success(); - } -}; -} // namespace - namespace { /// convert to LLVM IR dialect `alloca` struct AllocaOpConversion : public fir::FIROpConversion { @@ -3736,19 +3714,19 @@ void fir::populateFIRToLLVMConversionPatterns( BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, - CoordinateOpConversion, DTEntryOpConversion, DeclareOpConversion, - DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion, - EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, - FirEndOpConversion, FreeMemOpConversion, GlobalLenOpConversion, - GlobalOpConversion, HasValueOpConversion, InsertOnRangeOpConversion, - InsertValueOpConversion, IsPresentOpConversion, LenParamIndexOpConversion, - LoadOpConversion, MulcOpConversion, NegcOpConversion, - NoReassocOpConversion, SelectCaseOpConversion, SelectOpConversion, - SelectRankOpConversion, SelectTypeOpConversion, ShapeOpConversion, - ShapeShiftOpConversion, ShiftOpConversion, SliceOpConversion, - StoreOpConversion, StringLitOpConversion, SubcOpConversion, - TypeDescOpConversion, TypeInfoOpConversion, UnboxCharOpConversion, - UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion, + CoordinateOpConversion, DTEntryOpConversion, DivcOpConversion, + EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion, + ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion, + FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion, + HasValueOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion, + IsPresentOpConversion, LenParamIndexOpConversion, LoadOpConversion, + MulcOpConversion, NegcOpConversion, NoReassocOpConversion, + SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, + SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion, + ShiftOpConversion, SliceOpConversion, StoreOpConversion, + StringLitOpConversion, SubcOpConversion, TypeDescOpConversion, + TypeInfoOpConversion, UnboxCharOpConversion, UnboxProcOpConversion, + UndefOpConversion, UnreachableOpConversion, UnrealizedConversionCastOpConversion, XArrayCoorOpConversion, XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(converter, options); diff --git a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp index c54a7457db76..5bd3ec8d1845 100644 --- a/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp +++ b/flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp @@ -12,8 +12,8 @@ #include "flang/Optimizer/CodeGen/CodeGen.h" +#include "CGOps.h" #include "flang/Optimizer/Builder/Todo.h" // remove when TODO's are done -#include "flang/Optimizer/CodeGen/CGOps.h" #include "flang/Optimizer/Dialect/FIRDialect.h" #include "flang/Optimizer/Dialect/FIROps.h" #include "flang/Optimizer/Dialect/FIRType.h" @@ -270,43 +270,13 @@ public: }; class DeclareOpConversion : public mlir::OpRewritePattern { - bool preserveDeclare; - public: using OpRewritePattern::OpRewritePattern; - DeclareOpConversion(mlir::MLIRContext *ctx, bool preserveDecl) - : OpRewritePattern(ctx), preserveDeclare(preserveDecl) {} mlir::LogicalResult matchAndRewrite(fir::DeclareOp declareOp, mlir::PatternRewriter &rewriter) const override { - if (!preserveDeclare) { - rewriter.replaceOp(declareOp, declareOp.getMemref()); - return mlir::success(); - } - auto loc = declareOp.getLoc(); - llvm::SmallVector shapeOpers; - llvm::SmallVector shiftOpers; - if (auto shapeVal = declareOp.getShape()) { - if (auto shapeOp = mlir::dyn_cast(shapeVal.getDefiningOp())) - populateShape(shapeOpers, shapeOp); - else if (auto shiftOp = - mlir::dyn_cast(shapeVal.getDefiningOp())) - populateShapeAndShift(shapeOpers, shiftOpers, shiftOp); - else if (auto shiftOp = - mlir::dyn_cast(shapeVal.getDefiningOp())) - populateShift(shiftOpers, shiftOp); - else - return mlir::failure(); - } - // FIXME: Add FortranAttrs and CudaAttrs - auto xDeclOp = rewriter.create( - loc, declareOp.getType(), declareOp.getMemref(), shapeOpers, shiftOpers, - declareOp.getTypeparams(), declareOp.getDummyScope(), - declareOp.getUniqName()); - LLVM_DEBUG(llvm::dbgs() - << "rewriting " << declareOp << " to " << xDeclOp << '\n'); - rewriter.replaceOp(declareOp, xDeclOp.getOperation()->getResults()); + rewriter.replaceOp(declareOp, declareOp.getMemref()); return mlir::success(); } }; @@ -327,7 +297,6 @@ public: class CodeGenRewrite : public fir::impl::CodeGenRewriteBase { public: - CodeGenRewrite(fir::CodeGenRewriteOptions opts) : Base(opts) {} void runOnOperation() override final { mlir::ModuleOp mod = getOperation(); @@ -345,7 +314,7 @@ public: mlir::cast(embox.getType()).getEleTy())); }); mlir::RewritePatternSet patterns(&context); - fir::populatePreCGRewritePatterns(patterns, preserveDeclare); + fir::populatePreCGRewritePatterns(patterns); if (mlir::failed( mlir::applyPartialConversion(mod, target, std::move(patterns)))) { mlir::emitError(mlir::UnknownLoc::get(&context), @@ -361,14 +330,12 @@ public: } // namespace -std::unique_ptr -fir::createFirCodeGenRewritePass(fir::CodeGenRewriteOptions Options) { - return std::make_unique(Options); +std::unique_ptr fir::createFirCodeGenRewritePass() { + return std::make_unique(); } -void fir::populatePreCGRewritePatterns(mlir::RewritePatternSet &patterns, - bool preserveDeclare) { +void fir::populatePreCGRewritePatterns(mlir::RewritePatternSet &patterns) { patterns.insert(patterns.getContext()); - patterns.add(patterns.getContext(), preserveDeclare); + DeclareOpConversion, DummyScopeOpConversion>( + patterns.getContext()); } diff --git a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp index cfad366cb5cb..908c8fc96f63 100644 --- a/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp +++ b/flang/lib/Optimizer/Transforms/AddDebugInfo.cpp @@ -15,7 +15,6 @@ #include "flang/Common/Version.h" #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Builder/Todo.h" -#include "flang/Optimizer/CodeGen/CGOps.h" #include "flang/Optimizer/Dialect/FIRDialect.h" #include "flang/Optimizer/Dialect/FIROps.h" #include "flang/Optimizer/Dialect/FIRType.h" @@ -46,59 +45,13 @@ namespace fir { namespace { class AddDebugInfoPass : public fir::impl::AddDebugInfoBase { - void handleDeclareOp(fir::cg::XDeclareOp declOp, - mlir::LLVM::DIFileAttr fileAttr, - mlir::LLVM::DIScopeAttr scopeAttr, - fir::DebugTypeGenerator &typeGen); - public: AddDebugInfoPass(fir::AddDebugInfoOptions options) : Base(options) {} void runOnOperation() override; }; -static uint32_t getLineFromLoc(mlir::Location loc) { - uint32_t line = 1; - if (auto fileLoc = mlir::dyn_cast(loc)) - line = fileLoc.getLine(); - return line; -} - } // namespace -void AddDebugInfoPass::handleDeclareOp(fir::cg::XDeclareOp declOp, - mlir::LLVM::DIFileAttr fileAttr, - mlir::LLVM::DIScopeAttr scopeAttr, - fir::DebugTypeGenerator &typeGen) { - mlir::MLIRContext *context = &getContext(); - mlir::OpBuilder builder(context); - auto result = fir::NameUniquer::deconstruct(declOp.getUniqName()); - - if (result.first != fir::NameUniquer::NameKind::VARIABLE) - return; - - // Only accept local variables. - if (result.second.procs.empty()) - return; - - // FIXME: There may be cases where an argument is processed a bit before - // DeclareOp is generated. In that case, DeclareOp may point to an - // intermediate op and not to BlockArgument. We need to find those cases and - // walk the chain to get to the actual argument. - - unsigned argNo = 0; - if (auto Arg = llvm::dyn_cast(declOp.getMemref())) - argNo = Arg.getArgNumber() + 1; - - auto tyAttr = typeGen.convertType(fir::unwrapRefType(declOp.getType()), - fileAttr, scopeAttr, declOp.getLoc()); - - auto localVarAttr = mlir::LLVM::DILocalVariableAttr::get( - context, scopeAttr, mlir::StringAttr::get(context, result.second.name), - fileAttr, getLineFromLoc(declOp.getLoc()), argNo, /* alignInBits*/ 0, - tyAttr); - declOp->setLoc(builder.getFusedLoc({declOp->getLoc()}, localVarAttr)); -} - void AddDebugInfoPass::runOnOperation() { mlir::ModuleOp module = getOperation(); mlir::MLIRContext *context = &getContext(); @@ -191,15 +144,14 @@ void AddDebugInfoPass::runOnOperation() { subprogramFlags = subprogramFlags | mlir::LLVM::DISubprogramFlags::Definition; } - unsigned line = getLineFromLoc(l); + unsigned line = 1; + if (auto funcLoc = mlir::dyn_cast(l)) + line = funcLoc.getLine(); + auto spAttr = mlir::LLVM::DISubprogramAttr::get( context, id, compilationUnit, fileAttr, funcName, fullName, funcFileAttr, line, line, subprogramFlags, subTypeAttr); funcOp->setLoc(builder.getFusedLoc({funcOp->getLoc()}, spAttr)); - - funcOp.walk([&](fir::cg::XDeclareOp declOp) { - handleDeclareOp(declOp, fileAttr, spAttr, typeGen); - }); }); } diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp index 64c6547e06e0..e5b4050dfb24 100644 --- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp +++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp @@ -24,6 +24,11 @@ DebugTypeGenerator::DebugTypeGenerator(mlir::ModuleOp m) LLVM_DEBUG(llvm::dbgs() << "DITypeAttr generator\n"); } +static mlir::LLVM::DITypeAttr genPlaceholderType(mlir::MLIRContext *context) { + return mlir::LLVM::DIBasicTypeAttr::get( + context, llvm::dwarf::DW_TAG_base_type, "void", 32, 1); +} + static mlir::LLVM::DITypeAttr genBasicType(mlir::MLIRContext *context, mlir::StringAttr name, unsigned bitSize, @@ -32,11 +37,6 @@ static mlir::LLVM::DITypeAttr genBasicType(mlir::MLIRContext *context, context, llvm::dwarf::DW_TAG_base_type, name, bitSize, decoding); } -static mlir::LLVM::DITypeAttr genPlaceholderType(mlir::MLIRContext *context) { - return genBasicType(context, mlir::StringAttr::get(context, "integer"), 32, - llvm::dwarf::DW_ATE_signed); -} - mlir::LLVM::DITypeAttr DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr, mlir::LLVM::DIScopeAttr scope, diff --git a/flang/test/Fir/declare-codegen.fir b/flang/test/Fir/declare-codegen.fir index c5879facb157..9d68d3b2f9d4 100644 --- a/flang/test/Fir/declare-codegen.fir +++ b/flang/test/Fir/declare-codegen.fir @@ -1,7 +1,5 @@ // Test rewrite of fir.declare. The result is replaced by the memref operand. -// RUN: fir-opt --cg-rewrite="preserve-declare=true" %s -o - | FileCheck %s --check-prefixes DECL -// RUN: fir-opt --cg-rewrite="preserve-declare=false" %s -o - | FileCheck %s --check-prefixes NODECL -// RUN: fir-opt --cg-rewrite %s -o - | FileCheck %s --check-prefixes NODECL +// RUN: fir-opt --cg-rewrite %s -o - | FileCheck %s func.func @test(%arg0: !fir.ref>) { @@ -17,14 +15,9 @@ func.func @test(%arg0: !fir.ref>) { func.func private @bar(%arg0: !fir.ref>) -// NODECL-LABEL: func.func @test( -// NODECL-SAME: %[[arg0:.*]]: !fir.ref>) { -// NODECL-NEXT: fir.call @bar(%[[arg0]]) : (!fir.ref>) -> () - -// DECL-LABEL: func.func @test( -// DECL-SAME: %[[arg0:.*]]: !fir.ref>) { -// DECL: fircg.ext_declare - +// CHECK-LABEL: func.func @test( +// CHECK-SAME: %[[arg0:.*]]: !fir.ref>) { +// CHECK-NEXT: fir.call @bar(%[[arg0]]) : (!fir.ref>) -> () func.func @useless_shape_with_duplicate_extent_operand(%arg0: !fir.ref>) { %c3 = arith.constant 3 : index @@ -33,8 +26,5 @@ func.func @useless_shape_with_duplicate_extent_operand(%arg0: !fir.ref) { %scope = fir.dummy_scope : !fir.dscope %0 = fir.declare %arg0 dummy_scope %scope {uniq_name = "x"} : (!fir.ref, !fir.dscope) -> !fir.ref return } -// DECL-LABEL: func.func @dummy_scope( -// DECL: fircg.ext_declare - -// NODECL-LABEL: func.func @dummy_scope( -// NODECL-NEXT: return \ No newline at end of file +// CHECK-LABEL: func.func @dummy_scope( +// CHECK-NEXT: return diff --git a/flang/test/Transforms/debug-local-var-2.f90 b/flang/test/Transforms/debug-local-var-2.f90 deleted file mode 100644 index 15b9b148492e..000000000000 --- a/flang/test/Transforms/debug-local-var-2.f90 +++ /dev/null @@ -1,91 +0,0 @@ -! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s - -! This tests checks the debug information for local variables in llvm IR. - -! CHECK-LABEL: define void @_QQmain -! CHECK-DAG: %[[AL11:.*]] = alloca i32 -! CHECK-DAG: %[[AL12:.*]] = alloca i64 -! CHECK-DAG: %[[AL13:.*]] = alloca i8 -! CHECK-DAG: %[[AL14:.*]] = alloca i32 -! CHECK-DAG: %[[AL15:.*]] = alloca float -! CHECK-DAG: %[[AL16:.*]] = alloca double -! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL11]], metadata ![[I4:.*]], metadata !DIExpression()) -! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL12]], metadata ![[I8:.*]], metadata !DIExpression()) -! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL13]], metadata ![[L1:.*]], metadata !DIExpression()) -! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL14]], metadata ![[L4:.*]], metadata !DIExpression()) -! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL15]], metadata ![[R4:.*]], metadata !DIExpression()) -! CHECK-DAG: call void @llvm.dbg.declare(metadata ptr %[[AL16]], metadata ![[R8:.*]], metadata !DIExpression()) -! CHECK-LABEL: } - -! CHECK-LABEL: define {{.*}}i64 @_QFPfn1 -! CHECK-SAME: (ptr %[[ARG1:.*]], ptr %[[ARG2:.*]], ptr %[[ARG3:.*]]) -! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG1]], metadata ![[A1:.*]], metadata !DIExpression()) -! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG2]], metadata ![[B1:.*]], metadata !DIExpression()) -! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[ARG3]], metadata ![[C1:.*]], metadata !DIExpression()) -! CHECK-DAG: %[[AL2:.*]] = alloca i64 -! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[AL2]], metadata ![[RES1:.*]], metadata !DIExpression()) -! CHECK-LABEL: } - -! CHECK-LABEL: define {{.*}}i32 @_QFPfn2 -! CHECK-SAME: (ptr %[[FN2ARG1:.*]], ptr %[[FN2ARG2:.*]], ptr %[[FN2ARG3:.*]]) -! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG1]], metadata ![[A2:.*]], metadata !DIExpression()) -! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG2]], metadata ![[B2:.*]], metadata !DIExpression()) -! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[FN2ARG3]], metadata ![[C2:.*]], metadata !DIExpression()) -! CHECK-DAG: %[[AL3:.*]] = alloca i32 -! CHECK-DAG: tail call void @llvm.dbg.declare(metadata ptr %[[AL3]], metadata ![[RES2:.*]], metadata !DIExpression()) -! CHECK-LABEL: } - -program mn -! CHECK-DAG: ![[MAIN:.*]] = distinct !DISubprogram(name: "_QQmain", {{.*}}) - -! CHECK-DAG: ![[TYI32:.*]] = !DIBasicType(name: "integer", size: 32, encoding: DW_ATE_signed) -! CHECK-DAG: ![[TYI64:.*]] = !DIBasicType(name: "integer", size: 64, encoding: DW_ATE_signed) -! CHECK-DAG: ![[TYL8:.*]] = !DIBasicType(name: "logical", size: 8, encoding: DW_ATE_boolean) -! CHECK-DAG: ![[TYL32:.*]] = !DIBasicType(name: "logical", size: 32, encoding: DW_ATE_boolean) -! CHECK-DAG: ![[TYR32:.*]] = !DIBasicType(name: "real", size: 32, encoding: DW_ATE_float) -! CHECK-DAG: ![[TYR64:.*]] = !DIBasicType(name: "real", size: 64, encoding: DW_ATE_float) - -! CHECK-DAG: ![[I4]] = !DILocalVariable(name: "i4", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYI32]]) -! CHECK-DAG: ![[I8]] = !DILocalVariable(name: "i8", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYI64]]) -! CHECK-DAG: ![[R4]] = !DILocalVariable(name: "r4", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYR32]]) -! CHECK-DAG: ![[R8]] = !DILocalVariable(name: "r8", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYR64]]) -! CHECK-DAG: ![[L1]] = !DILocalVariable(name: "l1", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYL8]]) -! CHECK-DAG: ![[L4]] = !DILocalVariable(name: "l4", scope: ![[MAIN]], file: !{{.*}}, line: [[@LINE+6]], type: ![[TYL32]]) - integer(kind=4) :: i4 - integer(kind=8) :: i8 - real(kind=4) :: r4 - real(kind=8) :: r8 - logical(kind=1) :: l1 - logical(kind=4) :: l4 - - i8 = fn1(i4, r8, l1) - i4 = fn2(i8, r4, l4) -contains -! CHECK-DAG: ![[FN1:.*]] = distinct !DISubprogram(name: "fn1", {{.*}}) -! CHECK-DAG: ![[A1]] = !DILocalVariable(name: "a1", arg: 1, scope: ![[FN1]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYI32]]) -! CHECK-DAG: ![[B1]] = !DILocalVariable(name: "b1", arg: 2, scope: ![[FN1]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYR64]]) -! CHECK-DAG: ![[C1]] = !DILocalVariable(name: "c1", arg: 3, scope: ![[FN1]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYL8]]) -! CHECK-DAG: ![[RES1]] = !DILocalVariable(name: "res1", scope: ![[FN1]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYI64]]) - function fn1(a1, b1, c1) result (res1) - integer(kind=4), intent(in) :: a1 - real(kind=8), intent(in) :: b1 - logical(kind=1), intent(in) :: c1 - integer(kind=8) :: res1 - - res1 = a1 + b1 - end function - -! CHECK-DAG: ![[FN2:.*]] = distinct !DISubprogram(name: "fn2", {{.*}}) -! CHECK-DAG: ![[A2]] = !DILocalVariable(name: "a2", arg: 1, scope: ![[FN2]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYI64]]) -! CHECK-DAG: ![[B2]] = !DILocalVariable(name: "b2", arg: 2, scope: ![[FN2]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYR32]]) -! CHECK-DAG: ![[C2]] = !DILocalVariable(name: "c2", arg: 3, scope: ![[FN2]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYL32]]) -! CHECK-DAG: ![[RES2]] = !DILocalVariable(name: "res2", scope: ![[FN2]], file: !{{.*}}, line: [[@LINE+5]], type: ![[TYI32]]) - function fn2(a2, b2, c2) result (res2) - integer(kind=8), intent(in) :: a2 - real(kind=4), intent(in) :: b2 - logical(kind=4), intent(in) :: c2 - integer(kind=4) :: res2 - - res2 = a2 + b2 - end function -end program diff --git a/flang/test/Transforms/debug-local-var.f90 b/flang/test/Transforms/debug-local-var.f90 deleted file mode 100644 index 96dc111ad308..000000000000 --- a/flang/test/Transforms/debug-local-var.f90 +++ /dev/null @@ -1,54 +0,0 @@ -! RUN: %flang_fc1 -emit-fir -debug-info-kind=standalone -mmlir --mlir-print-debuginfo %s -o - | \ -! RUN: fir-opt --cg-rewrite="preserve-declare=true" --mlir-print-debuginfo | fir-opt --add-debug-info --mlir-print-debuginfo | FileCheck %s - -! CHECK-DAG: #[[INT8:.*]] = #llvm.di_basic_type -! CHECK-DAG: #[[INT4:.*]] = #llvm.di_basic_type -! CHECK-DAG: #[[REAL8:.*]] = #llvm.di_basic_type -! CHECK-DAG: #[[LOG1:.*]] = #llvm.di_basic_type -! CHECK-DAG: #[[REAL4:.*]] = #llvm.di_basic_type -! CHECK-DAG: #[[LOG4:.*]] = #llvm.di_basic_type -! CHECK-DAG: #[[MAIN:.*]] = #llvm.di_subprogram<{{.*}}name = "_QQmain"{{.*}}> -! CHECK-DAG: #[[FN1:.*]] = #llvm.di_subprogram<{{.*}}name = "fn1"{{.*}}> -! CHECK-DAG: #[[FN2:.*]] = #llvm.di_subprogram<{{.*}}name = "fn2"{{.*}}> - -program mn -! CHECK-DAG: #[[I4:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[I8:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[R4:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[R8:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[L1:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[L4:.*]] = #llvm.di_local_variable - integer(kind=4) :: i4 - integer(kind=8) :: i8 - real(kind=4) :: r4 - real(kind=8) :: r8 - logical(kind=1) :: l1 - logical(kind=4) :: l4 - i8 = fn1(i4, r8, l1) - i4 = fn2(i8, r4, l4) -contains - function fn1(a1, b1, c1) result (res1) -! CHECK-DAG: #[[A1:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[B1:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[C1:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[RES1:.*]] = #llvm.di_local_variable - integer(kind=4), intent(in) :: a1 - real(kind=8), intent(in) :: b1 - logical(kind=1), intent(in) :: c1 - integer(kind=8) :: res1 - res1 = a1 + b1 - end function - - function fn2(a2, b2, c2) result (res2) - implicit none -! CHECK-DAG: #[[A2:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[B2:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[C2:.*]] = #llvm.di_local_variable -! CHECK-DAG: #[[RES2:.*]] = #llvm.di_local_variable - integer(kind=8), intent(in) :: a2 - real(kind=4), intent(in) :: b2 - logical(kind=4), intent(in) :: c2 - integer(kind=4) :: res2 - res2 = a2 + b2 - end function -end program -- GitLab From 411bf385ba27f15145c635c7d8ff2701fe8de5b9 Mon Sep 17 00:00:00 2001 From: Walter Erquinigo Date: Wed, 15 May 2024 20:44:12 +0200 Subject: [PATCH 012/403] [lldb-dap] Include npm install in the extension installation steps (#92028) Otherwise the build step fails due to missing dependencies. --- lldb/tools/lldb-dap/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/tools/lldb-dap/README.md b/lldb/tools/lldb-dap/README.md index 274b1519208a..16ce4672be71 100644 --- a/lldb/tools/lldb-dap/README.md +++ b/lldb/tools/lldb-dap/README.md @@ -46,6 +46,7 @@ Installing the plug-in is very straightforward and involves just a few steps. ```bash cd /path/to/lldb/tools/lldb-dap +npm install npm run package # This also compiles the extension. npm run vscode-install ``` @@ -69,6 +70,7 @@ no effect. ```bash # Bump version in package.json cd /path/to/lldb/tools/lldb-dap +npm install npm run package npm run vscode-install ``` -- GitLab From 2c54bf497f7d7aecd24f4b849ee08e37a3519611 Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Wed, 15 May 2024 11:44:26 -0700 Subject: [PATCH 013/403] Revert "Reapply "[ctx_profile] Profile reader and writer" (#92199)" This reverts commit c19f2c773b0e23fd623502888894add822079f63. Broke the gcc-7 bot. --- .../llvm/ProfileData/PGOCtxProfReader.h | 92 ------- .../llvm/ProfileData/PGOCtxProfWriter.h | 91 ------- llvm/lib/ProfileData/CMakeLists.txt | 3 - llvm/lib/ProfileData/PGOCtxProfReader.cpp | 173 ------------ llvm/lib/ProfileData/PGOCtxProfWriter.cpp | 49 ---- llvm/unittests/ProfileData/CMakeLists.txt | 1 - .../PGOCtxProfReaderWriterTest.cpp | 255 ------------------ 7 files changed, 664 deletions(-) delete mode 100644 llvm/include/llvm/ProfileData/PGOCtxProfReader.h delete mode 100644 llvm/include/llvm/ProfileData/PGOCtxProfWriter.h delete mode 100644 llvm/lib/ProfileData/PGOCtxProfReader.cpp delete mode 100644 llvm/lib/ProfileData/PGOCtxProfWriter.cpp delete mode 100644 llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h deleted file mode 100644 index a19b3f51d642..000000000000 --- a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h +++ /dev/null @@ -1,92 +0,0 @@ -//===--- PGOCtxProfReader.h - Contextual profile reader ---------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -/// -/// \file -/// -/// Reader for contextual iFDO profile, which comes in bitstream format. -/// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H -#define LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H - -#include "llvm/ADT/DenseSet.h" -#include "llvm/Bitstream/BitstreamReader.h" -#include "llvm/IR/GlobalValue.h" -#include "llvm/ProfileData/PGOCtxProfWriter.h" -#include "llvm/Support/Error.h" -#include -#include - -namespace llvm { -/// The loaded contextual profile, suitable for mutation during IPO passes. We -/// generally expect a fraction of counters and of callsites to be populated. -/// We continue to model counters as vectors, but callsites are modeled as a map -/// of a map. The expectation is that, typically, there is a small number of -/// indirect targets (usually, 1 for direct calls); but potentially a large -/// number of callsites, and, as inlining progresses, the callsite count of a -/// caller will grow. -class PGOContextualProfile final { -public: - using CallTargetMapTy = std::map; - using CallsiteMapTy = DenseMap; - -private: - friend class PGOCtxProfileReader; - GlobalValue::GUID GUID = 0; - SmallVector Counters; - CallsiteMapTy Callsites; - - PGOContextualProfile(GlobalValue::GUID G, - SmallVectorImpl &&Counters) - : GUID(G), Counters(std::move(Counters)) {} - - Expected - getOrEmplace(uint32_t Index, GlobalValue::GUID G, - SmallVectorImpl &&Counters); - -public: - PGOContextualProfile(const PGOContextualProfile &) = delete; - PGOContextualProfile &operator=(const PGOContextualProfile &) = delete; - PGOContextualProfile(PGOContextualProfile &&) = default; - PGOContextualProfile &operator=(PGOContextualProfile &&) = default; - - GlobalValue::GUID guid() const { return GUID; } - const SmallVectorImpl &counters() const { return Counters; } - const CallsiteMapTy &callsites() const { return Callsites; } - CallsiteMapTy &callsites() { return Callsites; } - - bool hasCallsite(uint32_t I) const { - return Callsites.find(I) != Callsites.end(); - } - - const CallTargetMapTy &callsite(uint32_t I) const { - assert(hasCallsite(I) && "Callsite not found"); - return Callsites.find(I)->second; - } - void getContainedGuids(DenseSet &Guids) const; -}; - -class PGOCtxProfileReader final { - BitstreamCursor &Cursor; - Expected advance(); - Error readMetadata(); - Error wrongValue(const Twine &); - Error unsupported(const Twine &); - - Expected, PGOContextualProfile>> - readContext(bool ExpectIndex); - bool canReadContext(); - -public: - PGOCtxProfileReader(BitstreamCursor &Cursor) : Cursor(Cursor) {} - - Expected> loadContexts(); -}; -} // namespace llvm -#endif diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h deleted file mode 100644 index 15578c51a495..000000000000 --- a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h +++ /dev/null @@ -1,91 +0,0 @@ -//===- PGOCtxProfWriter.h - Contextual Profile Writer -----------*- C++ -*-===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file declares a utility for writing a contextual profile to bitstream. -// -//===----------------------------------------------------------------------===// - -#ifndef LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_ -#define LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_ - -#include "llvm/Bitstream/BitstreamWriter.h" -#include "llvm/ProfileData/CtxInstrContextNode.h" - -namespace llvm { -enum PGOCtxProfileRecords { Invalid = 0, Version, Guid, CalleeIndex, Counters }; - -enum PGOCtxProfileBlockIDs { - ProfileMetadataBlockID = 100, - ContextNodeBlockID = ProfileMetadataBlockID + 1 -}; - -/// Write one or more ContextNodes to the provided raw_fd_stream. -/// The caller must destroy the PGOCtxProfileWriter object before closing the -/// stream. -/// The design allows serializing a bunch of contexts embedded in some other -/// file. The overall format is: -/// -/// [... other data written to the stream...] -/// SubBlock(ProfileMetadataBlockID) -/// Version -/// SubBlock(ContextNodeBlockID) -/// [RECORDS] -/// SubBlock(ContextNodeBlockID) -/// [RECORDS] -/// [... more SubBlocks] -/// EndBlock -/// EndBlock -/// -/// The "RECORDS" are bitsream records. The IDs are in CtxProfileCodes (except) -/// for Version, which is just for metadata). All contexts will have Guid and -/// Counters, and all but the roots have CalleeIndex. The order in which the -/// records appear does not matter, but they must precede any subcontexts, -/// because that helps keep the reader code simpler. -/// -/// Subblock containment captures the context->subcontext relationship. The -/// "next()" relationship in the raw profile, between call targets of indirect -/// calls, are just modeled as peer subblocks where the callee index is the -/// same. -/// -/// Versioning: the writer may produce additional records not known by the -/// reader. The version number indicates a more structural change. -/// The current version, in particular, is set up to expect optional extensions -/// like value profiling - which would appear as additional records. For -/// example, value profiling would produce a new record with a new record ID, -/// containing the profiled values (much like the counters) -class PGOCtxProfileWriter final { - SmallVector Buff; - BitstreamWriter Writer; - - void writeCounters(const ctx_profile::ContextNode &Node); - void writeImpl(std::optional CallerIndex, - const ctx_profile::ContextNode &Node); - -public: - PGOCtxProfileWriter(raw_fd_stream &Out, - std::optional VersionOverride = std::nullopt) - : Writer(Buff, &Out, 0) { - Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, - CodeLen); - const auto Version = VersionOverride ? *VersionOverride : CurrentVersion; - Writer.EmitRecord(PGOCtxProfileRecords::Version, - SmallVector({Version})); - } - - ~PGOCtxProfileWriter() { Writer.ExitBlock(); } - - void write(const ctx_profile::ContextNode &); - - // constants used in writing which a reader may find useful. - static constexpr unsigned CodeLen = 2; - static constexpr uint32_t CurrentVersion = 1; - static constexpr unsigned VBREncodingBits = 6; -}; - -} // namespace llvm -#endif diff --git a/llvm/lib/ProfileData/CMakeLists.txt b/llvm/lib/ProfileData/CMakeLists.txt index 4fa1b76f0a06..408f9ff01ec8 100644 --- a/llvm/lib/ProfileData/CMakeLists.txt +++ b/llvm/lib/ProfileData/CMakeLists.txt @@ -7,8 +7,6 @@ add_llvm_component_library(LLVMProfileData ItaniumManglingCanonicalizer.cpp MemProf.cpp MemProfReader.cpp - PGOCtxProfReader.cpp - PGOCtxProfWriter.cpp ProfileSummaryBuilder.cpp SampleProf.cpp SampleProfReader.cpp @@ -22,7 +20,6 @@ add_llvm_component_library(LLVMProfileData intrinsics_gen LINK_COMPONENTS - BitstreamReader Core Object Support diff --git a/llvm/lib/ProfileData/PGOCtxProfReader.cpp b/llvm/lib/ProfileData/PGOCtxProfReader.cpp deleted file mode 100644 index 3710f2e4b818..000000000000 --- a/llvm/lib/ProfileData/PGOCtxProfReader.cpp +++ /dev/null @@ -1,173 +0,0 @@ -//===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// Read a contextual profile into a datastructure suitable for maintenance -// throughout IPO -// -//===----------------------------------------------------------------------===// - -#include "llvm/ProfileData/PGOCtxProfReader.h" -#include "llvm/Bitstream/BitCodeEnums.h" -#include "llvm/Bitstream/BitstreamReader.h" -#include "llvm/ProfileData/InstrProf.h" -#include "llvm/ProfileData/PGOCtxProfWriter.h" -#include "llvm/Support/Errc.h" -#include "llvm/Support/Error.h" - -using namespace llvm; - -// FIXME(#92054) - these Error handling macros are (re-)invented in a few -// places. -#define EXPECT_OR_RET(LHS, RHS) \ - auto LHS = RHS; \ - if (!LHS) \ - return LHS.takeError(); - -#define RET_ON_ERR(EXPR) \ - if (auto Err = (EXPR)) \ - return Err; - -Expected -PGOContextualProfile::getOrEmplace(uint32_t Index, GlobalValue::GUID G, - SmallVectorImpl &&Counters) { - auto [Iter, Inserted] = Callsites[Index].insert( - {G, PGOContextualProfile(G, std::move(Counters))}); - if (!Inserted) - return make_error(instrprof_error::invalid_prof, - "Duplicate GUID for same callsite."); - return Iter->second; -} - -void PGOContextualProfile::getContainedGuids( - DenseSet &Guids) const { - Guids.insert(GUID); - for (const auto &[_, Callsite] : Callsites) - for (const auto &[_, Callee] : Callsite) - Callee.getContainedGuids(Guids); -} - -Expected PGOCtxProfileReader::advance() { - return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); -} - -Error PGOCtxProfileReader::wrongValue(const Twine &Msg) { - return make_error(instrprof_error::invalid_prof, Msg); -} - -Error PGOCtxProfileReader::unsupported(const Twine &Msg) { - return make_error(instrprof_error::unsupported_version, Msg); -} - -bool PGOCtxProfileReader::canReadContext() { - auto Blk = advance(); - if (!Blk) { - consumeError(Blk.takeError()); - return false; - } - return Blk->Kind == BitstreamEntry::SubBlock && - Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID; -} - -Expected, PGOContextualProfile>> -PGOCtxProfileReader::readContext(bool ExpectIndex) { - RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID)); - - std::optional Guid; - std::optional> Counters; - std::optional CallsiteIndex; - - SmallVector RecordValues; - - // We don't prescribe the order in which the records come in, and we are ok - // if other unsupported records appear. We seek in the current subblock until - // we get all we know. - auto GotAllWeNeed = [&]() { - return Guid.has_value() && Counters.has_value() && - (!ExpectIndex || CallsiteIndex.has_value()); - }; - while (!GotAllWeNeed()) { - RecordValues.clear(); - EXPECT_OR_RET(Entry, advance()); - if (Entry->Kind != BitstreamEntry::Record) - return wrongValue( - "Expected records before encountering more subcontexts"); - EXPECT_OR_RET(ReadRecord, - Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues)); - switch (*ReadRecord) { - case PGOCtxProfileRecords::Guid: - if (RecordValues.size() != 1) - return wrongValue("The GUID record should have exactly one value"); - Guid = RecordValues[0]; - break; - case PGOCtxProfileRecords::Counters: - Counters = std::move(RecordValues); - if (Counters->empty()) - return wrongValue("Empty counters. At least the entry counter (one " - "value) was expected"); - break; - case PGOCtxProfileRecords::CalleeIndex: - if (!ExpectIndex) - return wrongValue("The root context should not have a callee index"); - if (RecordValues.size() != 1) - return wrongValue("The callee index should have exactly one value"); - CallsiteIndex = RecordValues[0]; - break; - default: - // OK if we see records we do not understand, like records (profile - // components) introduced later. - break; - } - } - - PGOContextualProfile Ret(*Guid, std::move(*Counters)); - - while (canReadContext()) { - EXPECT_OR_RET(SC, readContext(true)); - auto &Targets = Ret.callsites()[*SC->first]; - auto [_, Inserted] = - Targets.insert({SC->second.guid(), std::move(SC->second)}); - if (!Inserted) - return wrongValue( - "Unexpected duplicate target (callee) at the same callsite."); - } - return std::make_pair(CallsiteIndex, std::move(Ret)); -} - -Error PGOCtxProfileReader::readMetadata() { - EXPECT_OR_RET(Blk, advance()); - if (Blk->Kind != BitstreamEntry::SubBlock) - return unsupported("Expected Version record"); - RET_ON_ERR( - Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID)); - EXPECT_OR_RET(MData, advance()); - if (MData->Kind != BitstreamEntry::Record) - return unsupported("Expected Version record"); - - SmallVector Ver; - EXPECT_OR_RET(Code, Cursor.readRecord(bitc::UNABBREV_RECORD, Ver)); - if (*Code != PGOCtxProfileRecords::Version) - return unsupported("Expected Version record"); - if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion) - return unsupported("Version " + Twine(*Code) + - " is higher than supported version " + - Twine(PGOCtxProfileWriter::CurrentVersion)); - return Error::success(); -} - -Expected> -PGOCtxProfileReader::loadContexts() { - std::map Ret; - RET_ON_ERR(readMetadata()); - while (canReadContext()) { - EXPECT_OR_RET(E, readContext(false)); - auto Key = E->second.guid(); - if (!Ret.insert({Key, std::move(E->second)}).second) - return wrongValue("Duplicate roots"); - } - return Ret; -} diff --git a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp deleted file mode 100644 index 508179756446..000000000000 --- a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp +++ /dev/null @@ -1,49 +0,0 @@ -//===- PGOCtxProfWriter.cpp - Contextual Instrumentation profile writer ---===// -// -// 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 -// -//===----------------------------------------------------------------------===// -// -// Write a contextual profile to bitstream. -// -//===----------------------------------------------------------------------===// - -#include "llvm/ProfileData/PGOCtxProfWriter.h" -#include "llvm/Bitstream/BitCodeEnums.h" - -using namespace llvm; -using namespace llvm::ctx_profile; - -void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) { - Writer.EmitCode(bitc::UNABBREV_RECORD); - Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits); - Writer.EmitVBR(Node.counters_size(), VBREncodingBits); - for (uint32_t I = 0U; I < Node.counters_size(); ++I) - Writer.EmitVBR64(Node.counters()[I], VBREncodingBits); -} - -// recursively write all the subcontexts. We do need to traverse depth first to -// model the context->subcontext implicitly, and since this captures call -// stacks, we don't really need to be worried about stack overflow and we can -// keep the implementation simple. -void PGOCtxProfileWriter::writeImpl(std::optional CallerIndex, - const ContextNode &Node) { - Writer.EnterSubblock(PGOCtxProfileBlockIDs::ContextNodeBlockID, CodeLen); - Writer.EmitRecord(PGOCtxProfileRecords::Guid, - SmallVector{Node.guid()}); - if (CallerIndex) - Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex, - SmallVector{*CallerIndex}); - writeCounters(Node); - for (uint32_t I = 0U; I < Node.callsites_size(); ++I) - for (const auto *Subcontext = Node.subContexts()[I]; Subcontext; - Subcontext = Subcontext->next()) - writeImpl(I, *Subcontext); - Writer.ExitBlock(); -} - -void PGOCtxProfileWriter::write(const ContextNode &RootNode) { - writeImpl(std::nullopt, RootNode); -} diff --git a/llvm/unittests/ProfileData/CMakeLists.txt b/llvm/unittests/ProfileData/CMakeLists.txt index c92642ded828..ce3a0a45ccf1 100644 --- a/llvm/unittests/ProfileData/CMakeLists.txt +++ b/llvm/unittests/ProfileData/CMakeLists.txt @@ -13,7 +13,6 @@ add_llvm_unittest(ProfileDataTests InstrProfTest.cpp ItaniumManglingCanonicalizerTest.cpp MemProfTest.cpp - PGOCtxProfReaderWriterTest.cpp SampleProfTest.cpp SymbolRemappingReaderTest.cpp ) diff --git a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp deleted file mode 100644 index d2cdbb28e2fc..000000000000 --- a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp +++ /dev/null @@ -1,255 +0,0 @@ -//===-------------- PGOCtxProfReadWriteTest.cpp ---------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - -#include "llvm/Bitstream/BitstreamReader.h" -#include "llvm/ProfileData/CtxInstrContextNode.h" -#include "llvm/ProfileData/PGOCtxProfReader.h" -#include "llvm/ProfileData/PGOCtxProfWriter.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Testing/Support/SupportHelpers.h" -#include "gtest/gtest.h" - -using namespace llvm; -using namespace llvm::ctx_profile; - -class PGOCtxProfRWTest : public ::testing::Test { - std::vector> Nodes; - std::map Roots; - -public: - ContextNode *createNode(GUID Guid, uint32_t NrCounters, uint32_t NrCallsites, - ContextNode *Next = nullptr) { - auto AllocSize = ContextNode::getAllocSize(NrCounters, NrCallsites); - auto *Mem = Nodes.emplace_back(std::make_unique(AllocSize)).get(); - std::memset(Mem, 0, AllocSize); - auto *Ret = new (Mem) ContextNode(Guid, NrCounters, NrCallsites, Next); - return Ret; - } - - void SetUp() override { - // Root (guid 1) has 2 callsites, one used for an indirect call to either - // guid 2 or 4. - // guid 2 calls guid 5 - // guid 5 calls guid 2 - // there's also a second root, guid3. - auto *Root1 = createNode(1, 2, 2); - Root1->counters()[0] = 10; - Root1->counters()[1] = 11; - Roots.insert({1, Root1}); - auto *L1 = createNode(2, 1, 1); - L1->counters()[0] = 12; - Root1->subContexts()[1] = createNode(4, 3, 1, L1); - Root1->subContexts()[1]->counters()[0] = 13; - Root1->subContexts()[1]->counters()[1] = 14; - Root1->subContexts()[1]->counters()[2] = 15; - - auto *L3 = createNode(5, 6, 3); - for (auto I = 0; I < 6; ++I) - L3->counters()[I] = 16 + I; - L1->subContexts()[0] = L3; - L3->subContexts()[2] = createNode(2, 1, 1); - L3->subContexts()[2]->counters()[0] = 30; - auto *Root2 = createNode(3, 1, 0); - Root2->counters()[0] = 40; - Roots.insert({3, Root2}); - } - - const std::map &roots() const { return Roots; } -}; - -void checkSame(const ContextNode &Raw, const PGOContextualProfile &Profile) { - EXPECT_EQ(Raw.guid(), Profile.guid()); - ASSERT_EQ(Raw.counters_size(), Profile.counters().size()); - for (auto I = 0U; I < Raw.counters_size(); ++I) - EXPECT_EQ(Raw.counters()[I], Profile.counters()[I]); - - for (auto I = 0U; I < Raw.callsites_size(); ++I) { - if (Raw.subContexts()[I] == nullptr) - continue; - EXPECT_TRUE(Profile.hasCallsite(I)); - const auto &ProfileTargets = Profile.callsite(I); - - std::map Targets; - for (const auto *N = Raw.subContexts()[I]; N; N = N->next()) - EXPECT_TRUE(Targets.insert({N->guid(), N}).second); - - EXPECT_EQ(Targets.size(), ProfileTargets.size()); - for (auto It : Targets) { - auto PIt = ProfileTargets.find(It.second->guid()); - EXPECT_NE(PIt, ProfileTargets.end()); - checkSame(*It.second, PIt->second); - } - } -} - -TEST_F(PGOCtxProfRWTest, RoundTrip) { - llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); - { - std::error_code EC; - raw_fd_stream Out(ProfileFile.path(), EC); - ASSERT_FALSE(EC); - { - PGOCtxProfileWriter Writer(Out); - for (auto &[_, R] : roots()) - Writer.write(*R); - } - } - { - ErrorOr> MB = - MemoryBuffer::getFile(ProfileFile.path()); - ASSERT_TRUE(!!MB); - ASSERT_NE(*MB, nullptr); - BitstreamCursor Cursor((*MB)->getBuffer()); - PGOCtxProfileReader Reader(Cursor); - auto Expected = Reader.loadContexts(); - ASSERT_TRUE(!!Expected); - auto &Ctxes = *Expected; - EXPECT_EQ(Ctxes.size(), roots().size()); - EXPECT_EQ(Ctxes.size(), 2U); - for (auto &[G, R] : roots()) - checkSame(*R, Ctxes.find(G)->second); - } -} - -TEST_F(PGOCtxProfRWTest, InvalidCounters) { - auto *R = createNode(1, 0, 1); - llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); - { - std::error_code EC; - raw_fd_stream Out(ProfileFile.path(), EC); - ASSERT_FALSE(EC); - { - PGOCtxProfileWriter Writer(Out); - Writer.write(*R); - } - } - { - auto MB = MemoryBuffer::getFile(ProfileFile.path()); - ASSERT_TRUE(!!MB); - ASSERT_NE(*MB, nullptr); - BitstreamCursor Cursor((*MB)->getBuffer()); - PGOCtxProfileReader Reader(Cursor); - auto Expected = Reader.loadContexts(); - EXPECT_FALSE(Expected); - consumeError(Expected.takeError()); - } -} - -TEST_F(PGOCtxProfRWTest, Empty) { - BitstreamCursor Cursor(""); - PGOCtxProfileReader Reader(Cursor); - auto Expected = Reader.loadContexts(); - EXPECT_FALSE(Expected); - consumeError(Expected.takeError()); -} - -TEST_F(PGOCtxProfRWTest, Invalid) { - BitstreamCursor Cursor("Surely this is not valid"); - PGOCtxProfileReader Reader(Cursor); - auto Expected = Reader.loadContexts(); - EXPECT_FALSE(Expected); - consumeError(Expected.takeError()); -} - -TEST_F(PGOCtxProfRWTest, ValidButEmpty) { - llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); - { - std::error_code EC; - raw_fd_stream Out(ProfileFile.path(), EC); - ASSERT_FALSE(EC); - { - PGOCtxProfileWriter Writer(Out); - // don't write anything - this will just produce the metadata subblock. - } - } - { - auto MB = MemoryBuffer::getFile(ProfileFile.path()); - ASSERT_TRUE(!!MB); - ASSERT_NE(*MB, nullptr); - BitstreamCursor Cursor((*MB)->getBuffer()); - PGOCtxProfileReader Reader(Cursor); - auto Expected = Reader.loadContexts(); - EXPECT_TRUE(!!Expected); - EXPECT_TRUE(Expected->empty()); - } -} - -TEST_F(PGOCtxProfRWTest, WrongVersion) { - llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); - { - std::error_code EC; - raw_fd_stream Out(ProfileFile.path(), EC); - ASSERT_FALSE(EC); - { - PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1); - } - } - { - auto MB = MemoryBuffer::getFile(ProfileFile.path()); - ASSERT_TRUE(!!MB); - ASSERT_NE(*MB, nullptr); - BitstreamCursor Cursor((*MB)->getBuffer()); - PGOCtxProfileReader Reader(Cursor); - auto Expected = Reader.loadContexts(); - EXPECT_FALSE(Expected); - consumeError(Expected.takeError()); - } -} - -TEST_F(PGOCtxProfRWTest, DuplicateRoots) { - llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); - { - std::error_code EC; - raw_fd_stream Out(ProfileFile.path(), EC); - ASSERT_FALSE(EC); - { - PGOCtxProfileWriter Writer(Out); - Writer.write(*createNode(1, 1, 1)); - Writer.write(*createNode(1, 1, 1)); - } - } - { - auto MB = MemoryBuffer::getFile(ProfileFile.path()); - ASSERT_TRUE(!!MB); - ASSERT_NE(*MB, nullptr); - BitstreamCursor Cursor((*MB)->getBuffer()); - PGOCtxProfileReader Reader(Cursor); - auto Expected = Reader.loadContexts(); - EXPECT_FALSE(Expected); - consumeError(Expected.takeError()); - } -} - -TEST_F(PGOCtxProfRWTest, DuplicateTargets) { - llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); - { - std::error_code EC; - raw_fd_stream Out(ProfileFile.path(), EC); - ASSERT_FALSE(EC); - { - auto *R = createNode(1, 1, 1); - auto *L1 = createNode(2, 1, 0); - auto *L2 = createNode(2, 1, 0, L1); - R->subContexts()[0] = L2; - PGOCtxProfileWriter Writer(Out); - Writer.write(*R); - } - } - { - auto MB = MemoryBuffer::getFile(ProfileFile.path()); - ASSERT_TRUE(!!MB); - ASSERT_NE(*MB, nullptr); - BitstreamCursor Cursor((*MB)->getBuffer()); - PGOCtxProfileReader Reader(Cursor); - auto Expected = Reader.loadContexts(); - EXPECT_FALSE(Expected); - consumeError(Expected.takeError()); - } -} -- GitLab From 9ae2177843f681c70ad89506155a2cb83eeebfd4 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 16 May 2024 02:41:48 +0800 Subject: [PATCH 014/403] [RISCV] Handle undef AVLs in RISCVInsertVSETVLI Before #91440 a VSETVLIInfo would have had an IMPLICIT_DEF defining instruction, but now we look up a VNInfo which doesn't exist, which triggers an assertion failure. Mark these undef AVLs as AVLIsIgnored. --- llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp | 20 +++++++++------- llvm/test/CodeGen/RISCV/rvv/vsetvli-insert.ll | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp b/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp index 1c815424bdfa..363007d7b68b 100644 --- a/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp +++ b/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp @@ -48,15 +48,13 @@ static cl::opt DisableInsertVSETVLPHIOpt( namespace { /// Given a virtual register \p Reg, return the corresponding VNInfo for it. -/// This should never return nullptr. +/// This will return nullptr if the virtual register is an implicit_def. static VNInfo *getVNInfoFromReg(Register Reg, const MachineInstr &MI, const LiveIntervals *LIS) { assert(Reg.isVirtual()); auto &LI = LIS->getInterval(Reg); SlotIndex SI = LIS->getSlotIndexes()->getInstructionIndex(MI); - VNInfo *VNI = LI.getVNInfoBefore(SI); - assert(VNI); - return VNI; + return LI.getVNInfoBefore(SI); } static unsigned getVLOpNum(const MachineInstr &MI) { @@ -894,8 +892,12 @@ static VSETVLIInfo getInfoForVSETVLI(const MachineInstr &MI, "Can't handle X0, X0 vsetvli yet"); if (AVLReg == RISCV::X0) NewInfo.setAVLVLMAX(); - else - NewInfo.setAVLRegDef(getVNInfoFromReg(AVLReg, MI, LIS), AVLReg); + else if (VNInfo *VNI = getVNInfoFromReg(AVLReg, MI, LIS)) + NewInfo.setAVLRegDef(VNI, AVLReg); + else { + assert(MI.getOperand(1).isUndef()); + NewInfo.setAVLIgnored(); + } } NewInfo.setVTYPE(MI.getOperand(2).getImm()); @@ -966,9 +968,11 @@ static VSETVLIInfo computeInfoForInstr(const MachineInstr &MI, uint64_t TSFlags, } else InstrInfo.setAVLImm(Imm); + } else if (VNInfo *VNI = getVNInfoFromReg(VLOp.getReg(), MI, LIS)) { + InstrInfo.setAVLRegDef(VNI, VLOp.getReg()); } else { - InstrInfo.setAVLRegDef(getVNInfoFromReg(VLOp.getReg(), MI, LIS), - VLOp.getReg()); + assert(VLOp.isUndef()); + InstrInfo.setAVLIgnored(); } } else { assert(isScalarExtractInstr(MI)); diff --git a/llvm/test/CodeGen/RISCV/rvv/vsetvli-insert.ll b/llvm/test/CodeGen/RISCV/rvv/vsetvli-insert.ll index 12bb4d27b0f9..da0c1cfb5009 100644 --- a/llvm/test/CodeGen/RISCV/rvv/vsetvli-insert.ll +++ b/llvm/test/CodeGen/RISCV/rvv/vsetvli-insert.ll @@ -699,3 +699,27 @@ declare @llvm.riscv.vmsgt.nxv2i32.i32.i64(, declare @llvm.riscv.vmor.nxv2i1.i64(, , i64) declare void @llvm.riscv.vse.mask.nxv2i32.i64(, ptr nocapture, , i64) declare void @llvm.riscv.vse.nxv2i32.i64(, ptr nocapture, i64) + +define @avl_undef1(, , ) { +; CHECK-LABEL: avl_undef1: +; CHECK: # %bb.0: +; CHECK-NEXT: vsetivli zero, 1, e32, m1, tu, ma +; CHECK-NEXT: vadd.vv v8, v9, v10 +; CHECK-NEXT: ret + %a = call @llvm.riscv.vadd.nxv2i32.nxv2i32( + %0, + %1, + %2, + i64 undef + ) + ret %a +} + +define i64 @avl_undef2() { +; CHECK-LABEL: avl_undef2: +; CHECK: # %bb.0: +; CHECK-NEXT: vsetvli a0, a0, e32, mf2, ta, ma +; CHECK-NEXT: ret + %1 = tail call i64 @llvm.riscv.vsetvli(i64 poison, i64 2, i64 7) + ret i64 %1 +} -- GitLab From 378c9e952a3d198873677fb2d2afb33695185b72 Mon Sep 17 00:00:00 2001 From: LLVM GN Syncbot Date: Wed, 15 May 2024 18:51:56 +0000 Subject: [PATCH 015/403] [gn build] Port 2c54bf497f7d --- llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn | 2 -- llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn | 1 - 2 files changed, 3 deletions(-) diff --git a/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn index c6fa142b3766..9dbfe0f94c1d 100644 --- a/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn @@ -17,8 +17,6 @@ static_library("ProfileData") { "ItaniumManglingCanonicalizer.cpp", "MemProf.cpp", "MemProfReader.cpp", - "PGOCtxProfReader.cpp", - "PGOCtxProfWriter.cpp", "ProfileSummaryBuilder.cpp", "SampleProf.cpp", "SampleProfReader.cpp", diff --git a/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn b/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn index f45542519173..4919a8089209 100644 --- a/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn @@ -14,7 +14,6 @@ unittest("ProfileDataTests") { "InstrProfTest.cpp", "ItaniumManglingCanonicalizerTest.cpp", "MemProfTest.cpp", - "PGOCtxProfReaderWriterTest.cpp", "SampleProfTest.cpp", "SymbolRemappingReaderTest.cpp", ] -- GitLab From d542eb7aa830e94490b943a3ea0937506fece15b Mon Sep 17 00:00:00 2001 From: Vlad Serebrennikov Date: Wed, 15 May 2024 23:12:02 +0400 Subject: [PATCH 016/403] [clang] Add tests for CWG issues regarding completeness of types (#92113) This patch covers the following Core issues: [CWG930](https://cplusplus.github.io/CWG/issues/930.html) "`alignof` with incomplete array type" [CWG1110](https://cplusplus.github.io/CWG/issues/1110.html) "Incomplete return type should be allowed in `decltype` operand" [CWG1340](https://cplusplus.github.io/CWG/issues/1340.html) "Complete type in member pointer expressions" [CWG1352](https://cplusplus.github.io/CWG/issues/1352.html) "Inconsistent class scope and completeness rules" [CWG1458](https://cplusplus.github.io/CWG/issues/1458.html) "Address of incomplete type vs `operator&()`" [CWG1824](https://cplusplus.github.io/CWG/issues/1824.html) "Completeness of return type vs point of instantiation" [CWG1832](https://cplusplus.github.io/CWG/issues/1832.html) "Casting to incomplete enumeration" [CWG2304](https://cplusplus.github.io/CWG/issues/2304.html) "Incomplete type vs overload resolution" [CWG2310](https://cplusplus.github.io/CWG/issues/2310.html) "Type completeness and derived-to-base pointer conversions" [CWG2430](https://cplusplus.github.io/CWG/issues/2430.html) "Completeness of return and parameter types of member functions" [CWG2512](https://cplusplus.github.io/CWG/issues/2512.html) "`typeid` and incomplete class types" [CWG2630](https://cplusplus.github.io/CWG/issues/2630.html) "Syntactic specification of class completeness" [CWG2718](https://cplusplus.github.io/CWG/issues/2718.html) "Type completeness for derived-to-base conversions" [CWG2857](https://cplusplus.github.io/CWG/issues/2857.html) "Argument-dependent lookup with incomplete class types" Current wording for CWG1110 came from [P0135R1](https://wg21.link/p0135R1) "Wording for guaranteed copy elision through simplified value categories". As a drive-by fix, I fixed incorrect status of CWG1815, test for which was added in #87933. CC @yronglin --- clang/test/CXX/drs/cwg11xx.cpp | 15 +++++++++++ clang/test/CXX/drs/cwg13xx.cpp | 31 ++++++++++++++++++++++ clang/test/CXX/drs/cwg14xx.cpp | 17 ++++++++++++ clang/test/CXX/drs/cwg18xx.cpp | 28 +++++++++++++++++++- clang/test/CXX/drs/cwg23xx.cpp | 48 ++++++++++++++++++++++++++++++++-- clang/test/CXX/drs/cwg24xx.cpp | 6 +++++ clang/test/CXX/drs/cwg25xx.cpp | 15 ++++++++--- clang/test/CXX/drs/cwg2630.cpp | 23 ++++++++++++++++ clang/test/CXX/drs/cwg26xx.cpp | 2 ++ clang/test/CXX/drs/cwg27xx.cpp | 14 +++++++--- clang/test/CXX/drs/cwg28xx.cpp | 28 +++++++++++++++++--- clang/test/CXX/drs/cwg9xx.cpp | 7 +++++ clang/www/cxx_dr_status.html | 46 ++++++++++++++++++++------------ 13 files changed, 250 insertions(+), 30 deletions(-) create mode 100644 clang/test/CXX/drs/cwg2630.cpp diff --git a/clang/test/CXX/drs/cwg11xx.cpp b/clang/test/CXX/drs/cwg11xx.cpp index 46a0e526be39..8d187041400a 100644 --- a/clang/test/CXX/drs/cwg11xx.cpp +++ b/clang/test/CXX/drs/cwg11xx.cpp @@ -4,6 +4,21 @@ // RUN: %clang_cc1 -std=c++17 %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++2a %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors +namespace cwg1110 { // cwg1110: 3.1 +#if __cplusplus >= 201103L +template +T return_T(); + +struct A; + +template +struct B; + +decltype(return_T())* a; +decltype(return_T>())* b; +#endif +} // namespace cwg1110 + namespace cwg1111 { // cwg1111: 3.2 namespace example1 { template struct set; // #cwg1111-struct-set diff --git a/clang/test/CXX/drs/cwg13xx.cpp b/clang/test/CXX/drs/cwg13xx.cpp index a334b6d01acf..416de7c536b1 100644 --- a/clang/test/CXX/drs/cwg13xx.cpp +++ b/clang/test/CXX/drs/cwg13xx.cpp @@ -306,6 +306,18 @@ namespace cwg1330 { // cwg1330: 4 c++11 // cwg1334: sup 1719 +namespace cwg1340 { // cwg1340: 2.9 +struct A; +struct B; + +void f(B* a, A B::* p) { + (*a).*p; + // expected-warning@-1 {{expression result unused}} + a->*p; + // expected-warning@-1 {{expression result unused}} +} +} // namespace cwg1340 + namespace cwg1341 { // cwg1341: sup P0683R1 #if __cplusplus >= 202002L int a; @@ -451,6 +463,25 @@ static_assert(!__is_nothrow_constructible(D4, int), ""); #endif } // namespace cwg1350 +namespace cwg1352 { // cwg1352: 3.0 +struct A { +#if __cplusplus >= 201103L + int a = sizeof(A); +#endif + void f(int b = sizeof(A)); +}; + +template +struct B { +#if __cplusplus >= 201103L + int a = sizeof(B) + sizeof(T); +#endif + void f(int b = sizeof(B) + sizeof(T)); +}; + +template class B; +} // namespace cwg1352 + namespace cwg1358 { // cwg1358: 3.1 #if __cplusplus >= 201103L struct Lit { constexpr operator int() const { return 0; } }; diff --git a/clang/test/CXX/drs/cwg14xx.cpp b/clang/test/CXX/drs/cwg14xx.cpp index 9ff9a68dc13c..f01d96ad47f3 100644 --- a/clang/test/CXX/drs/cwg14xx.cpp +++ b/clang/test/CXX/drs/cwg14xx.cpp @@ -86,6 +86,23 @@ struct A { }; } +namespace cwg1458 { // cwg1458: 3.1 +#if __cplusplus >= 201103L +struct A; + +void f() { + constexpr A* a = nullptr; + constexpr int p = &*a; + // expected-error@-1 {{cannot initialize a variable of type 'const int' with an rvalue of type 'A *'}} + constexpr A *p2 = &*a; +} + +struct A { + int operator&(); +}; +#endif +} // namespace cwg1458 + namespace cwg1460 { // cwg1460: 3.5 #if __cplusplus >= 201103L namespace DRExample { diff --git a/clang/test/CXX/drs/cwg18xx.cpp b/clang/test/CXX/drs/cwg18xx.cpp index 9eb749153e57..89adc2838490 100644 --- a/clang/test/CXX/drs/cwg18xx.cpp +++ b/clang/test/CXX/drs/cwg18xx.cpp @@ -206,7 +206,7 @@ namespace cwg1814 { // cwg1814: yes #endif } -namespace cwg1815 { // cwg1815: yes +namespace cwg1815 { // cwg1815: 19 #if __cplusplus >= 201402L struct A { int &&r = 0; }; A a = {}; @@ -303,6 +303,32 @@ namespace cwg1822 { // cwg1822: yes #endif } +namespace cwg1824 { // cwg1824: 2.7 +template +struct A { + T t; +}; + +struct S { + A f() { return A(); } +}; +} // namespace cwg1824 + +namespace cwg1832 { // cwg1832: 3.0 +enum E { // #cwg1832-E + a = static_cast(static_cast(0)) + // expected-error@-1 {{'E' is an incomplete type}} + // expected-note@#cwg1832-E {{definition of 'cwg1832::E' is not complete until the closing '}'}} +}; + +#if __cplusplus >= 201103L +enum E2: decltype(static_cast(0), 0) {}; +// expected-error@-1 {{unknown type name 'E2'}} +enum class E3: decltype(static_cast(0), 0) {}; +// expected-error@-1 {{unknown type name 'E3'}} +#endif +} // namespace cwg1832 + namespace cwg1837 { // cwg1837: 3.3 #if __cplusplus >= 201103L template diff --git a/clang/test/CXX/drs/cwg23xx.cpp b/clang/test/CXX/drs/cwg23xx.cpp index db5b7c3cd3c9..ae5ec3b878f5 100644 --- a/clang/test/CXX/drs/cwg23xx.cpp +++ b/clang/test/CXX/drs/cwg23xx.cpp @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -std=c++98 %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s -// RUN: %clang_cc1 -std=c++11 %s -verify=expected,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s -// RUN: %clang_cc1 -std=c++14 %s -verify=expected,since-cxx11,since-cxx14 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++11 %s -verify=expected,cxx11-14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s +// RUN: %clang_cc1 -std=c++14 %s -verify=expected,cxx11-14,since-cxx11,since-cxx14 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s // RUN: %clang_cc1 -std=c++17 %s -verify=expected,since-cxx11,since-cxx14,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s // RUN: %clang_cc1 -std=c++20 %s -verify=expected,since-cxx11,since-cxx14,since-cxx17,since-cxx20 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s // RUN: %clang_cc1 -std=c++23 %s -verify=expected,since-cxx11,since-cxx14,since-cxx17,since-cxx20 -fexceptions -fcxx-exceptions -pedantic-errors 2>&1 | FileCheck %s @@ -47,6 +47,50 @@ void g() { } // namespace cwg2303 #endif +namespace cwg2304 { // cwg2304: 2.8 +template void foo(T, int); +template void foo(T&, ...); +struct Q; // #cwg2304-Q +void fn1(Q &data_vectors) { + foo(data_vectors, 0); + // expected-error@-1 {{argument type 'cwg2304::Q' is incomplete}} + // expected-note@#cwg2304-Q {{forward declaration of 'cwg2304::Q'}} +} +} // namespace cwg2304 + +namespace cwg2310 { // cwg2310: partial +#if __cplusplus >= 201103L +template +struct check_derived_from { + static A a; + // FIXME: all 3 examples should be rejected in all language modes. + // FIXME: we should test this in 98 mode. + // FIXME: we accept this when MSVC triple is used + static constexpr B *p = &a; +#if !defined(_WIN32) || defined(__MINGW32__) + // cxx11-14-error@-2 {{cannot initialize a variable of type 'cwg2310::X *const' with an rvalue of type 'cwg2310::Z *'}} + // cxx11-14-note@#cwg2310-X {{in instantiation of template class 'cwg2310::check_derived_from' requested here}} + // cxx11-14-error@-4 {{cannot initialize a variable of type 'cwg2310::Y *const' with an rvalue of type 'cwg2310::Z *'}} + // cxx11-14-note@#cwg2310-Y {{in instantiation of template class 'cwg2310::check_derived_from' requested here}} +#endif +}; + +struct W {}; +struct X {}; +struct Y {}; +struct Z : W, + X, check_derived_from, // #cwg2310-X + check_derived_from, Y // #cwg2310-Y +{ + // FIXME: It was properly rejected before, but we're crashing since Clang 11 in C++11 and C++14 modes. + // See https://github.com/llvm/llvm-project/issues/59920 +#if __cplusplus >= 201703L + check_derived_from cdf; +#endif +}; +#endif +} // namespace cwg2310 + // cwg2331: na // cwg2335 is in cwg2335.cxx diff --git a/clang/test/CXX/drs/cwg24xx.cpp b/clang/test/CXX/drs/cwg24xx.cpp index 9f876cd87083..75e1a614765c 100644 --- a/clang/test/CXX/drs/cwg24xx.cpp +++ b/clang/test/CXX/drs/cwg24xx.cpp @@ -45,6 +45,12 @@ void fallthrough(int n) { #endif } +namespace cwg2430 { // cwg2430: 2.7 +struct S { + S f(S s) { return s; } +}; +} // namespace cwg2430 + namespace cwg2450 { // cwg2450: 18 #if __cplusplus >= 202302L struct S {int a;}; diff --git a/clang/test/CXX/drs/cwg25xx.cpp b/clang/test/CXX/drs/cwg25xx.cpp index 8bca58f44944..0934f0cc19c6 100644 --- a/clang/test/CXX/drs/cwg25xx.cpp +++ b/clang/test/CXX/drs/cwg25xx.cpp @@ -6,12 +6,21 @@ // RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23 -fexceptions -fcxx-exceptions -pedantic-errors // RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx11,since-cxx20,since-cxx23 -fexceptions -fcxx-exceptions -pedantic-errors -#if __cplusplus == 199711L -// expected-no-diagnostics -#endif +namespace std { +struct type_info{}; +} // namespace std // cwg2504 is in cwg2504.cpp +namespace cwg2512 { // cwg2512: 2.7 +struct A; // #cwg2512-A +void foo(A* p) { + typeid(*p); + // expected-error@-1 {{'typeid' of incomplete type 'A'}} + // expected-note@#cwg2512-A {{forward declaration of 'cwg2512::A'}} +} +} // namespace cwg2512 + namespace cwg2516 { // cwg2516: 3.0 // NB: reusing 1482 test #if __cplusplus >= 201103L diff --git a/clang/test/CXX/drs/cwg2630.cpp b/clang/test/CXX/drs/cwg2630.cpp new file mode 100644 index 000000000000..0f50dc4f7458 --- /dev/null +++ b/clang/test/CXX/drs/cwg2630.cpp @@ -0,0 +1,23 @@ +// RUN: split-file --leading-lines %s %t +// RUN: %clang_cc1 -std=c++20 -verify -emit-module-interface %t/module.cppm -o %t/module.pcm +// RUN: %clang_cc1 -std=c++20 -verify -fmodule-file=A=%t/module.pcm %t/main.cpp +// RUN: %clang_cc1 -std=c++23 -verify -emit-module-interface %t/module.cppm -o %t/module.pcm +// RUN: %clang_cc1 -std=c++23 -verify -fmodule-file=A=%t/module.pcm %t/main.cpp +// RUN: %clang_cc1 -std=c++2c -verify -emit-module-interface %t/module.cppm -o %t/module.pcm +// RUN: %clang_cc1 -std=c++2c -verify -fmodule-file=A=%t/module.pcm %t/main.cpp + +//--- module.cppm +// expected-no-diagnostics +export module A; + +namespace cwg2630 { +export class X {}; +} // namespace cwg2630 + +//--- main.cpp +// expected-no-diagnostics +import A; + +namespace cwg2630 { // cwg2630: 9 +X x; +} // namespace cwg2630 diff --git a/clang/test/CXX/drs/cwg26xx.cpp b/clang/test/CXX/drs/cwg26xx.cpp index f7a05b9827a2..d3c5b5bb7b6b 100644 --- a/clang/test/CXX/drs/cwg26xx.cpp +++ b/clang/test/CXX/drs/cwg26xx.cpp @@ -49,6 +49,8 @@ void f() { #endif } +// cwg2630 is in cwg2630.cpp + namespace cwg2631 { // cwg2631: 16 #if __cplusplus >= 202002L constexpr int g(); diff --git a/clang/test/CXX/drs/cwg27xx.cpp b/clang/test/CXX/drs/cwg27xx.cpp index 0434427d6c92..53ddd566b7db 100644 --- a/clang/test/CXX/drs/cwg27xx.cpp +++ b/clang/test/CXX/drs/cwg27xx.cpp @@ -6,9 +6,17 @@ // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++23 -verify=expected,since-cxx23 %s // RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++2c -verify=expected,since-cxx23,since-cxx26 %s -#if __cplusplus <= 202002L -// expected-no-diagnostics -#endif +namespace cwg2718 { // cwg2718: 2.7 +struct B {}; +struct D; + +void f(B b) { + static_cast(b); + // expected-error@-1 {{non-const lvalue reference to type 'D' cannot bind to a value of unrelated type 'B'}} +} + +struct D : B {}; +} // namespace cwg2718 namespace cwg2759 { // cwg2759: 19 #if __cplusplus >= 201103L diff --git a/clang/test/CXX/drs/cwg28xx.cpp b/clang/test/CXX/drs/cwg28xx.cpp index be35d366bdd6..696cd1b9c84e 100644 --- a/clang/test/CXX/drs/cwg28xx.cpp +++ b/clang/test/CXX/drs/cwg28xx.cpp @@ -6,10 +6,6 @@ // RUN: %clang_cc1 -std=c++23 -verify=expected,since-cxx20,since-cxx23 %s // RUN: %clang_cc1 -std=c++2c -verify=expected,since-cxx20,since-cxx23,since-cxx26 %s -#if __cplusplus < 202002L -// expected-no-diagnostics -#endif - namespace cwg2819 { // cwg2819: 19 tentatively ready 2023-12-01 #if __cpp_constexpr >= 202306L constexpr void* p = nullptr; @@ -67,6 +63,30 @@ void B::g() requires true; } // namespace cwg2847 +namespace cwg2857 { // cwg2857: no +struct A {}; +template +struct D; +namespace N { + struct B {}; + void adl_only(A*, D*); // #cwg2857-adl_only +} + +void f(A* a, D* d) { + adl_only(a, d); + // expected-error@-1 {{use of undeclared identifier 'adl_only'; did you mean 'N::adl_only'?}} + // expected-note@#cwg2857-adl_only {{'N::adl_only' declared here}} +} + +#if __cplusplus >= 201103L +template +struct D : N::B { + // FIXME: ADL shouldn't associate it's base B and N since D is not complete here + decltype(adl_only((A*) nullptr, (D*) nullptr)) f; +}; +#endif +} // namespace cwg2857 + namespace cwg2858 { // cwg2858: 19 tentatively ready 2024-04-05 #if __cplusplus > 202302L diff --git a/clang/test/CXX/drs/cwg9xx.cpp b/clang/test/CXX/drs/cwg9xx.cpp index 8ecb149c355f..2700b0f5662a 100644 --- a/clang/test/CXX/drs/cwg9xx.cpp +++ b/clang/test/CXX/drs/cwg9xx.cpp @@ -14,6 +14,13 @@ namespace std { }; } +namespace cwg930 { // cwg930: 2.7 +#if __cplusplus >= 201103L +static_assert(alignof(int[]) == alignof(int), ""); +static_assert(alignof(int[][2]) == alignof(int[2]), ""); +#endif +} // namespace cwg930 + namespace cwg948 { // cwg948: 3.7 #if __cplusplus >= 201103L class A { diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html index 92fdcf5556ed..abf5d4ae4676 100755 --- a/clang/www/cxx_dr_status.html +++ b/clang/www/cxx_dr_status.html @@ -5388,7 +5388,7 @@ and POD class 930 CD2 alignof with incomplete array type - Unknown + Clang 2.7 931 @@ -6468,7 +6468,7 @@ and POD class 1110 NAD Incomplete return type should be allowed in decltype operand - Unknown + Clang 3.1 1111 @@ -7848,7 +7848,7 @@ and POD class 1340 CD3 Complete type in member pointer expressions - Unknown + Clang 2.9 1341 @@ -7920,7 +7920,7 @@ and POD class 1352 CD3 Inconsistent class scope and completeness rules - Unknown + Clang 3.0 1353 @@ -8556,7 +8556,7 @@ and POD class 1458 CD3 Address of incomplete type vs operator&() - Unknown + Clang 3.1 1459 @@ -10752,7 +10752,7 @@ and POD class 1824 CD4 Completeness of return type vs point of instantiation - Unknown + Clang 2.7 1825 @@ -10800,7 +10800,7 @@ and POD class 1832 CD4 Casting to incomplete enumeration - Unknown + Clang 3.0 1833 @@ -13632,7 +13632,7 @@ and POD class 2304 NAD Incomplete type vs overload resolution - Unknown + Clang 2.8 2305 @@ -13668,7 +13668,7 @@ and POD class 2310 CD5 Type completeness and derived-to-base pointer conversions - Unknown + Partial 2311 @@ -14388,7 +14388,7 @@ and POD class 2430 C++20 Completeness of return and parameter types of member functions - Unknown + Clang 2.7 2431 @@ -14880,7 +14880,7 @@ and POD class 2512 NAD typeid and incomplete class types - Unknown + Clang 2.7 2513 @@ -15588,7 +15588,7 @@ and POD class 2630 C++23 Syntactic specification of class completeness - Unknown + Clang 9 2631 @@ -16116,7 +16116,7 @@ and POD class 2718 DRWP Type completeness for derived-to-base conversions - Unknown + Clang 2.7 2719 @@ -16951,7 +16951,7 @@ objects 2857 DR Argument-dependent lookup with incomplete class types - Unknown + No 2858 @@ -16985,7 +16985,7 @@ objects 2863 - tentatively ready + drafting Unclear synchronization requirements for object lifetime rules Not resolved @@ -17021,13 +17021,13 @@ objects 2869 - open + review this in local classes Not resolved 2870 - open + review Combining absent encoding-prefixes Not resolved @@ -17138,6 +17138,18 @@ objects open Missing cases for reference and array types for argument-dependent lookup Not resolved + + + 2889 + open + Requiring an accessible destructor for destroying operator delete + Not resolved + + + 2890 + open + Defining members of local classes + Not resolved -- GitLab From 64b3cdc0220174c1af236a42b227a5226f0f12c5 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Wed, 15 May 2024 14:13:50 -0500 Subject: [PATCH 017/403] [libc] Fix GPU handling for unsupported backends (#92271) Summary: If the user does not have the selected backend enabled, we should still be able to build the LLVM-IR an ddistribute it. This patch makes logic to suppress tests if the backend can't build it, as well as removing a flag for the building that's only present int he NVPTX backend. --- libc/cmake/modules/LLVMLibCCompileOptionRules.cmake | 1 - libc/cmake/modules/prepare_libc_gpu_build.cmake | 10 +++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake index 5b3a10d55fed..3bf429381d4a 100644 --- a/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake +++ b/libc/cmake/modules/LLVMLibCCompileOptionRules.cmake @@ -101,7 +101,6 @@ function(_get_common_compile_options output_var flags) if(LIBC_TARGET_ARCHITECTURE_IS_NVPTX) list(APPEND compile_options "-Wno-unknown-cuda-version") - list(APPEND compile_options "SHELL:-mllvm -nvptx-emit-init-fini-kernel=false") list(APPEND compile_options "--cuda-feature=+ptx63") if(LIBC_CUDA_ROOT) list(APPEND compile_options "--cuda-path=${LIBC_CUDA_ROOT}") diff --git a/libc/cmake/modules/prepare_libc_gpu_build.cmake b/libc/cmake/modules/prepare_libc_gpu_build.cmake index 20aca16990fc..88538caaa3bc 100644 --- a/libc/cmake/modules/prepare_libc_gpu_build.cmake +++ b/libc/cmake/modules/prepare_libc_gpu_build.cmake @@ -76,7 +76,15 @@ elseif(LIBC_TARGET_ARCHITECTURE_IS_NVPTX) endif() set(gpu_test_architecture "") -if(LIBC_GPU_TEST_ARCHITECTURE) +if(DEFINED LLVM_TARGETS_TO_BUILD AND LIBC_TARGET_ARCHITECTURE_IS_AMDGPU + AND NOT "AMDGPU" IN_LIST LLVM_TARGETS_TO_BUILD) + set(LIBC_GPU_TESTS_DISABLED TRUE) + message(STATUS "AMDGPU backend is not available, tests will not be built") +elseif(DEFINED LLVM_TARGETS_TO_BUILD AND LIBC_TARGET_ARCHITECTURE_IS_AMDGPU + AND NOT "NVPTX" IN_LIST LLVM_TARGETS_TO_BUILD) + set(LIBC_GPU_TESTS_DISABLED TRUE) + message(STATUS "NVPTX backend is not available, tests will not be built") +elseif(LIBC_GPU_TEST_ARCHITECTURE) set(LIBC_GPU_TESTS_DISABLED FALSE) set(gpu_test_architecture ${LIBC_GPU_TEST_ARCHITECTURE}) message(STATUS "Using user-specified GPU architecture for testing: " -- GitLab From 4ab2ac22d0a481460536f673377b644702cb3372 Mon Sep 17 00:00:00 2001 From: Patrick O'Neill Date: Wed, 15 May 2024 12:39:28 -0700 Subject: [PATCH 018/403] [DAGCombiner] Mark vectors as not AllAddOne/AllSubOne on type mismatch (#92195) Fixes #92193. --- llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp | 7 +++++-- llvm/test/CodeGen/RISCV/pr92193.ll | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 llvm/test/CodeGen/RISCV/pr92193.ll diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp index a044b6dc4838..2b181cd3ab1d 100644 --- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp @@ -12142,8 +12142,11 @@ SDValue DAGCombiner::foldVSelectOfConstants(SDNode *N) { SDValue N2Elt = N2.getOperand(i); if (N1Elt.isUndef() || N2Elt.isUndef()) continue; - if (N1Elt.getValueType() != N2Elt.getValueType()) - continue; + if (N1Elt.getValueType() != N2Elt.getValueType()) { + AllAddOne = false; + AllSubOne = false; + break; + } const APInt &C1 = N1Elt->getAsAPIntVal(); const APInt &C2 = N2Elt->getAsAPIntVal(); diff --git a/llvm/test/CodeGen/RISCV/pr92193.ll b/llvm/test/CodeGen/RISCV/pr92193.ll new file mode 100644 index 000000000000..8c8398c4b45f --- /dev/null +++ b/llvm/test/CodeGen/RISCV/pr92193.ll @@ -0,0 +1,21 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 4 +; RUN: llc -mtriple=riscv64-unknown-linux-gnu < %s | FileCheck %s +; RUN: llc -mtriple=riscv32-unknown-linux-gnu < %s | FileCheck %s + +; Dag-combine used to improperly combine a vector vselect of 0 and 2 into +; 2 + condition(0/1) because one of the two args was transformed from an i32->i64. + +define i16 @foo() { +; CHECK-LABEL: foo: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: li a0, 0 +; CHECK-NEXT: ret +entry: + %insert.0 = insertelement <4 x i16> zeroinitializer, i16 2, i64 0 + %all.two = shufflevector <4 x i16> %insert.0, <4 x i16> zeroinitializer, <4 x i32> zeroinitializer + %sel.0 = select <4 x i1> , <4 x i16> zeroinitializer, <4 x i16> %all.two + %mul.0 = call i16 @llvm.vector.reduce.mul.v4i16(<4 x i16> %sel.0) + ret i16 %mul.0 +} + +declare i16 @llvm.vector.reduce.mul.v4i32(<4 x i16>) -- GitLab From fc8775e2142c6bd7876831c27c3fbef0d64860bc Mon Sep 17 00:00:00 2001 From: Mircea Trofin Date: Wed, 15 May 2024 12:45:50 -0700 Subject: [PATCH 019/403] "Reapply "[ctx_profile] Profile reader and writer" (#92199)" This reverts commit 2c54bf497f7d7aecd24f4b849ee08e37a3519611. Fixed gcc-7 issue. --- .../llvm/ProfileData/PGOCtxProfReader.h | 92 +++++++ .../llvm/ProfileData/PGOCtxProfWriter.h | 91 +++++++ llvm/lib/ProfileData/CMakeLists.txt | 3 + llvm/lib/ProfileData/PGOCtxProfReader.cpp | 173 ++++++++++++ llvm/lib/ProfileData/PGOCtxProfWriter.cpp | 49 ++++ llvm/unittests/ProfileData/CMakeLists.txt | 1 + .../PGOCtxProfReaderWriterTest.cpp | 255 ++++++++++++++++++ 7 files changed, 664 insertions(+) create mode 100644 llvm/include/llvm/ProfileData/PGOCtxProfReader.h create mode 100644 llvm/include/llvm/ProfileData/PGOCtxProfWriter.h create mode 100644 llvm/lib/ProfileData/PGOCtxProfReader.cpp create mode 100644 llvm/lib/ProfileData/PGOCtxProfWriter.cpp create mode 100644 llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfReader.h b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h new file mode 100644 index 000000000000..a19b3f51d642 --- /dev/null +++ b/llvm/include/llvm/ProfileData/PGOCtxProfReader.h @@ -0,0 +1,92 @@ +//===--- PGOCtxProfReader.h - Contextual profile reader ---------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Reader for contextual iFDO profile, which comes in bitstream format. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H +#define LLVM_PROFILEDATA_CTXINSTRPROFILEREADER_H + +#include "llvm/ADT/DenseSet.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/ProfileData/PGOCtxProfWriter.h" +#include "llvm/Support/Error.h" +#include +#include + +namespace llvm { +/// The loaded contextual profile, suitable for mutation during IPO passes. We +/// generally expect a fraction of counters and of callsites to be populated. +/// We continue to model counters as vectors, but callsites are modeled as a map +/// of a map. The expectation is that, typically, there is a small number of +/// indirect targets (usually, 1 for direct calls); but potentially a large +/// number of callsites, and, as inlining progresses, the callsite count of a +/// caller will grow. +class PGOContextualProfile final { +public: + using CallTargetMapTy = std::map; + using CallsiteMapTy = DenseMap; + +private: + friend class PGOCtxProfileReader; + GlobalValue::GUID GUID = 0; + SmallVector Counters; + CallsiteMapTy Callsites; + + PGOContextualProfile(GlobalValue::GUID G, + SmallVectorImpl &&Counters) + : GUID(G), Counters(std::move(Counters)) {} + + Expected + getOrEmplace(uint32_t Index, GlobalValue::GUID G, + SmallVectorImpl &&Counters); + +public: + PGOContextualProfile(const PGOContextualProfile &) = delete; + PGOContextualProfile &operator=(const PGOContextualProfile &) = delete; + PGOContextualProfile(PGOContextualProfile &&) = default; + PGOContextualProfile &operator=(PGOContextualProfile &&) = default; + + GlobalValue::GUID guid() const { return GUID; } + const SmallVectorImpl &counters() const { return Counters; } + const CallsiteMapTy &callsites() const { return Callsites; } + CallsiteMapTy &callsites() { return Callsites; } + + bool hasCallsite(uint32_t I) const { + return Callsites.find(I) != Callsites.end(); + } + + const CallTargetMapTy &callsite(uint32_t I) const { + assert(hasCallsite(I) && "Callsite not found"); + return Callsites.find(I)->second; + } + void getContainedGuids(DenseSet &Guids) const; +}; + +class PGOCtxProfileReader final { + BitstreamCursor &Cursor; + Expected advance(); + Error readMetadata(); + Error wrongValue(const Twine &); + Error unsupported(const Twine &); + + Expected, PGOContextualProfile>> + readContext(bool ExpectIndex); + bool canReadContext(); + +public: + PGOCtxProfileReader(BitstreamCursor &Cursor) : Cursor(Cursor) {} + + Expected> loadContexts(); +}; +} // namespace llvm +#endif diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h new file mode 100644 index 000000000000..15578c51a495 --- /dev/null +++ b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h @@ -0,0 +1,91 @@ +//===- PGOCtxProfWriter.h - Contextual Profile Writer -----------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares a utility for writing a contextual profile to bitstream. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_ +#define LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_ + +#include "llvm/Bitstream/BitstreamWriter.h" +#include "llvm/ProfileData/CtxInstrContextNode.h" + +namespace llvm { +enum PGOCtxProfileRecords { Invalid = 0, Version, Guid, CalleeIndex, Counters }; + +enum PGOCtxProfileBlockIDs { + ProfileMetadataBlockID = 100, + ContextNodeBlockID = ProfileMetadataBlockID + 1 +}; + +/// Write one or more ContextNodes to the provided raw_fd_stream. +/// The caller must destroy the PGOCtxProfileWriter object before closing the +/// stream. +/// The design allows serializing a bunch of contexts embedded in some other +/// file. The overall format is: +/// +/// [... other data written to the stream...] +/// SubBlock(ProfileMetadataBlockID) +/// Version +/// SubBlock(ContextNodeBlockID) +/// [RECORDS] +/// SubBlock(ContextNodeBlockID) +/// [RECORDS] +/// [... more SubBlocks] +/// EndBlock +/// EndBlock +/// +/// The "RECORDS" are bitsream records. The IDs are in CtxProfileCodes (except) +/// for Version, which is just for metadata). All contexts will have Guid and +/// Counters, and all but the roots have CalleeIndex. The order in which the +/// records appear does not matter, but they must precede any subcontexts, +/// because that helps keep the reader code simpler. +/// +/// Subblock containment captures the context->subcontext relationship. The +/// "next()" relationship in the raw profile, between call targets of indirect +/// calls, are just modeled as peer subblocks where the callee index is the +/// same. +/// +/// Versioning: the writer may produce additional records not known by the +/// reader. The version number indicates a more structural change. +/// The current version, in particular, is set up to expect optional extensions +/// like value profiling - which would appear as additional records. For +/// example, value profiling would produce a new record with a new record ID, +/// containing the profiled values (much like the counters) +class PGOCtxProfileWriter final { + SmallVector Buff; + BitstreamWriter Writer; + + void writeCounters(const ctx_profile::ContextNode &Node); + void writeImpl(std::optional CallerIndex, + const ctx_profile::ContextNode &Node); + +public: + PGOCtxProfileWriter(raw_fd_stream &Out, + std::optional VersionOverride = std::nullopt) + : Writer(Buff, &Out, 0) { + Writer.EnterSubblock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID, + CodeLen); + const auto Version = VersionOverride ? *VersionOverride : CurrentVersion; + Writer.EmitRecord(PGOCtxProfileRecords::Version, + SmallVector({Version})); + } + + ~PGOCtxProfileWriter() { Writer.ExitBlock(); } + + void write(const ctx_profile::ContextNode &); + + // constants used in writing which a reader may find useful. + static constexpr unsigned CodeLen = 2; + static constexpr uint32_t CurrentVersion = 1; + static constexpr unsigned VBREncodingBits = 6; +}; + +} // namespace llvm +#endif diff --git a/llvm/lib/ProfileData/CMakeLists.txt b/llvm/lib/ProfileData/CMakeLists.txt index 408f9ff01ec8..4fa1b76f0a06 100644 --- a/llvm/lib/ProfileData/CMakeLists.txt +++ b/llvm/lib/ProfileData/CMakeLists.txt @@ -7,6 +7,8 @@ add_llvm_component_library(LLVMProfileData ItaniumManglingCanonicalizer.cpp MemProf.cpp MemProfReader.cpp + PGOCtxProfReader.cpp + PGOCtxProfWriter.cpp ProfileSummaryBuilder.cpp SampleProf.cpp SampleProfReader.cpp @@ -20,6 +22,7 @@ add_llvm_component_library(LLVMProfileData intrinsics_gen LINK_COMPONENTS + BitstreamReader Core Object Support diff --git a/llvm/lib/ProfileData/PGOCtxProfReader.cpp b/llvm/lib/ProfileData/PGOCtxProfReader.cpp new file mode 100644 index 000000000000..1b42d8c765f2 --- /dev/null +++ b/llvm/lib/ProfileData/PGOCtxProfReader.cpp @@ -0,0 +1,173 @@ +//===- PGOCtxProfReader.cpp - Contextual Instrumentation profile reader ---===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Read a contextual profile into a datastructure suitable for maintenance +// throughout IPO +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/PGOCtxProfReader.h" +#include "llvm/Bitstream/BitCodeEnums.h" +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/ProfileData/InstrProf.h" +#include "llvm/ProfileData/PGOCtxProfWriter.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" + +using namespace llvm; + +// FIXME(#92054) - these Error handling macros are (re-)invented in a few +// places. +#define EXPECT_OR_RET(LHS, RHS) \ + auto LHS = RHS; \ + if (!LHS) \ + return LHS.takeError(); + +#define RET_ON_ERR(EXPR) \ + if (auto Err = (EXPR)) \ + return Err; + +Expected +PGOContextualProfile::getOrEmplace(uint32_t Index, GlobalValue::GUID G, + SmallVectorImpl &&Counters) { + auto [Iter, Inserted] = Callsites[Index].insert( + {G, PGOContextualProfile(G, std::move(Counters))}); + if (!Inserted) + return make_error(instrprof_error::invalid_prof, + "Duplicate GUID for same callsite."); + return Iter->second; +} + +void PGOContextualProfile::getContainedGuids( + DenseSet &Guids) const { + Guids.insert(GUID); + for (const auto &[_, Callsite] : Callsites) + for (const auto &[_, Callee] : Callsite) + Callee.getContainedGuids(Guids); +} + +Expected PGOCtxProfileReader::advance() { + return Cursor.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); +} + +Error PGOCtxProfileReader::wrongValue(const Twine &Msg) { + return make_error(instrprof_error::invalid_prof, Msg); +} + +Error PGOCtxProfileReader::unsupported(const Twine &Msg) { + return make_error(instrprof_error::unsupported_version, Msg); +} + +bool PGOCtxProfileReader::canReadContext() { + auto Blk = advance(); + if (!Blk) { + consumeError(Blk.takeError()); + return false; + } + return Blk->Kind == BitstreamEntry::SubBlock && + Blk->ID == PGOCtxProfileBlockIDs::ContextNodeBlockID; +} + +Expected, PGOContextualProfile>> +PGOCtxProfileReader::readContext(bool ExpectIndex) { + RET_ON_ERR(Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ContextNodeBlockID)); + + std::optional Guid; + std::optional> Counters; + std::optional CallsiteIndex; + + SmallVector RecordValues; + + // We don't prescribe the order in which the records come in, and we are ok + // if other unsupported records appear. We seek in the current subblock until + // we get all we know. + auto GotAllWeNeed = [&]() { + return Guid.has_value() && Counters.has_value() && + (!ExpectIndex || CallsiteIndex.has_value()); + }; + while (!GotAllWeNeed()) { + RecordValues.clear(); + EXPECT_OR_RET(Entry, advance()); + if (Entry->Kind != BitstreamEntry::Record) + return wrongValue( + "Expected records before encountering more subcontexts"); + EXPECT_OR_RET(ReadRecord, + Cursor.readRecord(bitc::UNABBREV_RECORD, RecordValues)); + switch (*ReadRecord) { + case PGOCtxProfileRecords::Guid: + if (RecordValues.size() != 1) + return wrongValue("The GUID record should have exactly one value"); + Guid = RecordValues[0]; + break; + case PGOCtxProfileRecords::Counters: + Counters = std::move(RecordValues); + if (Counters->empty()) + return wrongValue("Empty counters. At least the entry counter (one " + "value) was expected"); + break; + case PGOCtxProfileRecords::CalleeIndex: + if (!ExpectIndex) + return wrongValue("The root context should not have a callee index"); + if (RecordValues.size() != 1) + return wrongValue("The callee index should have exactly one value"); + CallsiteIndex = RecordValues[0]; + break; + default: + // OK if we see records we do not understand, like records (profile + // components) introduced later. + break; + } + } + + PGOContextualProfile Ret(*Guid, std::move(*Counters)); + + while (canReadContext()) { + EXPECT_OR_RET(SC, readContext(true)); + auto &Targets = Ret.callsites()[*SC->first]; + auto [_, Inserted] = + Targets.insert({SC->second.guid(), std::move(SC->second)}); + if (!Inserted) + return wrongValue( + "Unexpected duplicate target (callee) at the same callsite."); + } + return std::make_pair(CallsiteIndex, std::move(Ret)); +} + +Error PGOCtxProfileReader::readMetadata() { + EXPECT_OR_RET(Blk, advance()); + if (Blk->Kind != BitstreamEntry::SubBlock) + return unsupported("Expected Version record"); + RET_ON_ERR( + Cursor.EnterSubBlock(PGOCtxProfileBlockIDs::ProfileMetadataBlockID)); + EXPECT_OR_RET(MData, advance()); + if (MData->Kind != BitstreamEntry::Record) + return unsupported("Expected Version record"); + + SmallVector Ver; + EXPECT_OR_RET(Code, Cursor.readRecord(bitc::UNABBREV_RECORD, Ver)); + if (*Code != PGOCtxProfileRecords::Version) + return unsupported("Expected Version record"); + if (Ver.size() != 1 || Ver[0] > PGOCtxProfileWriter::CurrentVersion) + return unsupported("Version " + Twine(*Code) + + " is higher than supported version " + + Twine(PGOCtxProfileWriter::CurrentVersion)); + return Error::success(); +} + +Expected> +PGOCtxProfileReader::loadContexts() { + std::map Ret; + RET_ON_ERR(readMetadata()); + while (canReadContext()) { + EXPECT_OR_RET(E, readContext(false)); + auto Key = E->second.guid(); + if (!Ret.insert({Key, std::move(E->second)}).second) + return wrongValue("Duplicate roots"); + } + return std::move(Ret); +} diff --git a/llvm/lib/ProfileData/PGOCtxProfWriter.cpp b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp new file mode 100644 index 000000000000..508179756446 --- /dev/null +++ b/llvm/lib/ProfileData/PGOCtxProfWriter.cpp @@ -0,0 +1,49 @@ +//===- PGOCtxProfWriter.cpp - Contextual Instrumentation profile writer ---===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Write a contextual profile to bitstream. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ProfileData/PGOCtxProfWriter.h" +#include "llvm/Bitstream/BitCodeEnums.h" + +using namespace llvm; +using namespace llvm::ctx_profile; + +void PGOCtxProfileWriter::writeCounters(const ContextNode &Node) { + Writer.EmitCode(bitc::UNABBREV_RECORD); + Writer.EmitVBR(PGOCtxProfileRecords::Counters, VBREncodingBits); + Writer.EmitVBR(Node.counters_size(), VBREncodingBits); + for (uint32_t I = 0U; I < Node.counters_size(); ++I) + Writer.EmitVBR64(Node.counters()[I], VBREncodingBits); +} + +// recursively write all the subcontexts. We do need to traverse depth first to +// model the context->subcontext implicitly, and since this captures call +// stacks, we don't really need to be worried about stack overflow and we can +// keep the implementation simple. +void PGOCtxProfileWriter::writeImpl(std::optional CallerIndex, + const ContextNode &Node) { + Writer.EnterSubblock(PGOCtxProfileBlockIDs::ContextNodeBlockID, CodeLen); + Writer.EmitRecord(PGOCtxProfileRecords::Guid, + SmallVector{Node.guid()}); + if (CallerIndex) + Writer.EmitRecord(PGOCtxProfileRecords::CalleeIndex, + SmallVector{*CallerIndex}); + writeCounters(Node); + for (uint32_t I = 0U; I < Node.callsites_size(); ++I) + for (const auto *Subcontext = Node.subContexts()[I]; Subcontext; + Subcontext = Subcontext->next()) + writeImpl(I, *Subcontext); + Writer.ExitBlock(); +} + +void PGOCtxProfileWriter::write(const ContextNode &RootNode) { + writeImpl(std::nullopt, RootNode); +} diff --git a/llvm/unittests/ProfileData/CMakeLists.txt b/llvm/unittests/ProfileData/CMakeLists.txt index ce3a0a45ccf1..c92642ded828 100644 --- a/llvm/unittests/ProfileData/CMakeLists.txt +++ b/llvm/unittests/ProfileData/CMakeLists.txt @@ -13,6 +13,7 @@ add_llvm_unittest(ProfileDataTests InstrProfTest.cpp ItaniumManglingCanonicalizerTest.cpp MemProfTest.cpp + PGOCtxProfReaderWriterTest.cpp SampleProfTest.cpp SymbolRemappingReaderTest.cpp ) diff --git a/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp new file mode 100644 index 000000000000..d2cdbb28e2fc --- /dev/null +++ b/llvm/unittests/ProfileData/PGOCtxProfReaderWriterTest.cpp @@ -0,0 +1,255 @@ +//===-------------- PGOCtxProfReadWriteTest.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Bitstream/BitstreamReader.h" +#include "llvm/ProfileData/CtxInstrContextNode.h" +#include "llvm/ProfileData/PGOCtxProfReader.h" +#include "llvm/ProfileData/PGOCtxProfWriter.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Testing/Support/SupportHelpers.h" +#include "gtest/gtest.h" + +using namespace llvm; +using namespace llvm::ctx_profile; + +class PGOCtxProfRWTest : public ::testing::Test { + std::vector> Nodes; + std::map Roots; + +public: + ContextNode *createNode(GUID Guid, uint32_t NrCounters, uint32_t NrCallsites, + ContextNode *Next = nullptr) { + auto AllocSize = ContextNode::getAllocSize(NrCounters, NrCallsites); + auto *Mem = Nodes.emplace_back(std::make_unique(AllocSize)).get(); + std::memset(Mem, 0, AllocSize); + auto *Ret = new (Mem) ContextNode(Guid, NrCounters, NrCallsites, Next); + return Ret; + } + + void SetUp() override { + // Root (guid 1) has 2 callsites, one used for an indirect call to either + // guid 2 or 4. + // guid 2 calls guid 5 + // guid 5 calls guid 2 + // there's also a second root, guid3. + auto *Root1 = createNode(1, 2, 2); + Root1->counters()[0] = 10; + Root1->counters()[1] = 11; + Roots.insert({1, Root1}); + auto *L1 = createNode(2, 1, 1); + L1->counters()[0] = 12; + Root1->subContexts()[1] = createNode(4, 3, 1, L1); + Root1->subContexts()[1]->counters()[0] = 13; + Root1->subContexts()[1]->counters()[1] = 14; + Root1->subContexts()[1]->counters()[2] = 15; + + auto *L3 = createNode(5, 6, 3); + for (auto I = 0; I < 6; ++I) + L3->counters()[I] = 16 + I; + L1->subContexts()[0] = L3; + L3->subContexts()[2] = createNode(2, 1, 1); + L3->subContexts()[2]->counters()[0] = 30; + auto *Root2 = createNode(3, 1, 0); + Root2->counters()[0] = 40; + Roots.insert({3, Root2}); + } + + const std::map &roots() const { return Roots; } +}; + +void checkSame(const ContextNode &Raw, const PGOContextualProfile &Profile) { + EXPECT_EQ(Raw.guid(), Profile.guid()); + ASSERT_EQ(Raw.counters_size(), Profile.counters().size()); + for (auto I = 0U; I < Raw.counters_size(); ++I) + EXPECT_EQ(Raw.counters()[I], Profile.counters()[I]); + + for (auto I = 0U; I < Raw.callsites_size(); ++I) { + if (Raw.subContexts()[I] == nullptr) + continue; + EXPECT_TRUE(Profile.hasCallsite(I)); + const auto &ProfileTargets = Profile.callsite(I); + + std::map Targets; + for (const auto *N = Raw.subContexts()[I]; N; N = N->next()) + EXPECT_TRUE(Targets.insert({N->guid(), N}).second); + + EXPECT_EQ(Targets.size(), ProfileTargets.size()); + for (auto It : Targets) { + auto PIt = ProfileTargets.find(It.second->guid()); + EXPECT_NE(PIt, ProfileTargets.end()); + checkSame(*It.second, PIt->second); + } + } +} + +TEST_F(PGOCtxProfRWTest, RoundTrip) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out); + for (auto &[_, R] : roots()) + Writer.write(*R); + } + } + { + ErrorOr> MB = + MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + ASSERT_TRUE(!!Expected); + auto &Ctxes = *Expected; + EXPECT_EQ(Ctxes.size(), roots().size()); + EXPECT_EQ(Ctxes.size(), 2U); + for (auto &[G, R] : roots()) + checkSame(*R, Ctxes.find(G)->second); + } +} + +TEST_F(PGOCtxProfRWTest, InvalidCounters) { + auto *R = createNode(1, 0, 1); + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out); + Writer.write(*R); + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); + } +} + +TEST_F(PGOCtxProfRWTest, Empty) { + BitstreamCursor Cursor(""); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); +} + +TEST_F(PGOCtxProfRWTest, Invalid) { + BitstreamCursor Cursor("Surely this is not valid"); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); +} + +TEST_F(PGOCtxProfRWTest, ValidButEmpty) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out); + // don't write anything - this will just produce the metadata subblock. + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_TRUE(!!Expected); + EXPECT_TRUE(Expected->empty()); + } +} + +TEST_F(PGOCtxProfRWTest, WrongVersion) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out, PGOCtxProfileWriter::CurrentVersion + 1); + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); + } +} + +TEST_F(PGOCtxProfRWTest, DuplicateRoots) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + PGOCtxProfileWriter Writer(Out); + Writer.write(*createNode(1, 1, 1)); + Writer.write(*createNode(1, 1, 1)); + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); + } +} + +TEST_F(PGOCtxProfRWTest, DuplicateTargets) { + llvm::unittest::TempFile ProfileFile("ctx_profile", "", "", /*Unique*/ true); + { + std::error_code EC; + raw_fd_stream Out(ProfileFile.path(), EC); + ASSERT_FALSE(EC); + { + auto *R = createNode(1, 1, 1); + auto *L1 = createNode(2, 1, 0); + auto *L2 = createNode(2, 1, 0, L1); + R->subContexts()[0] = L2; + PGOCtxProfileWriter Writer(Out); + Writer.write(*R); + } + } + { + auto MB = MemoryBuffer::getFile(ProfileFile.path()); + ASSERT_TRUE(!!MB); + ASSERT_NE(*MB, nullptr); + BitstreamCursor Cursor((*MB)->getBuffer()); + PGOCtxProfileReader Reader(Cursor); + auto Expected = Reader.loadContexts(); + EXPECT_FALSE(Expected); + consumeError(Expected.takeError()); + } +} -- GitLab From 2fb92520cba15afff6f25a1f0b959ef39912fa0a Mon Sep 17 00:00:00 2001 From: LLVM GN Syncbot Date: Wed, 15 May 2024 19:54:54 +0000 Subject: [PATCH 020/403] [gn build] Port fc8775e2142c --- llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn | 2 ++ llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn | 1 + 2 files changed, 3 insertions(+) diff --git a/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn index 9dbfe0f94c1d..c6fa142b3766 100644 --- a/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/ProfileData/BUILD.gn @@ -17,6 +17,8 @@ static_library("ProfileData") { "ItaniumManglingCanonicalizer.cpp", "MemProf.cpp", "MemProfReader.cpp", + "PGOCtxProfReader.cpp", + "PGOCtxProfWriter.cpp", "ProfileSummaryBuilder.cpp", "SampleProf.cpp", "SampleProfReader.cpp", diff --git a/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn b/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn index 4919a8089209..f45542519173 100644 --- a/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/unittests/ProfileData/BUILD.gn @@ -14,6 +14,7 @@ unittest("ProfileDataTests") { "InstrProfTest.cpp", "ItaniumManglingCanonicalizerTest.cpp", "MemProfTest.cpp", + "PGOCtxProfReaderWriterTest.cpp", "SampleProfTest.cpp", "SymbolRemappingReaderTest.cpp", ] -- GitLab From 24c39261e62d9f99bab91edf67bb9607a681b038 Mon Sep 17 00:00:00 2001 From: Alex Bradbury Date: Wed, 15 May 2024 21:04:20 +0100 Subject: [PATCH 021/403] [RISCV][test] Add tests for parsing profiles using RISCVISAInfo::parseArchString --- .../TargetParser/RISCVISAInfoTest.cpp | 63 ++++++++++++++++++- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp index 7f2d1eb8c017..d04f21fa2006 100644 --- a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp +++ b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp @@ -21,8 +21,8 @@ bool operator==(const RISCVISAUtils::ExtensionVersion &A, } TEST(ParseNormalizedArchString, RejectsInvalidChars) { - for (StringRef Input : - {"RV32", "rV64", "rv32i2P0", "rv64i2p0_A2p0", "rv32e2.0"}) { + for (StringRef Input : {"RV32", "rV64", "rv32i2P0", "rv64i2p0_A2p0", + "rv32e2.0", "rva20u64+zbc"}) { EXPECT_EQ( toString(RISCVISAInfo::parseNormalizedArchString(Input).takeError()), "string may only contain [a-z0-9_]"); @@ -667,6 +667,65 @@ TEST(ParseArchString, RejectsConflictingExtensions) { } } +TEST(ParseArchString, RejectsUnrecognizedProfileNames) { + for (StringRef Input : {"rvi23u99", "rvz23u64", "rva99u32"}) { + EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()), + "string must begin with rv32{i,e,g}, rv64{i,e,g}, or a supported " + "profile name"); + } +} + +TEST(ParseArchString, RejectsProfilesWithUnseparatedExtraExtensions) { + for (StringRef Input : {"rvi20u32m", "rvi20u64c"}) { + EXPECT_EQ(toString(RISCVISAInfo::parseArchString(Input, true).takeError()), + "additional extensions must be after separator '_'"); + } +} + +TEST(ParseArchString, AcceptsBareProfileNames) { + auto MaybeRVA20U64 = RISCVISAInfo::parseArchString("rva20u64", true); + ASSERT_THAT_EXPECTED(MaybeRVA20U64, Succeeded()); + const auto &Exts = (*MaybeRVA20U64)->getExtensions(); + EXPECT_EQ(Exts.size(), 13UL); + EXPECT_EQ(Exts.count("i"), 1U); + EXPECT_EQ(Exts.count("m"), 1U); + EXPECT_EQ(Exts.count("f"), 1U); + EXPECT_EQ(Exts.count("a"), 1U); + EXPECT_EQ(Exts.count("d"), 1U); + EXPECT_EQ(Exts.count("c"), 1U); + EXPECT_EQ(Exts.count("za128rs"), 1U); + EXPECT_EQ(Exts.count("zicntr"), 1U); + EXPECT_EQ(Exts.count("ziccif"), 1U); + EXPECT_EQ(Exts.count("zicsr"), 1U); + EXPECT_EQ(Exts.count("ziccrse"), 1U); + EXPECT_EQ(Exts.count("ziccamoa"), 1U); + EXPECT_EQ(Exts.count("zicclsm"), 1U); + + auto MaybeRVA23U64 = RISCVISAInfo::parseArchString("rva23u64", true); + ASSERT_THAT_EXPECTED(MaybeRVA23U64, Succeeded()); + EXPECT_GT((*MaybeRVA23U64)->getExtensions().size(), 13UL); +} + +TEST(ParseArchSTring, AcceptsProfileNamesWithSeparatedAdditionalExtensions) { + auto MaybeRVI20U64 = RISCVISAInfo::parseArchString("rvi20u64_m_zba", true); + ASSERT_THAT_EXPECTED(MaybeRVI20U64, Succeeded()); + const auto &Exts = (*MaybeRVI20U64)->getExtensions(); + EXPECT_EQ(Exts.size(), 3UL); + EXPECT_EQ(Exts.count("i"), 1U); + EXPECT_EQ(Exts.count("m"), 1U); + EXPECT_EQ(Exts.count("zba"), 1U); +} + +TEST(ParseArchString, + RejectsProfilesWithAdditionalExtensionsGivenAlreadyInProfile) { + // This test was added to document the current behaviour. Discussion isn't + // believed to have taken place about if this is desirable or not. + EXPECT_EQ( + toString( + RISCVISAInfo::parseArchString("rva20u64_zicntr", true).takeError()), + "duplicated standard user-level extension 'zicntr'"); +} + TEST(ToFeatures, IIsDroppedAndExperimentalExtensionsArePrefixed) { auto MaybeISAInfo1 = RISCVISAInfo::parseArchString("rv64im_ztso", true, false); -- GitLab From 891d687137ad9bb3b4efae116f9539addb5be0ea Mon Sep 17 00:00:00 2001 From: Alex Bradbury Date: Wed, 15 May 2024 21:09:43 +0100 Subject: [PATCH 022/403] [RISCV] Gate unratified profiles behind -menable-experimental-extensions (#92167) As discussed in the last sync-up call, because these profiles are not yet finalised they shouldn't be exposed to users unless they opt-in to them (much like experimental extensions). We may later want to add a more specific flag, but reusing `-menable-experimental-extensions` solves the immediate problem. This is implemented using the new support for marking profiles s experimental added in #91993 to move the unratified profiles to RISCVExperimentalProfile and making the necessary changes to logic in RISCVISAInfo to handle this. --- clang/test/Driver/riscv-profiles.c | 10 +++++-- llvm/lib/Target/RISCV/RISCVProfiles.td | 14 +++++---- llvm/lib/TargetParser/RISCVISAInfo.cpp | 29 +++++++++++++++---- llvm/test/CodeGen/RISCV/attributes.ll | 10 +++---- .../TargetParser/RISCVISAInfoTest.cpp | 13 +++++++-- 5 files changed, 55 insertions(+), 21 deletions(-) diff --git a/clang/test/Driver/riscv-profiles.c b/clang/test/Driver/riscv-profiles.c index 298f301de3fe..55aa5b398cee 100644 --- a/clang/test/Driver/riscv-profiles.c +++ b/clang/test/Driver/riscv-profiles.c @@ -111,7 +111,7 @@ // RVA22S64: "-target-feature" "+svinval" // RVA22S64: "-target-feature" "+svpbmt" -// RUN: %clang --target=riscv64 -### -c %s 2>&1 -march=rva23u64 \ +// RUN: %clang --target=riscv64 -### -c %s 2>&1 -march=rva23u64 -menable-experimental-extensions \ // RUN: | FileCheck -check-prefix=RVA23U64 %s // RVA23U64: "-target-feature" "+m" // RVA23U64: "-target-feature" "+a" @@ -207,7 +207,7 @@ // RVA23S64: "-target-feature" "+svnapot" // RVA23S64: "-target-feature" "+svpbmt" -// RUN: %clang --target=riscv64 -### -c %s 2>&1 -march=rvb23u64 \ +// RUN: %clang --target=riscv64 -### -c %s 2>&1 -march=rvb23u64 -menable-experimental-extensions \ // RUN: | FileCheck -check-prefix=RVB23U64 %s // RVB23U64: "-target-feature" "+m" // RVB23U64: "-target-feature" "+a" @@ -284,7 +284,7 @@ // RVB23S64: "-target-feature" "+svnapot" // RVB23S64: "-target-feature" "+svpbmt" -// RUN: %clang --target=riscv32 -### -c %s 2>&1 -march=rvm23u32 \ +// RUN: %clang --target=riscv32 -### -c %s 2>&1 -march=rvm23u32 -menable-experimental-extensions \ // RUN: | FileCheck -check-prefix=RVM23U32 %s // RVM23U32: "-target-feature" "+m" // RVM23U32: "-target-feature" "+zicbop" @@ -322,3 +322,7 @@ // RUN: not %clang --target=riscv64 -### -c %s 2>&1 -march=rva22u64zfa | FileCheck -check-prefix=INVALID-ADDITIONAL %s // INVALID-ADDITIONAL: error: invalid arch name 'rva22u64zfa', additional extensions must be after separator '_' + +// RUN: not %clang --target=riscv64 -### -c %s 2>&1 -march=rva23u64 | FileCheck -check-prefix=EXPERIMENTAL-NOFLAG %s +// EXPERIMENTAL-NOFLAG: error: invalid arch name 'rva23u64' +// EXPERIMENTAL-NOFLAG: requires '-menable-experimental-extensions' for profile 'rva23u64' diff --git a/llvm/lib/Target/RISCV/RISCVProfiles.td b/llvm/lib/Target/RISCV/RISCVProfiles.td index e56df33bd8cb..c4a64681f5f1 100644 --- a/llvm/lib/Target/RISCV/RISCVProfiles.td +++ b/llvm/lib/Target/RISCV/RISCVProfiles.td @@ -13,6 +13,10 @@ class RISCVProfile features> // experimental. bit Experimental = false; } +class RISCVExperimentalProfile features> + : RISCVProfile<"experimental-"#name, features> { + let Experimental = true; +} defvar RVI20U32Features = [Feature32Bit, FeatureStdExtI]; defvar RVI20U64Features = [Feature64Bit, FeatureStdExtI]; @@ -201,8 +205,8 @@ def RVA20U64 : RISCVProfile<"rva20u64", RVA20U64Features>; def RVA20S64 : RISCVProfile<"rva20s64", RVA20S64Features>; def RVA22U64 : RISCVProfile<"rva22u64", RVA22U64Features>; def RVA22S64 : RISCVProfile<"rva22s64", RVA22S64Features>; -def RVA23U64 : RISCVProfile<"rva23u64", RVA23U64Features>; -def RVA23S64 : RISCVProfile<"rva23s64", RVA23S64Features>; -def RVB23U64 : RISCVProfile<"rvb23u64", RVB23U64Features>; -def RVB23S64 : RISCVProfile<"rvb23s64", RVB23S64Features>; -def RVM23U32 : RISCVProfile<"rvm23u32", RVM23U32Features>; +def RVA23U64 : RISCVExperimentalProfile<"rva23u64", RVA23U64Features>; +def RVA23S64 : RISCVExperimentalProfile<"rva23s64", RVA23S64Features>; +def RVB23U64 : RISCVExperimentalProfile<"rvb23u64", RVB23U64Features>; +def RVB23S64 : RISCVExperimentalProfile<"rvb23s64", RVB23S64Features>; +def RVM23U32 : RISCVExperimentalProfile<"rvm23u32", RVM23U32Features>; diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp index 575c9dbad515..706b2853cd2c 100644 --- a/llvm/lib/TargetParser/RISCVISAInfo.cpp +++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp @@ -102,6 +102,10 @@ void llvm::riscvExtensionsHelp(StringMap DescMap) { for (const auto &P : SupportedProfiles) outs().indent(4) << P.Name << "\n"; + outs() << "\nExperimental Profiles\n"; + for (const auto &P : SupportedExperimentalProfiles) + outs().indent(4) << P.Name << "\n"; + outs() << "\nUse -march to specify the target's extension.\n" "For example, clang -march=rv32i_v1p0\n"; } @@ -608,12 +612,25 @@ RISCVISAInfo::parseArchString(StringRef Arch, bool EnableExperimentalExtension, XLen = 64; } else { // Try parsing as a profile. - auto I = llvm::upper_bound(SupportedProfiles, Arch, - [](StringRef Arch, const RISCVProfile &Profile) { - return Arch < Profile.Name; - }); - - if (I != std::begin(SupportedProfiles) && Arch.starts_with((--I)->Name)) { + auto ProfileCmp = [](StringRef Arch, const RISCVProfile &Profile) { + return Arch < Profile.Name; + }; + auto I = llvm::upper_bound(SupportedProfiles, Arch, ProfileCmp); + bool FoundProfile = I != std::begin(SupportedProfiles) && + Arch.starts_with(std::prev(I)->Name); + if (!FoundProfile) { + I = llvm::upper_bound(SupportedExperimentalProfiles, Arch, ProfileCmp); + FoundProfile = (I != std::begin(SupportedExperimentalProfiles) && + Arch.starts_with(std::prev(I)->Name)); + if (FoundProfile && !EnableExperimentalExtension) { + return createStringError(errc::invalid_argument, + "requires '-menable-experimental-extensions' " + "for profile '" + + std::prev(I)->Name + "'"); + } + } + if (FoundProfile) { + --I; std::string NewArch = I->MArch.str(); StringRef ArchWithoutProfile = Arch.drop_front(I->Name.size()); if (!ArchWithoutProfile.empty()) { diff --git a/llvm/test/CodeGen/RISCV/attributes.ll b/llvm/test/CodeGen/RISCV/attributes.ll index 8f49f6648ad2..953ed5ee3795 100644 --- a/llvm/test/CodeGen/RISCV/attributes.ll +++ b/llvm/test/CodeGen/RISCV/attributes.ll @@ -265,11 +265,11 @@ ; RUN: llc -mtriple=riscv64 -mattr=+rva20s64 %s -o - | FileCheck --check-prefix=RVA20S64 %s ; RUN: llc -mtriple=riscv64 -mattr=+rva22u64 %s -o - | FileCheck --check-prefix=RVA22U64 %s ; RUN: llc -mtriple=riscv64 -mattr=+rva22s64 %s -o - | FileCheck --check-prefix=RVA22S64 %s -; RUN: llc -mtriple=riscv64 -mattr=+rva23u64 %s -o - | FileCheck --check-prefix=RVA23U64 %s -; RUN: llc -mtriple=riscv64 -mattr=+rva23s64 %s -o - | FileCheck --check-prefix=RVA23S64 %s -; RUN: llc -mtriple=riscv64 -mattr=+rvb23u64 %s -o - | FileCheck --check-prefix=RVB23U64 %s -; RUN: llc -mtriple=riscv64 -mattr=+rvb23s64 %s -o - | FileCheck --check-prefix=RVB23S64 %s -; RUN: llc -mtriple=riscv32 -mattr=+rvm23u32 %s -o - | FileCheck --check-prefix=RVM23U32 %s +; RUN: llc -mtriple=riscv64 -mattr=+experimental-rva23u64 %s -o - | FileCheck --check-prefix=RVA23U64 %s +; RUN: llc -mtriple=riscv64 -mattr=+experimental-rva23s64 %s -o - | FileCheck --check-prefix=RVA23S64 %s +; RUN: llc -mtriple=riscv64 -mattr=+experimental-rvb23u64 %s -o - | FileCheck --check-prefix=RVB23U64 %s +; RUN: llc -mtriple=riscv64 -mattr=+experimental-rvb23s64 %s -o - | FileCheck --check-prefix=RVB23S64 %s +; RUN: llc -mtriple=riscv32 -mattr=+experimental-rvm23u32 %s -o - | FileCheck --check-prefix=RVM23U32 %s ; CHECK: .attribute 4, 16 diff --git a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp index d04f21fa2006..22fe31809319 100644 --- a/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp +++ b/llvm/unittests/TargetParser/RISCVISAInfoTest.cpp @@ -726,6 +726,13 @@ TEST(ParseArchString, "duplicated standard user-level extension 'zicntr'"); } +TEST(ParseArchString, + RejectsExperimentalProfilesIfEnableExperimentalExtensionsNotSet) { + EXPECT_EQ( + toString(RISCVISAInfo::parseArchString("rva23u64", false).takeError()), + "requires '-menable-experimental-extensions' for profile 'rva23u64'"); +} + TEST(ToFeatures, IIsDroppedAndExperimentalExtensionsArePrefixed) { auto MaybeISAInfo1 = RISCVISAInfo::parseArchString("rv64im_ztso", true, false); @@ -1073,12 +1080,14 @@ Supported Profiles rva20u64 rva22s64 rva22u64 + rvi20u32 + rvi20u64 + +Experimental Profiles rva23s64 rva23u64 rvb23s64 rvb23u64 - rvi20u32 - rvi20u64 rvm23u32 Use -march to specify the target's extension. -- GitLab From 80d9ae9cbf692a73404995a88665af7166c7e8ad Mon Sep 17 00:00:00 2001 From: Samira Bazuzi Date: Wed, 15 May 2024 16:11:11 -0400 Subject: [PATCH 023/403] [clang][dataflow] Fully support Environment construction for Stmt analysis. (#91616) Assume in fewer places that the analysis is of a `FunctionDecl`, and initialize the `Environment` properly for `Stmt`s. Moves constructors for `Environment` to header to make it more obvious that there are only minor differences between them and very little initialization in the constructors. Tested with check-clang-tooling. --- .../FlowSensitive/DataflowEnvironment.h | 118 +++++++++++------- .../FlowSensitive/DataflowEnvironment.cpp | 107 ++++++++-------- .../TypeErasedDataflowAnalysis.cpp | 2 +- .../FlowSensitive/DataflowEnvironmentTest.cpp | 33 +++++ .../Analysis/FlowSensitive/TestingSupport.h | 4 +- .../TypeErasedDataflowAnalysisTest.cpp | 32 +++++ 6 files changed, 198 insertions(+), 98 deletions(-) diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index cdf89c7def2c..097ff2bdfe7a 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -19,6 +19,7 @@ #include "clang/AST/DeclBase.h" #include "clang/AST/Expr.h" #include "clang/AST/Type.h" +#include "clang/Analysis/FlowSensitive/ASTOps.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/Formula.h" @@ -30,9 +31,11 @@ #include "llvm/ADT/MapVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" +#include #include #include #include +#include namespace clang { namespace dataflow { @@ -155,7 +158,28 @@ public: /// Creates an environment that uses `DACtx` to store objects that encompass /// the state of a program. - explicit Environment(DataflowAnalysisContext &DACtx); + explicit Environment(DataflowAnalysisContext &DACtx) + : DACtx(&DACtx), + FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {} + + /// Creates an environment that uses `DACtx` to store objects that encompass + /// the state of a program, with `S` as the statement to analyze. + Environment(DataflowAnalysisContext &DACtx, Stmt &S) : Environment(DACtx) { + InitialTargetStmt = &S; + } + + /// Creates an environment that uses `DACtx` to store objects that encompass + /// the state of a program, with `FD` as the function to analyze. + /// + /// Requirements: + /// + /// The function must have a body, i.e. + /// `FunctionDecl::doesThisDecalarationHaveABody()` must be true. + Environment(DataflowAnalysisContext &DACtx, const FunctionDecl &FD) + : Environment(DACtx, *FD.getBody()) { + assert(FD.doesThisDeclarationHaveABody()); + InitialTargetFunc = &FD; + } // Copy-constructor is private, Environments should not be copied. See fork(). Environment &operator=(const Environment &Other) = delete; @@ -163,24 +187,11 @@ public: Environment(Environment &&Other) = default; Environment &operator=(Environment &&Other) = default; - /// Creates an environment that uses `DACtx` to store objects that encompass - /// the state of a program. - /// - /// If `DeclCtx` is a function, initializes the environment with symbolic - /// representations of the function parameters. - /// - /// If `DeclCtx` is a non-static member function, initializes the environment - /// with a symbolic representation of the `this` pointee. - Environment(DataflowAnalysisContext &DACtx, const DeclContext &DeclCtx); - /// Assigns storage locations and values to all parameters, captures, global - /// variables, fields and functions referenced in the function currently being - /// analyzed. - /// - /// Requirements: + /// variables, fields and functions referenced in the `Stmt` or `FunctionDecl` + /// passed to the constructor. /// - /// The function must have a body, i.e. - /// `FunctionDecl::doesThisDecalarationHaveABody()` must be true. + /// If no `Stmt` or `FunctionDecl` was supplied, this function does nothing. void initialize(); /// Returns a new environment that is a copy of this one. @@ -193,7 +204,7 @@ public: /// forked flow condition references the original). Environment fork() const; - /// Creates and returns an environment to use for an inline analysis of the + /// Creates and returns an environment to use for an inline analysis of the /// callee. Uses the storage location from each argument in the `Call` as the /// storage location for the corresponding parameter in the callee. /// @@ -365,46 +376,51 @@ public: RecordStorageLocation & getResultObjectLocation(const Expr &RecordPRValue) const; - /// Returns the return value of the current function. This can be null if: + /// Returns the return value of the function currently being analyzed. + /// This can be null if: /// - The function has a void return type /// - No return value could be determined for the function, for example /// because it calls a function without a body. /// /// Requirements: - /// The current function must have a non-reference return type. + /// The current analysis target must be a function and must have a + /// non-reference return type. Value *getReturnValue() const { assert(getCurrentFunc() != nullptr && !getCurrentFunc()->getReturnType()->isReferenceType()); return ReturnVal; } - /// Returns the storage location for the reference returned by the current - /// function. This can be null if function doesn't return a single consistent - /// reference. + /// Returns the storage location for the reference returned by the function + /// currently being analyzed. This can be null if the function doesn't return + /// a single consistent reference. /// /// Requirements: - /// The current function must have a reference return type. + /// The current analysis target must be a function and must have a reference + /// return type. StorageLocation *getReturnStorageLocation() const { assert(getCurrentFunc() != nullptr && getCurrentFunc()->getReturnType()->isReferenceType()); return ReturnLoc; } - /// Sets the return value of the current function. + /// Sets the return value of the function currently being analyzed. /// /// Requirements: - /// The current function must have a non-reference return type. + /// The current analysis target must be a function and must have a + /// non-reference return type. void setReturnValue(Value *Val) { assert(getCurrentFunc() != nullptr && !getCurrentFunc()->getReturnType()->isReferenceType()); ReturnVal = Val; } - /// Sets the storage location for the reference returned by the current - /// function. + /// Sets the storage location for the reference returned by the function + /// currently being analyzed. /// /// Requirements: - /// The current function must have a reference return type. + /// The current analysis target must be a function and must have a reference + /// return type. void setReturnStorageLocation(StorageLocation *Loc) { assert(getCurrentFunc() != nullptr && getCurrentFunc()->getReturnType()->isReferenceType()); @@ -641,23 +657,21 @@ public: /// (or the flow condition is overly constraining) or if the solver times out. bool allows(const Formula &) const; - /// Returns the `DeclContext` of the block being analysed, if any. Otherwise, - /// returns null. - const DeclContext *getDeclCtx() const { return CallStack.back(); } - /// Returns the function currently being analyzed, or null if the code being /// analyzed isn't part of a function. const FunctionDecl *getCurrentFunc() const { - return dyn_cast(getDeclCtx()); + return CallStack.empty() ? InitialTargetFunc : CallStack.back(); } - /// Returns the size of the call stack. + /// Returns the size of the call stack, not counting the initial analysis + /// target. size_t callStackSize() const { return CallStack.size(); } /// Returns whether this `Environment` can be extended to analyze the given - /// `Callee` (i.e. if `pushCall` can be used), with recursion disallowed and a - /// given `MaxDepth`. - bool canDescend(unsigned MaxDepth, const DeclContext *Callee) const; + /// `Callee` (i.e. if `pushCall` can be used). + /// Recursion is not allowed. `MaxDepth` is the maximum size of the call stack + /// (i.e. the maximum value that `callStackSize()` may assume after the call). + bool canDescend(unsigned MaxDepth, const FunctionDecl *Callee) const; /// Returns the `DataflowAnalysisContext` used by the environment. DataflowAnalysisContext &getDataflowAnalysisContext() const { return *DACtx; } @@ -719,8 +733,8 @@ private: ArrayRef Args); /// Assigns storage locations and values to all global variables, fields - /// and functions referenced in `FuncDecl`. `FuncDecl` must have a body. - void initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl); + /// and functions in `Referenced`. + void initFieldsGlobalsAndFuncs(const ReferencedDecls &Referenced); static PrValueToResultObject buildResultObjectMap(DataflowAnalysisContext *DACtx, @@ -728,6 +742,11 @@ private: RecordStorageLocation *ThisPointeeLoc, RecordStorageLocation *LocForRecordReturnVal); + static PrValueToResultObject + buildResultObjectMap(DataflowAnalysisContext *DACtx, Stmt *S, + RecordStorageLocation *ThisPointeeLoc, + RecordStorageLocation *LocForRecordReturnVal); + // `DACtx` is not null and not owned by this object. DataflowAnalysisContext *DACtx; @@ -736,11 +755,20 @@ private: // shared between environments in the same call. // https://github.com/llvm/llvm-project/issues/59005 - // `DeclContext` of the block being analysed if provided. - std::vector CallStack; + // The stack of functions called from the initial analysis target. + std::vector CallStack; + + // Initial function to analyze, if a function was passed to the constructor. + // Null otherwise. + const FunctionDecl *InitialTargetFunc = nullptr; + // Top-level statement of the initial analysis target. + // If a function was passed to the constructor, this is its body. + // If a statement was passed to the constructor, this is that statement. + // Null if no analysis target was passed to the constructor. + Stmt *InitialTargetStmt = nullptr; // Maps from prvalues of record type to their result objects. Shared between - // all environments for the same function. + // all environments for the same analysis target. // FIXME: It's somewhat unsatisfactory that we have to use a `shared_ptr` // here, though the cost is acceptable: The overhead of a `shared_ptr` is // incurred when it is copied, and this happens only relatively rarely (when @@ -749,7 +777,7 @@ private: std::shared_ptr ResultObjectMap; // The following three member variables handle various different types of - // return values. + // return values when the current analysis target is a function. // - If the return type is not a reference and not a record: Value returned // by the function. Value *ReturnVal = nullptr; @@ -762,7 +790,7 @@ private: RecordStorageLocation *LocForRecordReturnVal = nullptr; // The storage location of the `this` pointee. Should only be null if the - // function being analyzed is only a function and not a method. + // analysis target is not a method. RecordStorageLocation *ThisPointeeLoc = nullptr; // Maps from declarations and glvalue expression to storage locations that are diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index cb6c8b2ef107..338a85525b38 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -16,17 +16,22 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/Stmt.h" #include "clang/AST/Type.h" #include "clang/Analysis/FlowSensitive/ASTOps.h" +#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" #include "clang/Analysis/FlowSensitive/DataflowLattice.h" #include "clang/Analysis/FlowSensitive/Value.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" +#include "llvm/ADT/PointerUnion.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/Support/ErrorHandling.h" +#include #include +#include #include #define DEBUG_TYPE "dataflow" @@ -290,15 +295,14 @@ widenKeyToValueMap(const llvm::MapVector &CurMap, namespace { // Visitor that builds a map from record prvalues to result objects. -// This traverses the body of the function to be analyzed; for each result -// object that it encounters, it propagates the storage location of the result -// object to all record prvalues that can initialize it. +// For each result object that it encounters, it propagates the storage location +// of the result object to all record prvalues that can initialize it. class ResultObjectVisitor : public RecursiveASTVisitor { public: // `ResultObjectMap` will be filled with a map from record prvalues to result - // object. If the function being analyzed returns a record by value, - // `LocForRecordReturnVal` is the location to which this record should be - // written; otherwise, it is null. + // object. If this visitor will traverse a function that returns a record by + // value, `LocForRecordReturnVal` is the location to which this record should + // be written; otherwise, it is null. explicit ResultObjectVisitor( llvm::DenseMap &ResultObjectMap, RecordStorageLocation *LocForRecordReturnVal, @@ -514,39 +518,31 @@ private: } // namespace -Environment::Environment(DataflowAnalysisContext &DACtx) - : DACtx(&DACtx), - FlowConditionToken(DACtx.arena().makeFlowConditionToken()) {} - -Environment::Environment(DataflowAnalysisContext &DACtx, - const DeclContext &DeclCtx) - : Environment(DACtx) { - CallStack.push_back(&DeclCtx); -} - void Environment::initialize() { - const DeclContext *DeclCtx = getDeclCtx(); - if (DeclCtx == nullptr) + if (InitialTargetStmt == nullptr) return; - const auto *FuncDecl = dyn_cast(DeclCtx); - if (FuncDecl == nullptr) + if (InitialTargetFunc == nullptr) { + initFieldsGlobalsAndFuncs(getReferencedDecls(*InitialTargetStmt)); + ResultObjectMap = + std::make_shared(buildResultObjectMap( + DACtx, InitialTargetStmt, getThisPointeeStorageLocation(), + /*LocForRecordReturnValue=*/nullptr)); return; + } - assert(FuncDecl->doesThisDeclarationHaveABody()); - - initFieldsGlobalsAndFuncs(FuncDecl); + initFieldsGlobalsAndFuncs(getReferencedDecls(*InitialTargetFunc)); - for (const auto *ParamDecl : FuncDecl->parameters()) { + for (const auto *ParamDecl : InitialTargetFunc->parameters()) { assert(ParamDecl != nullptr); setStorageLocation(*ParamDecl, createObject(*ParamDecl, nullptr)); } - if (FuncDecl->getReturnType()->isRecordType()) + if (InitialTargetFunc->getReturnType()->isRecordType()) LocForRecordReturnVal = &cast( - createStorageLocation(FuncDecl->getReturnType())); + createStorageLocation(InitialTargetFunc->getReturnType())); - if (const auto *MethodDecl = dyn_cast(DeclCtx)) { + if (const auto *MethodDecl = dyn_cast(InitialTargetFunc)) { auto *Parent = MethodDecl->getParent(); assert(Parent != nullptr); @@ -558,7 +554,7 @@ void Environment::initialize() { setStorageLocation(*VarDecl, createObject(*VarDecl, nullptr)); } else if (Capture.capturesThis()) { const auto *SurroundingMethodDecl = - cast(DeclCtx->getNonClosureAncestor()); + cast(InitialTargetFunc->getNonClosureAncestor()); QualType ThisPointeeType = SurroundingMethodDecl->getFunctionObjectParameterType(); setThisPointeeStorageLocation( @@ -580,18 +576,16 @@ void Environment::initialize() { // We do this below the handling of `CXXMethodDecl` above so that we can // be sure that the storage location for `this` has been set. - ResultObjectMap = std::make_shared( - buildResultObjectMap(DACtx, FuncDecl, getThisPointeeStorageLocation(), - LocForRecordReturnVal)); + ResultObjectMap = + std::make_shared(buildResultObjectMap( + DACtx, InitialTargetFunc, getThisPointeeStorageLocation(), + LocForRecordReturnVal)); } -// FIXME: Add support for resetting globals after function calls to enable -// the implementation of sound analyses. -void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { - assert(FuncDecl->doesThisDeclarationHaveABody()); - - ReferencedDecls Referenced = getReferencedDecls(*FuncDecl); +// FIXME: Add support for resetting globals after function calls to enable the +// implementation of sound analyses. +void Environment::initFieldsGlobalsAndFuncs(const ReferencedDecls &Referenced) { // These have to be added before the lines that follow to ensure that // `create*` work correctly for structs. DACtx->addModeledFields(Referenced.Fields); @@ -602,9 +596,9 @@ void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { // We don't run transfer functions on the initializers of global variables, // so they won't be associated with a value or storage location. We - // therefore intentionally don't pass an initializer to `createObject()`; - // in particular, this ensures that `createObject()` will initialize the - // fields of record-type variables with values. + // therefore intentionally don't pass an initializer to `createObject()`; in + // particular, this ensures that `createObject()` will initialize the fields + // of record-type variables with values. setStorageLocation(*D, createObject(*D, nullptr)); } @@ -623,8 +617,8 @@ Environment Environment::fork() const { } bool Environment::canDescend(unsigned MaxDepth, - const DeclContext *Callee) const { - return CallStack.size() <= MaxDepth && !llvm::is_contained(CallStack, Callee); + const FunctionDecl *Callee) const { + return CallStack.size() < MaxDepth && !llvm::is_contained(CallStack, Callee); } Environment Environment::pushCall(const CallExpr *Call) const { @@ -671,7 +665,7 @@ void Environment::pushCallInternal(const FunctionDecl *FuncDecl, CallStack.push_back(FuncDecl); - initFieldsGlobalsAndFuncs(FuncDecl); + initFieldsGlobalsAndFuncs(getReferencedDecls(*FuncDecl)); const auto *ParamIt = FuncDecl->param_begin(); @@ -755,6 +749,8 @@ LatticeEffect Environment::widen(const Environment &PrevEnv, assert(ThisPointeeLoc == PrevEnv.ThisPointeeLoc); assert(CallStack == PrevEnv.CallStack); assert(ResultObjectMap == PrevEnv.ResultObjectMap); + assert(InitialTargetFunc == PrevEnv.InitialTargetFunc); + assert(InitialTargetStmt == PrevEnv.InitialTargetStmt); auto Effect = LatticeEffect::Unchanged; @@ -790,6 +786,8 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB, assert(EnvA.ThisPointeeLoc == EnvB.ThisPointeeLoc); assert(EnvA.CallStack == EnvB.CallStack); assert(EnvA.ResultObjectMap == EnvB.ResultObjectMap); + assert(EnvA.InitialTargetFunc == EnvB.InitialTargetFunc); + assert(EnvA.InitialTargetStmt == EnvB.InitialTargetStmt); Environment JoinedEnv(*EnvA.DACtx); @@ -797,14 +795,13 @@ Environment Environment::join(const Environment &EnvA, const Environment &EnvB, JoinedEnv.ResultObjectMap = EnvA.ResultObjectMap; JoinedEnv.LocForRecordReturnVal = EnvA.LocForRecordReturnVal; JoinedEnv.ThisPointeeLoc = EnvA.ThisPointeeLoc; + JoinedEnv.InitialTargetFunc = EnvA.InitialTargetFunc; + JoinedEnv.InitialTargetStmt = EnvA.InitialTargetStmt; - if (EnvA.CallStack.empty()) { + const FunctionDecl *Func = EnvA.getCurrentFunc(); + if (!Func) { JoinedEnv.ReturnVal = nullptr; } else { - // FIXME: Make `CallStack` a vector of `FunctionDecl` so we don't need this - // cast. - auto *Func = dyn_cast(EnvA.CallStack.back()); - assert(Func != nullptr); JoinedEnv.ReturnVal = joinValues(Func->getReturnType(), EnvA.ReturnVal, EnvA, EnvB.ReturnVal, EnvB, JoinedEnv, Model); @@ -1229,16 +1226,26 @@ Environment::PrValueToResultObject Environment::buildResultObjectMap( RecordStorageLocation *LocForRecordReturnVal) { assert(FuncDecl->doesThisDeclarationHaveABody()); - PrValueToResultObject Map; + PrValueToResultObject Map = buildResultObjectMap( + DACtx, FuncDecl->getBody(), ThisPointeeLoc, LocForRecordReturnVal); ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx); if (const auto *Ctor = dyn_cast(FuncDecl)) Visitor.TraverseConstructorInits(Ctor, ThisPointeeLoc); - Visitor.TraverseStmt(FuncDecl->getBody()); return Map; } +Environment::PrValueToResultObject Environment::buildResultObjectMap( + DataflowAnalysisContext *DACtx, Stmt *S, + RecordStorageLocation *ThisPointeeLoc, + RecordStorageLocation *LocForRecordReturnVal) { + PrValueToResultObject Map; + ResultObjectVisitor Visitor(Map, LocForRecordReturnVal, *DACtx); + Visitor.TraverseStmt(S); + return Map; +} + RecordStorageLocation *getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env) { Expr *ImplicitObject = MCE.getImplicitObjectArgument(); diff --git a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp index 12eff4dd4b78..675b42550f17 100644 --- a/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp +++ b/clang/lib/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.cpp @@ -476,7 +476,7 @@ runTypeErasedDataflowAnalysis( PrettyStackTraceAnalysis CrashInfo(ACFG, "runTypeErasedDataflowAnalysis"); std::optional MaybeStartingEnv; - if (InitEnv.callStackSize() == 1) { + if (InitEnv.callStackSize() == 0) { MaybeStartingEnv = InitEnv.fork(); MaybeStartingEnv->initialize(); } diff --git a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp index 419564816124..bd710a00c47c 100644 --- a/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp @@ -9,6 +9,8 @@ #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" #include "TestingSupport.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Stmt.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" @@ -403,4 +405,35 @@ TEST_F(EnvironmentTest, Contains(Member)); } +TEST_F(EnvironmentTest, Stmt) { + using namespace ast_matchers; + + std::string Code = R"cc( + struct S { int i; }; + void foo() { + S AnS = S{1}; + } + )cc"; + auto Unit = + tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"}); + auto &Context = Unit->getASTContext(); + + ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U); + + auto *DeclStatement = const_cast(selectFirst( + "d", match(declStmt(hasSingleDecl(varDecl(hasName("AnS")))).bind("d"), + Context))); + ASSERT_THAT(DeclStatement, NotNull()); + auto *Init = (cast(*DeclStatement->decl_begin()))->getInit(); + ASSERT_THAT(Init, NotNull()); + + // Verify that we can retrieve the result object location for the initializer + // expression when we analyze the DeclStmt for `AnS`. + Environment Env(DAContext, *DeclStatement); + // Don't crash when initializing. + Env.initialize(); + // And don't crash when retrieving the result object location. + Env.getResultObjectLocation(*Init); +} + } // namespace diff --git a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h index 3b0e05ed7222..7348f8b1740d 100644 --- a/clang/unittests/Analysis/FlowSensitive/TestingSupport.h +++ b/clang/unittests/Analysis/FlowSensitive/TestingSupport.h @@ -355,8 +355,8 @@ checkDataflow(AnalysisInputs AI, auto SetupTest = [&StmtToAnnotations, PrevSetupTest = std::move(AI.SetupTest)]( AnalysisOutputs &AO) -> llvm::Error { - auto MaybeStmtToAnnotations = buildStatementToAnnotationMapping( - cast(AO.InitEnv.getDeclCtx()), AO.Code); + auto MaybeStmtToAnnotations = + buildStatementToAnnotationMapping(AO.InitEnv.getCurrentFunc(), AO.Code); if (!MaybeStmtToAnnotations) { return MaybeStmtToAnnotations.takeError(); } diff --git a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp index b0b579d2bc19..1a52b82d6566 100644 --- a/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TypeErasedDataflowAnalysisTest.cpp @@ -146,6 +146,38 @@ TEST_F(DataflowAnalysisTest, DiagnoseFunctionDiagnoserCalledOnEachElement) { " (Lifetime ends)\n"))); } +TEST_F(DataflowAnalysisTest, CanAnalyzeStmt) { + std::string Code = R"cc( + struct S { bool b; }; + void foo() { + S AnS = S{true}; + } + )cc"; + AST = tooling::buildASTFromCodeWithArgs(Code, {"-std=c++11"}); + const auto &DeclStatement = + matchNode(declStmt(hasSingleDecl(varDecl(hasName("AnS"))))); + const auto &Func = matchNode(functionDecl(hasName("foo"))); + + ACFG = std::make_unique(llvm::cantFail(AdornedCFG::build( + Func, const_cast(DeclStatement), AST->getASTContext()))); + + NoopAnalysis Analysis = NoopAnalysis(AST->getASTContext()); + DACtx = std::make_unique( + std::make_unique()); + Environment Env(*DACtx, const_cast(DeclStatement)); + + llvm::Expected>>> + Results = runDataflowAnalysis(*ACFG, Analysis, Env); + + ASSERT_THAT_ERROR(Results.takeError(), llvm::Succeeded()); + const Environment &ExitBlockEnv = Results->front()->Env; + BoolValue *BoolFieldValue = cast( + getFieldValue(ExitBlockEnv.get( + *cast((*DeclStatement.decl_begin()))), + "b", AST->getASTContext(), ExitBlockEnv)); + EXPECT_TRUE(Env.proves(BoolFieldValue->formula())); +} + // Tests for the statement-to-block map. using StmtToBlockTest = DataflowAnalysisTest; -- GitLab From ee765b0c94df7e636d9739216b1646d3a2d3b5db Mon Sep 17 00:00:00 2001 From: Tim Besard Date: Wed, 15 May 2024 22:17:29 +0200 Subject: [PATCH 024/403] [NewPM] Add pass options for `InternalizePass` to preserve GVs. (#91334) This PR adds a string interface to `InternalizePass`' `MustPreserveGV` option, which is a callback function to indicate if a GV is not to be internalized. This is for use in LLVM.jl, the Julia wrapper for LLVM, which uses the C API and is thus required to use the PassBuilder string API for building NewPM pipelines. --- llvm/lib/Passes/PassBuilder.cpp | 18 ++++++++++++++++++ llvm/lib/Passes/PassRegistry.def | 15 ++++++++++++++- llvm/test/Transforms/Internalize/lists.ll | 5 +++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index e4131706aba0..91c5b65c0351 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -1142,6 +1142,24 @@ Expected parseGlobalMergeOptions(StringRef Params) { return Result; } +Expected> parseInternalizeGVs(StringRef Params) { + SmallVector PreservedGVs; + while (!Params.empty()) { + StringRef ParamName; + std::tie(ParamName, Params) = Params.split(';'); + + if (ParamName.consume_front("preserve-gv=")) { + PreservedGVs.push_back(ParamName.str()); + } else { + return make_error( + formatv("invalid Internalize pass parameter '{0}' ", ParamName).str(), + inconvertibleErrorCode()); + } + } + + return PreservedGVs; +} + } // namespace /// Tests whether a pass name starts with a valid prefix for a default pipeline diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index e5ce6cb7da64..50682ca4970f 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -78,7 +78,6 @@ MODULE_PASS("insert-gcov-profiling", GCOVProfilerPass()) MODULE_PASS("instrorderfile", InstrOrderFilePass()) MODULE_PASS("instrprof", InstrProfilingLoweringPass()) MODULE_PASS("ctx-instr-lower", PGOCtxProfLoweringPass()) -MODULE_PASS("internalize", InternalizePass()) MODULE_PASS("invalidate", InvalidateAllAnalysesPass()) MODULE_PASS("iroutliner", IROutlinerPass()) MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass()) @@ -175,6 +174,20 @@ MODULE_PASS_WITH_PARAMS( "hwasan", "HWAddressSanitizerPass", [](HWAddressSanitizerOptions Opts) { return HWAddressSanitizerPass(Opts); }, parseHWASanPassOptions, "kernel;recover") +MODULE_PASS_WITH_PARAMS( + "internalize", "InternalizePass", + [](SmallVector PreservedGVs) { + if (PreservedGVs.empty()) + return InternalizePass(); + auto MustPreserveGV = [=](const GlobalValue &GV) { + for (auto &PreservedGV : PreservedGVs) + if (GV.getName() == PreservedGV) + return true; + return false; + }; + return InternalizePass(MustPreserveGV); + }, + parseInternalizeGVs, "preserve-gv=GV") MODULE_PASS_WITH_PARAMS( "ipsccp", "IPSCCPPass", [](IPSCCPOptions Opts) { return IPSCCPPass(Opts); }, parseIPSCCPOptions, "no-func-spec;func-spec") diff --git a/llvm/test/Transforms/Internalize/lists.ll b/llvm/test/Transforms/Internalize/lists.ll index df408f906b78..83dad03d75ea 100644 --- a/llvm/test/Transforms/Internalize/lists.ll +++ b/llvm/test/Transforms/Internalize/lists.ll @@ -13,6 +13,11 @@ ; -file and -list options should be merged, the apifile contains foo and j ; RUN: opt < %s -passes=internalize -internalize-public-api-list bar -internalize-public-api-file %S/apifile -S | FileCheck --check-prefix=FOO_J_AND_BAR %s +; specifying through pass builder option +; RUN: opt < %s -passes='internalize' -S | FileCheck --check-prefix=FOO_AND_J %s +; RUN: opt < %s -passes='internalize' -S | FileCheck --check-prefix=FOO_AND_BAR %s +; RUN: opt < %s -passes='internalize' -S | FileCheck --check-prefix=FOO_J_AND_BAR %s + ; ALL: @i = internal global ; FOO_AND_J: @i = internal global ; FOO_AND_BAR: @i = internal global -- GitLab From ec1f28dc97ce22ba5b3e6f95ff84414dfbda46b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolai=20H=C3=A4hnle?= Date: Wed, 15 May 2024 22:23:18 +0200 Subject: [PATCH 025/403] AMDGPU/gfx12: avoid crashing on legacy waitcnt intrinsics (#92306) They *are* still accepted by the HW but have a conservative effect. Leave them untouched since handling them would complicate the logic a bit, and developers who code to such a low level really need to revisit what they're doing anyway. --- llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp | 5 + .../CodeGen/AMDGPU/waitcnt-preexisting.mir | 175 ++++++++++++++++++ 2 files changed, 180 insertions(+) diff --git a/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp b/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp index 839ac927a0ee..5577ce9eb128 100644 --- a/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp +++ b/llvm/lib/Target/AMDGPU/SIInsertWaitcnts.cpp @@ -1364,6 +1364,11 @@ bool WaitcntGeneratorGFX12Plus::applyPreexistingWaitcnt( unsigned Opcode = SIInstrInfo::getNonSoftWaitcntOpcode(II.getOpcode()); bool TrySimplify = Opcode != II.getOpcode() && !OptNone; + // Don't crash if the programmer used legacy waitcnt intrinsics, but don't + // attempt to do more than that either. + if (Opcode == AMDGPU::S_WAITCNT) + continue; + if (Opcode == AMDGPU::S_WAIT_LOADCNT_DSCNT) { unsigned OldEnc = TII->getNamedOperand(II, AMDGPU::OpName::simm16)->getImm(); diff --git a/llvm/test/CodeGen/AMDGPU/waitcnt-preexisting.mir b/llvm/test/CodeGen/AMDGPU/waitcnt-preexisting.mir index 4c01786e45f5..e15814210dfd 100644 --- a/llvm/test/CodeGen/AMDGPU/waitcnt-preexisting.mir +++ b/llvm/test/CodeGen/AMDGPU/waitcnt-preexisting.mir @@ -1,5 +1,12 @@ # NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py # RUN: llc -mtriple=amdgcn -mcpu=gfx908 -verify-machineinstrs -run-pass si-insert-waitcnts -o - %s | FileCheck -check-prefixes=GFX9 %s +# RUN: llc -mtriple=amdgcn -mcpu=gfx1200 -verify-machineinstrs -run-pass si-insert-waitcnts -o - %s | FileCheck -check-prefixes=GFX12 %s + +# For gfx12+, this test simply ensures that we don't crash in the face of manually +# inserted waitcnt intrinsics. They are still allowed for compatibility, but +# their effect in the HW is very conservative and code generation does not attempt +# to do anything with them. Developers who write code at such a low level should +# revisit their code for gfx12+ anyway. --- name: test_waitcnt_preexisting_lgkmcnt_unmodified @@ -17,6 +24,22 @@ body: | ; GFX9-NEXT: S_WAITCNT 112 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_ENDPGM 0 + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_lgkmcnt_unmodified + ; GFX12: liveins: $vgpr0 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0_vgpr1 = DS_READ2_B32 $vgpr0, 0, 1, 0, implicit $m0, implicit $exec + ; GFX12-NEXT: S_WAITCNT 49279 + ; GFX12-NEXT: S_WAIT_DSCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_ENDPGM 0 $vgpr0_vgpr1 = DS_READ2_B32 $vgpr0, 0, 1, 0, implicit $m0, implicit $exec S_WAITCNT 49279 $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr @@ -40,6 +63,22 @@ body: | ; GFX9-NEXT: S_WAITCNT 112 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_ENDPGM 0 + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_vmcnt_unmodified + ; GFX12: liveins: $vgpr0_vgpr1 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0_vgpr1 = GLOBAL_LOAD_DWORDX2 $vgpr0_vgpr1, 0, 0, implicit $exec + ; GFX12-NEXT: S_WAITCNT 3952 + ; GFX12-NEXT: S_WAIT_LOADCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_ENDPGM 0 $vgpr0_vgpr1 = GLOBAL_LOAD_DWORDX2 $vgpr0_vgpr1, 0, 0, implicit $exec S_WAITCNT 3952 $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr @@ -65,6 +104,22 @@ body: | ; GFX9-NEXT: S_WAITCNT 112 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_ENDPGM 0 + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_vmcnt_needs_lgkmcnt + ; GFX12: liveins: $vgpr0 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0_vgpr1 = DS_READ2_B32 $vgpr0, 0, 1, 0, implicit $m0, implicit $exec + ; GFX12-NEXT: S_WAITCNT 3952 + ; GFX12-NEXT: S_WAIT_DSCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_ENDPGM 0 $vgpr0_vgpr1 = DS_READ2_B32 $vgpr0, 0, 1, 0, implicit $m0, implicit $exec S_WAITCNT 3952 $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr @@ -88,6 +143,22 @@ body: | ; GFX9-NEXT: S_WAITCNT 112 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_ENDPGM 0 + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_lgkmcnt_needs_vmcnt + ; GFX12: liveins: $vgpr0_vgpr1 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0_vgpr1 = GLOBAL_LOAD_DWORDX2 $vgpr0_vgpr1, 0, 0, implicit $exec + ; GFX12-NEXT: S_WAITCNT 49279 + ; GFX12-NEXT: S_WAIT_LOADCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_ENDPGM 0 $vgpr0_vgpr1 = GLOBAL_LOAD_DWORDX2 $vgpr0_vgpr1, 0, 0, implicit $exec S_WAITCNT 49279 $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr @@ -115,6 +186,24 @@ body: | ; GFX9-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr4_vgpr5, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_WAITCNT 112 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_apply_all_counters + ; GFX12: liveins: $vgpr0_vgpr1, $vgpr2 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr4_vgpr5 = GLOBAL_LOAD_DWORDX2 $vgpr0_vgpr1, 0, 0, implicit $exec + ; GFX12-NEXT: $vgpr6_vgpr7 = DS_READ2_B32 $vgpr2, 0, 1, 0, implicit $m0, implicit $exec + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_WAIT_DSCNT 0 + ; GFX12-NEXT: $vgpr6 = V_OR_B32_e32 1, killed $vgpr6, implicit $exec + ; GFX12-NEXT: S_WAIT_LOADCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr4_vgpr5, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr $vgpr4_vgpr5 = GLOBAL_LOAD_DWORDX2 $vgpr0_vgpr1, 0, 0, implicit $exec $vgpr6_vgpr7 = DS_READ2_B32 $vgpr2, 0, 1, 0, implicit $m0, implicit $exec S_WAITCNT 0 @@ -136,6 +225,24 @@ body: | ; GFX9-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_WAITCNT 0 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_combine_waitcnt + ; GFX12: liveins: $vgpr0_vgpr1 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr S_WAITCNT 0 S_WAITCNT 0 @@ -159,6 +266,20 @@ body: | ; GFX9-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_WAITCNT 112 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_combine_waitcnt_diff_counters + ; GFX12: liveins: $vgpr0_vgpr1 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAITCNT 49279 + ; GFX12-NEXT: S_WAITCNT 3952 + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr S_WAITCNT 49279 S_WAITCNT 3952 @@ -185,6 +306,23 @@ body: | ; GFX9-NEXT: S_NOP 0 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_ENDPGM 0 + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_early_wait + ; GFX12: liveins: $vgpr0_vgpr1 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_NOP 0 + ; GFX12-NEXT: S_NOP 0 + ; GFX12-NEXT: S_NOP 0 + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_ENDPGM 0 $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr S_WAITCNT 0 S_NOP 0 @@ -207,6 +345,18 @@ body: | ; GFX9-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_WAITCNT 3952 ; GFX9-NEXT: KILL $vgpr0 + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_ignore_kill + ; GFX12: liveins: $vgpr0_vgpr1 + ; GFX12-NEXT: {{ $}} + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAITCNT 3952 + ; GFX12-NEXT: KILL $vgpr0 $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr S_WAITCNT 3952 KILL $vgpr0 @@ -221,6 +371,15 @@ body: | ; GFX9-LABEL: name: test_waitcnt_preexisting_func_start ; GFX9: S_WAITCNT 0 ; GFX9-NEXT: S_ENDPGM 0 + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_func_start + ; GFX12: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: S_WAITCNT 0 + ; GFX12-NEXT: S_ENDPGM 0 S_WAITCNT 0 S_ENDPGM 0 ... @@ -241,6 +400,22 @@ body: | ; GFX9-NEXT: S_WAITCNT 112 ; GFX9-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr ; GFX9-NEXT: S_ENDPGM 0 + ; + ; GFX12-LABEL: name: test_waitcnt_preexisting_buffer_inv + ; GFX12: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: S_WAIT_EXPCNT 0 + ; GFX12-NEXT: S_WAIT_SAMPLECNT 0 + ; GFX12-NEXT: S_WAIT_BVHCNT 0 + ; GFX12-NEXT: S_WAIT_KMCNT 0 + ; GFX12-NEXT: $vgpr0_vgpr1 = GLOBAL_LOAD_DWORDX2 $vgpr0_vgpr1, 0, 0, implicit $exec + ; GFX12-NEXT: S_WAITCNT 3952 + ; GFX12-NEXT: BUFFER_INVL2 implicit $exec + ; GFX12-NEXT: S_WAIT_LOADCNT 0 + ; GFX12-NEXT: BUFFER_WBINVL1_VOL implicit $exec + ; GFX12-NEXT: $vgpr0 = FLAT_LOAD_DWORD $vgpr0_vgpr1, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_WAIT_LOADCNT_DSCNT 0 + ; GFX12-NEXT: FLAT_STORE_DWORD $vgpr0_vgpr1, $vgpr0, 0, 0, implicit $exec, implicit $flat_scr + ; GFX12-NEXT: S_ENDPGM 0 $vgpr0_vgpr1 = GLOBAL_LOAD_DWORDX2 $vgpr0_vgpr1, 0, 0, implicit $exec S_WAITCNT 3952 BUFFER_INVL2 implicit $exec -- GitLab From 81d20d861e48f5202c9f79b47dee244674fb9121 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Wed, 15 May 2024 15:30:05 -0500 Subject: [PATCH 026/403] [Offload][NFC] Fix warning messages in runtime Summary: These are lots of random warnings due to inconsistent initialization or signedness. --- offload/plugins-nextgen/amdgpu/src/rtl.cpp | 11 ++++------- .../plugins-nextgen/common/src/PluginInterface.cpp | 2 +- offload/src/LegacyAPI.cpp | 6 ++++-- offload/src/OpenMP/API.cpp | 2 +- offload/src/OpenMP/Mapping.cpp | 12 +++--------- offload/src/omptarget.cpp | 2 +- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/offload/plugins-nextgen/amdgpu/src/rtl.cpp b/offload/plugins-nextgen/amdgpu/src/rtl.cpp index 295685fceaa4..2a9503333c19 100644 --- a/offload/plugins-nextgen/amdgpu/src/rtl.cpp +++ b/offload/plugins-nextgen/amdgpu/src/rtl.cpp @@ -1670,10 +1670,10 @@ private: hsa_agent_t Agent; /// The maximum number of queues. - int MaxNumQueues; + uint32_t MaxNumQueues; /// The size of created queues. - int QueueSize; + uint32_t QueueSize; }; /// Abstract class that holds the common members of the actual kernel devices @@ -1847,8 +1847,7 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy { // Create an AMDGPU device with a device id and default AMDGPU grid values. AMDGPUDeviceTy(GenericPluginTy &Plugin, int32_t DeviceId, int32_t NumDevices, AMDHostDeviceTy &HostDevice, hsa_agent_t Agent) - : GenericDeviceTy(Plugin, DeviceId, NumDevices, {0}), - AMDGenericDeviceTy(), + : GenericDeviceTy(Plugin, DeviceId, NumDevices, {}), AMDGenericDeviceTy(), OMPX_NumQueues("LIBOMPTARGET_AMDGPU_NUM_HSA_QUEUES", 4), OMPX_QueueSize("LIBOMPTARGET_AMDGPU_HSA_QUEUE_SIZE", 512), OMPX_DefaultTeamsPerCU("LIBOMPTARGET_AMDGPU_TEAMS_PER_CU", 4), @@ -2015,9 +2014,7 @@ struct AMDGPUDeviceTy : public GenericDeviceTy, AMDGenericDeviceTy { return Plugin::success(); } - const uint64_t getStreamBusyWaitMicroseconds() const { - return OMPX_StreamBusyWait; - } + uint64_t getStreamBusyWaitMicroseconds() const { return OMPX_StreamBusyWait; } Expected> doJITPostProcessing(std::unique_ptr MB) const override { diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp index a5c8cce63fac..737a8b2a4064 100644 --- a/offload/plugins-nextgen/common/src/PluginInterface.cpp +++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp @@ -593,7 +593,7 @@ void *GenericKernelTy::prepareArgs( Args[0] = &Ptrs[0]; } - for (int I = KLEOffset; I < NumArgs; ++I) { + for (uint32_t I = KLEOffset; I < NumArgs; ++I) { Ptrs[I] = (void *)((intptr_t)ArgPtrs[I - KLEOffset] + ArgOffsets[I - KLEOffset]); Args[I] = &Ptrs[I]; diff --git a/offload/src/LegacyAPI.cpp b/offload/src/LegacyAPI.cpp index 91d5642e8112..033d7a3ef712 100644 --- a/offload/src/LegacyAPI.cpp +++ b/offload/src/LegacyAPI.cpp @@ -88,7 +88,8 @@ EXTERN int __tgt_target_mapper(ident_t *Loc, int64_t DeviceId, void *HostPtr, TIMESCOPE_WITH_IDENT(Loc); OMPT_IF_BUILT(ReturnAddressSetterRAII RA(__builtin_return_address(0))); KernelArgsTy KernelArgs{1, ArgNum, ArgsBase, Args, ArgSizes, - ArgTypes, ArgNames, ArgMappers, 0}; + ArgTypes, ArgNames, ArgMappers, 0, {}, + {}, {}, 0}; return __tgt_target_kernel(Loc, DeviceId, -1, -1, HostPtr, &KernelArgs); } @@ -132,7 +133,8 @@ EXTERN int __tgt_target_teams_mapper(ident_t *Loc, int64_t DeviceId, TIMESCOPE_WITH_IDENT(Loc); OMPT_IF_BUILT(ReturnAddressSetterRAII RA(__builtin_return_address(0))); KernelArgsTy KernelArgs{1, ArgNum, ArgsBase, Args, ArgSizes, - ArgTypes, ArgNames, ArgMappers, 0}; + ArgTypes, ArgNames, ArgMappers, 0, {}, + {}, {}, 0}; return __tgt_target_kernel(Loc, DeviceId, NumTeams, ThreadLimit, HostPtr, &KernelArgs); } diff --git a/offload/src/OpenMP/API.cpp b/offload/src/OpenMP/API.cpp index c85f9868e37c..374c54163d6a 100644 --- a/offload/src/OpenMP/API.cpp +++ b/offload/src/OpenMP/API.cpp @@ -642,7 +642,7 @@ EXTERN void *omp_get_mapped_ptr(const void *Ptr, int DeviceNum) { return nullptr; } - size_t NumDevices = omp_get_initial_device(); + int NumDevices = omp_get_initial_device(); if (DeviceNum == NumDevices) { DP("Device %d is initial device, returning Ptr " DPxMOD ".\n", DeviceNum, DPxPTR(Ptr)); diff --git a/offload/src/OpenMP/Mapping.cpp b/offload/src/OpenMP/Mapping.cpp index c6ff3aa54dd6..595e3456ab54 100644 --- a/offload/src/OpenMP/Mapping.cpp +++ b/offload/src/OpenMP/Mapping.cpp @@ -314,9 +314,7 @@ TargetPointerResultTy MappingInfoTy::getTargetPointer( // Notify the plugin about the new mapping. if (Device.notifyDataMapped(HstPtrBegin, Size)) - return {{false /*IsNewEntry=*/, false /*IsHostPointer=*/}, - nullptr /*Entry=*/, - nullptr /*TargetPointer=*/}; + return TargetPointerResultTy{}; } else { // This entry is not present and we did not create a new entry for it. LR.TPR.Flags.IsPresent = false; @@ -344,9 +342,7 @@ TargetPointerResultTy MappingInfoTy::getTargetPointer( LR.TPR.TargetPointer = nullptr; } else if (LR.TPR.getEntry()->addEventIfNecessary(Device, AsyncInfo) != OFFLOAD_SUCCESS) - return {{false /*IsNewEntry=*/, false /*IsHostPointer=*/}, - nullptr /*Entry=*/, - nullptr /*TargetPointer=*/}; + return TargetPointerResultTy{}; } else { // If not a host pointer and no present modifier, we need to wait for the // event if it exists. @@ -360,9 +356,7 @@ TargetPointerResultTy MappingInfoTy::getTargetPointer( // If it fails to wait for the event, we need to return nullptr in // case of any data race. REPORT("Failed to wait for event " DPxMOD ".\n", DPxPTR(Event)); - return {{false /*IsNewEntry=*/, false /*IsHostPointer=*/}, - nullptr /*Entry=*/, - nullptr /*TargetPointer=*/}; + return TargetPointerResultTy{}; } } } diff --git a/offload/src/omptarget.cpp b/offload/src/omptarget.cpp index 5d5c6b05051b..91e1213f175e 100644 --- a/offload/src/omptarget.cpp +++ b/offload/src/omptarget.cpp @@ -1750,7 +1750,7 @@ int target_replay(ident_t *Loc, DeviceTy &Device, void *HostPtr, TARGET_ALLOC_DEFAULT); Device.submitData(TgtPtr, DeviceMemory, DeviceMemorySize, AsyncInfo); - KernelArgsTy KernelArgs = {0}; + KernelArgsTy KernelArgs{}; KernelArgs.Version = OMP_KERNEL_ARG_VERSION; KernelArgs.NumArgs = NumArgs; KernelArgs.Tripcount = LoopTripCount; -- GitLab From 83f065d582977aca5c037c27a7290f30850bdd35 Mon Sep 17 00:00:00 2001 From: Alex Bradbury Date: Wed, 15 May 2024 21:33:10 +0100 Subject: [PATCH 027/403] [RISCV] static_assert SupportedProfiles and SupportedExperimentalProfiles are sorted Just as we do for the arrays of extension names. --- llvm/lib/TargetParser/RISCVISAInfo.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/llvm/lib/TargetParser/RISCVISAInfo.cpp b/llvm/lib/TargetParser/RISCVISAInfo.cpp index 706b2853cd2c..827bc5b44387 100644 --- a/llvm/lib/TargetParser/RISCVISAInfo.cpp +++ b/llvm/lib/TargetParser/RISCVISAInfo.cpp @@ -39,6 +39,10 @@ struct RISCVSupportedExtension { struct RISCVProfile { StringLiteral Name; StringLiteral MArch; + + bool operator<(const RISCVProfile &RHS) const { + return StringRef(Name) < StringRef(RHS.Name); + } }; } // end anonymous namespace @@ -61,6 +65,10 @@ static void verifyTables() { "Extensions are not sorted by name"); assert(llvm::is_sorted(SupportedExperimentalExtensions) && "Experimental extensions are not sorted by name"); + assert(llvm::is_sorted(SupportedProfiles) && + "Profiles are not sorted by name"); + assert(llvm::is_sorted(SupportedExperimentalProfiles) && + "Experimental profiles are not sorted by name"); TableChecked.store(true, std::memory_order_relaxed); } #endif -- GitLab From e1ed138a67a92ef1ff0214ca198094be13045090 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 15 May 2024 14:38:45 -0700 Subject: [PATCH 028/403] [bazel] Port #92199 --- utils/bazel/llvm-project-overlay/llvm/BUILD.bazel | 2 ++ 1 file changed, 2 insertions(+) diff --git a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel index df5cd276b12f..c469da74fc56 100644 --- a/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/llvm/BUILD.bazel @@ -1125,6 +1125,8 @@ cc_library( ]), copts = llvm_copts, deps = [ + ":BitstreamReader", + ":BitstreamWriter", ":Core", ":DebugInfo", ":DebugInfoDWARF", -- GitLab From 8530b1c464ae9af4a5c8be145a8db043798634f6 Mon Sep 17 00:00:00 2001 From: Dave Lee Date: Wed, 15 May 2024 14:44:42 -0700 Subject: [PATCH 029/403] [lldb] Support custom LLVM formatting for variables (#91868) Re-apply https://github.com/llvm/llvm-project/pull/81196, with a fix that handles the absence of llvm formatting: https://github.com/llvm/llvm-project/pull/91868/commits/3ba650e91eded3543764f37921dcce3b b47d425f --- lldb/docs/use/variable.rst | 9 +++ lldb/source/Core/FormatEntity.cpp | 72 ++++++++++++++++--- .../custom-printf-summary/Makefile | 2 + .../TestCustomSummaryLLVMFormat.py | 20 ++++++ .../custom-printf-summary/main.c | 13 ++++ 5 files changed, 106 insertions(+), 10 deletions(-) create mode 100644 lldb/test/API/functionalities/data-formatter/custom-printf-summary/Makefile create mode 100644 lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomSummaryLLVMFormat.py create mode 100644 lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c diff --git a/lldb/docs/use/variable.rst b/lldb/docs/use/variable.rst index 8eaed6405315..e9175b25336b 100644 --- a/lldb/docs/use/variable.rst +++ b/lldb/docs/use/variable.rst @@ -460,6 +460,15 @@ summary strings, regardless of the format they have applied to their types. To do that, you can use %format inside an expression path, as in ${var.x->x%u}, which would display the value of x as an unsigned integer. +Additionally, custom output can be achieved by using an LLVM format string, +commencing with the ``:`` marker. To illustrate, compare ``${var.byte%x}`` and +``${var.byte:x-}``. The former uses lldb's builtin hex formatting (``x``), +which unconditionally inserts a ``0x`` prefix, and also zero pads the value to +match the size of the type. The latter uses ``llvm::formatv`` formatting +(``:x-``), and will print only the hex value, with no ``0x`` prefix, and no +padding. This raw control is useful when composing multiple pieces into a +larger whole. + You can also use some other special format markers, not available for formats themselves, but which carry a special meaning when used in this context: diff --git a/lldb/source/Core/FormatEntity.cpp b/lldb/source/Core/FormatEntity.cpp index ba62e2625259..07978d388296 100644 --- a/lldb/source/Core/FormatEntity.cpp +++ b/lldb/source/Core/FormatEntity.cpp @@ -57,6 +57,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/Regex.h" #include "llvm/TargetParser/Triple.h" #include @@ -658,6 +659,37 @@ static char ConvertValueObjectStyleToChar( return '\0'; } +/// Options supported by format_provider for integral arithmetic types. +/// See table in FormatProviders.h. +static llvm::Regex LLVMFormatPattern{"x[-+]?\\d*|n|d", llvm::Regex::IgnoreCase}; + +static bool DumpValueWithLLVMFormat(Stream &s, llvm::StringRef options, + ValueObject &valobj) { + std::string formatted; + std::string llvm_format = ("{0:" + options + "}").str(); + + auto type_info = valobj.GetTypeInfo(); + if ((type_info & eTypeIsInteger) && LLVMFormatPattern.match(options)) { + if (type_info & eTypeIsSigned) { + bool success = false; + int64_t integer = valobj.GetValueAsSigned(0, &success); + if (success) + formatted = llvm::formatv(llvm_format.data(), integer); + } else { + bool success = false; + uint64_t integer = valobj.GetValueAsUnsigned(0, &success); + if (success) + formatted = llvm::formatv(llvm_format.data(), integer); + } + } + + if (formatted.empty()) + return false; + + s.Write(formatted.data(), formatted.size()); + return true; +} + static bool DumpValue(Stream &s, const SymbolContext *sc, const ExecutionContext *exe_ctx, const FormatEntity::Entry &entry, ValueObject *valobj) { @@ -728,9 +760,12 @@ static bool DumpValue(Stream &s, const SymbolContext *sc, return RunScriptFormatKeyword(s, sc, exe_ctx, valobj, entry.string.c_str()); } - llvm::StringRef subpath(entry.string); + auto split = llvm::StringRef(entry.string).split(':'); + auto subpath = split.first; + auto llvm_format = split.second; + // simplest case ${var}, just print valobj's value - if (entry.string.empty()) { + if (subpath.empty()) { if (entry.printf_format.empty() && entry.fmt == eFormatDefault && entry.number == ValueObject::eValueObjectRepresentationStyleValue) was_plain_var = true; @@ -739,7 +774,7 @@ static bool DumpValue(Stream &s, const SymbolContext *sc, target = valobj; } else // this is ${var.something} or multiple .something nested { - if (entry.string[0] == '[') + if (subpath[0] == '[') was_var_indexed = true; ScanBracketedRange(subpath, close_bracket_index, var_name_final_if_array_range, index_lower, @@ -747,14 +782,11 @@ static bool DumpValue(Stream &s, const SymbolContext *sc, Status error; - const std::string &expr_path = entry.string; - - LLDB_LOGF(log, "[Debugger::FormatPrompt] symbol to expand: %s", - expr_path.c_str()); + LLDB_LOG(log, "[Debugger::FormatPrompt] symbol to expand: {0}", subpath); target = valobj - ->GetValueForExpressionPath(expr_path.c_str(), &reason_to_stop, + ->GetValueForExpressionPath(subpath, &reason_to_stop, &final_value_type, options, &what_next) .get(); @@ -883,8 +915,18 @@ static bool DumpValue(Stream &s, const SymbolContext *sc, } if (!is_array_range) { - LLDB_LOGF(log, - "[Debugger::FormatPrompt] dumping ordinary printable output"); + if (!llvm_format.empty()) { + if (DumpValueWithLLVMFormat(s, llvm_format, *target)) { + LLDB_LOGF(log, "dumping using llvm format"); + return true; + } else { + LLDB_LOG( + log, + "empty output using llvm format '{0}' - with type info flags {1}", + entry.printf_format, target->GetTypeInfo()); + } + } + LLDB_LOGF(log, "dumping ordinary printable output"); return target->DumpPrintableRepresentation(s, val_obj_display, custom_format); } else { @@ -2227,6 +2269,16 @@ static Status ParseInternal(llvm::StringRef &format, Entry &parent_entry, if (error.Fail()) return error; + llvm::StringRef entry_string(entry.string); + if (entry_string.contains(':')) { + auto [_, llvm_format] = entry_string.split(':'); + if (!llvm_format.empty() && !LLVMFormatPattern.match(llvm_format)) { + error.SetErrorStringWithFormat("invalid llvm format: '%s'", + llvm_format.data()); + return error; + } + } + if (verify_is_thread_id) { if (entry.type != Entry::Type::ThreadID && entry.type != Entry::Type::ThreadProtocolID) { diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/Makefile b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/Makefile new file mode 100644 index 000000000000..c9319d6e6888 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/Makefile @@ -0,0 +1,2 @@ +C_SOURCES := main.c +include Makefile.rules diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomSummaryLLVMFormat.py b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomSummaryLLVMFormat.py new file mode 100644 index 000000000000..d6906a49463b --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/TestCustomSummaryLLVMFormat.py @@ -0,0 +1,20 @@ +import lldb +from lldbsuite.test.lldbtest import * +import lldbsuite.test.lldbutil as lldbutil + + +class TestCase(TestBase): + def test_raw_bytes(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c")) + self.runCmd("type summary add -s '${var.ubyte:x-2}${var.sbyte:x-2}!' Bytes") + self.expect("v bytes", substrs=[" = 3001!"]) + + def test_bad_format(self): + self.build() + lldbutil.run_to_source_breakpoint(self, "break here", lldb.SBFileSpec("main.c")) + self.expect( + "type summary add -s '${var.ubyte:y}!' Bytes", + error=True, + substrs=["invalid llvm format"], + ) diff --git a/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c new file mode 100644 index 000000000000..4164aff7dbf6 --- /dev/null +++ b/lldb/test/API/functionalities/data-formatter/custom-printf-summary/main.c @@ -0,0 +1,13 @@ +#include +#include + +struct Bytes { + uint8_t ubyte; + int8_t sbyte; +}; + +int main() { + struct Bytes bytes = {0x30, 0x01}; + (void)bytes; + printf("break here\n"); +} -- GitLab From 1daa7fd3fadd17e61d9dfa56f84228617c5514d9 Mon Sep 17 00:00:00 2001 From: Amara Emerson Date: Wed, 15 May 2024 14:38:18 -0700 Subject: [PATCH 030/403] [AArch64][SME] Remove Darwin compile error for ABI support routine calls. These are allowed for Darwin and use the same ABI. --- llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp | 8 ++------ .../sme-support-routines-calling-convention.ll | 12 ++++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp index 5a5a18edb12e..d82fa3924f83 100644 --- a/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64RegisterInfo.cpp @@ -233,13 +233,9 @@ AArch64RegisterInfo::getDarwinCallPreservedMask(const MachineFunction &MF, report_fatal_error( "Calling convention SVE_VectorCall is unsupported on Darwin."); if (CC == CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0) - report_fatal_error( - "Calling convention AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0 is " - "unsupported on Darwin."); + return CSR_AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0_RegMask; if (CC == CallingConv::AArch64_SME_ABI_Support_Routines_PreserveMost_From_X2) - report_fatal_error( - "Calling convention AArch64_SME_ABI_Support_Routines_PreserveMost_From_X2 is " - "unsupported on Darwin."); + return CSR_AArch64_SME_ABI_Support_Routines_PreserveMost_From_X2_RegMask; if (CC == CallingConv::CFGuard_Check) report_fatal_error( "Calling convention CFGuard_Check is unsupported on Darwin."); diff --git a/llvm/test/CodeGen/AArch64/sme-support-routines-calling-convention.ll b/llvm/test/CodeGen/AArch64/sme-support-routines-calling-convention.ll index d88deec40ce7..7535638137ca 100644 --- a/llvm/test/CodeGen/AArch64/sme-support-routines-calling-convention.ll +++ b/llvm/test/CodeGen/AArch64/sme-support-routines-calling-convention.ll @@ -1,5 +1,7 @@ ; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sme -verify-machineinstrs < %s | FileCheck %s +; RUN: llc -mtriple=aarch64-apple-darwin -mattr=+sme -verify-machineinstrs < %s | FileCheck %s --check-prefix=DARWIN ; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sme -verify-machineinstrs -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=CHECK-CSRMASK +; RUN: llc -mtriple=aarch64-apple-darwin -mattr=+sme -verify-machineinstrs -stop-after=finalize-isel < %s | FileCheck %s --check-prefix=CHECK-CSRMASK ; Test that the PCS attribute is accepted and uses the correct register mask. ; @@ -11,6 +13,11 @@ define void @test_sme_calling_convention_x0() nounwind { ; CHECK-NEXT: bl __arm_tpidr2_save ; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret +; DARWIN-LABEL: test_sme_calling_convention_x0: +; DARWIN: stp x29, x30, [sp, #-16]! +; DARWIN: bl ___arm_tpidr2_save +; DARWIN: ldp x29, x30, [sp], #16 +; DARWIN: ret ; ; CHECK-CSRMASK-LABEL: name: test_sme_calling_convention_x0 ; CHECK-CSRMASK: BL @__arm_tpidr2_save, csr_aarch64_sme_abi_support_routines_preservemost_from_x0 @@ -25,6 +32,11 @@ define i64 @test_sme_calling_convention_x2() nounwind { ; CHECK-NEXT: bl __arm_sme_state ; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload ; CHECK-NEXT: ret +; DARWIN-LABEL: test_sme_calling_convention_x2: +; DARWIN: stp x29, x30, [sp, #-16]! +; DARWIN: bl ___arm_sme_state +; DARWIN: ldp x29, x30, [sp], #16 +; DARWIN: ret ; ; CHECK-CSRMASK-LABEL: name: test_sme_calling_convention_x2 ; CHECK-CSRMASK: BL @__arm_sme_state, csr_aarch64_sme_abi_support_routines_preservemost_from_x2 -- GitLab From 537a94b2ef67cd96a4b3a9b5612ea726a91c602b Mon Sep 17 00:00:00 2001 From: Mehdi Amini Date: Wed, 15 May 2024 15:06:08 -0700 Subject: [PATCH 031/403] Revert "[NewPM] Add pass options for `InternalizePass` to preserve GVs." (#92321) Reverts llvm/llvm-project#91334 This broke the gcc7 build. I suspect the issue is a mismatch on user-defined move constructor on the return: `return PreservedGVs;` does not match the return type of the function. --- llvm/lib/Passes/PassBuilder.cpp | 18 ------------------ llvm/lib/Passes/PassRegistry.def | 15 +-------------- llvm/test/Transforms/Internalize/lists.ll | 5 ----- 3 files changed, 1 insertion(+), 37 deletions(-) diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index 91c5b65c0351..e4131706aba0 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -1142,24 +1142,6 @@ Expected parseGlobalMergeOptions(StringRef Params) { return Result; } -Expected> parseInternalizeGVs(StringRef Params) { - SmallVector PreservedGVs; - while (!Params.empty()) { - StringRef ParamName; - std::tie(ParamName, Params) = Params.split(';'); - - if (ParamName.consume_front("preserve-gv=")) { - PreservedGVs.push_back(ParamName.str()); - } else { - return make_error( - formatv("invalid Internalize pass parameter '{0}' ", ParamName).str(), - inconvertibleErrorCode()); - } - } - - return PreservedGVs; -} - } // namespace /// Tests whether a pass name starts with a valid prefix for a default pipeline diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 50682ca4970f..e5ce6cb7da64 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -78,6 +78,7 @@ MODULE_PASS("insert-gcov-profiling", GCOVProfilerPass()) MODULE_PASS("instrorderfile", InstrOrderFilePass()) MODULE_PASS("instrprof", InstrProfilingLoweringPass()) MODULE_PASS("ctx-instr-lower", PGOCtxProfLoweringPass()) +MODULE_PASS("internalize", InternalizePass()) MODULE_PASS("invalidate", InvalidateAllAnalysesPass()) MODULE_PASS("iroutliner", IROutlinerPass()) MODULE_PASS("jmc-instrumenter", JMCInstrumenterPass()) @@ -174,20 +175,6 @@ MODULE_PASS_WITH_PARAMS( "hwasan", "HWAddressSanitizerPass", [](HWAddressSanitizerOptions Opts) { return HWAddressSanitizerPass(Opts); }, parseHWASanPassOptions, "kernel;recover") -MODULE_PASS_WITH_PARAMS( - "internalize", "InternalizePass", - [](SmallVector PreservedGVs) { - if (PreservedGVs.empty()) - return InternalizePass(); - auto MustPreserveGV = [=](const GlobalValue &GV) { - for (auto &PreservedGV : PreservedGVs) - if (GV.getName() == PreservedGV) - return true; - return false; - }; - return InternalizePass(MustPreserveGV); - }, - parseInternalizeGVs, "preserve-gv=GV") MODULE_PASS_WITH_PARAMS( "ipsccp", "IPSCCPPass", [](IPSCCPOptions Opts) { return IPSCCPPass(Opts); }, parseIPSCCPOptions, "no-func-spec;func-spec") diff --git a/llvm/test/Transforms/Internalize/lists.ll b/llvm/test/Transforms/Internalize/lists.ll index 83dad03d75ea..df408f906b78 100644 --- a/llvm/test/Transforms/Internalize/lists.ll +++ b/llvm/test/Transforms/Internalize/lists.ll @@ -13,11 +13,6 @@ ; -file and -list options should be merged, the apifile contains foo and j ; RUN: opt < %s -passes=internalize -internalize-public-api-list bar -internalize-public-api-file %S/apifile -S | FileCheck --check-prefix=FOO_J_AND_BAR %s -; specifying through pass builder option -; RUN: opt < %s -passes='internalize' -S | FileCheck --check-prefix=FOO_AND_J %s -; RUN: opt < %s -passes='internalize' -S | FileCheck --check-prefix=FOO_AND_BAR %s -; RUN: opt < %s -passes='internalize' -S | FileCheck --check-prefix=FOO_J_AND_BAR %s - ; ALL: @i = internal global ; FOO_AND_J: @i = internal global ; FOO_AND_BAR: @i = internal global -- GitLab From f97f039e0bb7bb60c9cc437f678059c5ee19c8da Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 16 May 2024 01:11:14 +0300 Subject: [PATCH 032/403] [lld,test] Fix few FileCheck annotation typos (#92238) --- lld/test/MachO/install-name.s | 2 +- lld/test/MachO/objc-methname.s | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lld/test/MachO/install-name.s b/lld/test/MachO/install-name.s index 1cf675e278bf..c419c6ca1f95 100644 --- a/lld/test/MachO/install-name.s +++ b/lld/test/MachO/install-name.s @@ -31,7 +31,7 @@ # ID: cmd LC_ID_DYLIB # ID-NEXT: cmdsize -# LID-NEXT: name foo +# ID-NEXT: name foo .globl _main _main: diff --git a/lld/test/MachO/objc-methname.s b/lld/test/MachO/objc-methname.s index afc137eac8c2..3d06472971c8 100644 --- a/lld/test/MachO/objc-methname.s +++ b/lld/test/MachO/objc-methname.s @@ -16,7 +16,7 @@ # CSTRING: Contents of (__TEXT,__cstring) section # CSTRING-NEXT: existing-cstring -# CSTIRNG-EMPTY: +# CSTRING-EMPTY: # METHNAME: Contents of (__TEXT,__objc_methname) section # METHNAME-NEXT: existing_methname -- GitLab From 00179e92c147e16de1f7c653f88c8805aef820c1 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Wed, 15 May 2024 15:23:29 -0700 Subject: [PATCH 033/403] [bazel] Add new dependencies (#92323) This also fixes building ... on Linux. Seems like target_compatible_with isn't enough but you also need a manual tag. --- utils/bazel/llvm-project-overlay/lldb/BUILD.bazel | 5 ++++- utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel b/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel index c6fc4e08aa72..ddcaea5184d4 100644 --- a/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/lldb/BUILD.bazel @@ -916,7 +916,10 @@ apple_genrule( srcs = [":debugserver_unsigned"], outs = ["debugserver"], cmd = "cp $(SRCS) $(OUTS) && xcrun codesign -f -s - --entitlements $(location tools/debugserver/resources/debugserver-macosx-entitlements.plist) $(OUTS)", - tags = ["nobuildkite"], + tags = [ + "manual", + "nobuildkite", + ], target_compatible_with = select({ "@platforms//os:macos": [], "//conditions:default": ["@platforms//:incompatible"], diff --git a/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel b/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel index 21f0c7092f32..b44489e213a4 100644 --- a/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel +++ b/utils/bazel/llvm-project-overlay/llvm/unittests/BUILD.bazel @@ -617,6 +617,7 @@ cc_test( allow_empty = False, ), deps = [ + "//llvm:BitstreamReader", "//llvm:Core", "//llvm:Coverage", "//llvm:DebugInfo", -- GitLab From 050593fc4f9a7f2b9450ee093c4638b8539315b7 Mon Sep 17 00:00:00 2001 From: Andrey Ali Khan Bolshakov Date: Thu, 16 May 2024 01:39:12 +0300 Subject: [PATCH 034/403] [Coverage] Handle `CoroutineSuspendExpr` correctly (#88898) This avoids visiting `co_await` or `co_yield` operand 5 times (it is repeated under transformed awaiter subexpression, and under `await_ready`, `await_suspend`, and `await_resume` generated call subexpressions). --- clang/lib/CodeGen/CoverageMappingGen.cpp | 4 ++++ clang/test/CoverageMapping/coroutine.cpp | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp index ce2f39aeb082..e46560029ab0 100644 --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -1439,6 +1439,10 @@ struct CounterCoverageMappingBuilder terminateRegion(S); } + void VisitCoroutineSuspendExpr(const CoroutineSuspendExpr *E) { + Visit(E->getOperand()); + } + void VisitCXXThrowExpr(const CXXThrowExpr *E) { extendRegion(E); if (E->getSubExpr()) diff --git a/clang/test/CoverageMapping/coroutine.cpp b/clang/test/CoverageMapping/coroutine.cpp index 0105005d198a..d322bc351a72 100644 --- a/clang/test/CoverageMapping/coroutine.cpp +++ b/clang/test/CoverageMapping/coroutine.cpp @@ -32,6 +32,7 @@ struct std::coroutine_traits { suspend_always final_suspend() noexcept; void unhandled_exception() noexcept; void return_value(int); + suspend_always yield_value(int); }; }; @@ -45,3 +46,21 @@ int f1(int x) { // CHECK-NEXT: File 0, [[@LINE]]:15 -> [[@LINE+8]]:2 = #0 } // CHECK-NEXT: File 0, [[@LINE-2]]:10 -> [[@LINE]]:4 = (#0 - #1) co_return x; // CHECK-NEXT: Gap,File 0, [[@LINE-1]]:4 -> [[@LINE]]:3 = #1 } // CHECK-NEXT: File 0, [[@LINE-1]]:3 -> [[@LINE-1]]:14 = #1 + +// CHECK-LABEL: _Z2f2i: +// CHECK-NEXT: File 0, [[@LINE+1]]:15 -> [[@LINE+15]]:2 = #0 +int f2(int x) { +// CHECK-NEXT: File 0, [[@LINE+5]]:13 -> [[@LINE+5]]:18 = #0 +// CHECK-NEXT: Branch,File 0, [[@LINE+4]]:13 -> [[@LINE+4]]:18 = #1, (#0 - #1) +// CHECK-NEXT: Gap,File 0, [[@LINE+3]]:20 -> [[@LINE+3]]:21 = #1 +// CHECK-NEXT: File 0, [[@LINE+2]]:21 -> [[@LINE+2]]:37 = #1 +// CHECK-NEXT: File 0, [[@LINE+1]]:40 -> [[@LINE+1]]:56 = (#0 - #1) + co_await (x > 0 ? suspend_always{} : suspend_always{}); +// CHECK-NEXT: File 0, [[@LINE+5]]:12 -> [[@LINE+5]]:17 = #0 +// CHECK-NEXT: Branch,File 0, [[@LINE+4]]:12 -> [[@LINE+4]]:17 = #2, (#0 - #2) +// CHECK-NEXT: Gap,File 0, [[@LINE+3]]:19 -> [[@LINE+3]]:20 = #2 +// CHECK-NEXT: File 0, [[@LINE+2]]:20 -> [[@LINE+2]]:21 = #2 +// CHECK-NEXT: File 0, [[@LINE+1]]:24 -> [[@LINE+1]]:25 = (#0 - #2) + co_yield x > 0 ? 1 : 2; + co_return 0; +} -- GitLab From 5ff6c6542ac451daaed6c417e481e313165d3454 Mon Sep 17 00:00:00 2001 From: Andrey Ali Khan Bolshakov Date: Thu, 16 May 2024 01:40:03 +0300 Subject: [PATCH 035/403] [Coverage] Handle array decomposition correctly (#88881) `ArrayInitLoopExpr` AST node has two occurences of its as-written initializing expression in its subexpressions through a non-unique `OpaqueValueExpr`. It causes double-visiting of the initializing expression if not handled explicitly, as discussed in #85837. --- clang/lib/CodeGen/CoverageMappingGen.cpp | 4 ++++ clang/test/CoverageMapping/decomposition.cpp | 15 +++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 clang/test/CoverageMapping/decomposition.cpp diff --git a/clang/lib/CodeGen/CoverageMappingGen.cpp b/clang/lib/CodeGen/CoverageMappingGen.cpp index e46560029ab0..cc8ab7a5b436 100644 --- a/clang/lib/CodeGen/CoverageMappingGen.cpp +++ b/clang/lib/CodeGen/CoverageMappingGen.cpp @@ -2177,6 +2177,10 @@ struct CounterCoverageMappingBuilder // propagate counts into them. } + void VisitArrayInitLoopExpr(const ArrayInitLoopExpr *AILE) { + Visit(AILE->getCommonExpr()->getSourceExpr()); + } + void VisitPseudoObjectExpr(const PseudoObjectExpr *POE) { // Just visit syntatic expression as this is what users actually write. VisitStmt(POE->getSyntacticForm()); diff --git a/clang/test/CoverageMapping/decomposition.cpp b/clang/test/CoverageMapping/decomposition.cpp new file mode 100644 index 000000000000..601ea630faee --- /dev/null +++ b/clang/test/CoverageMapping/decomposition.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -mllvm -emptyline-comment-coverage=false -triple %itanium_abi_triple -fprofile-instrument=clang -fcoverage-mapping -dump-coverage-mapping -emit-llvm-only %s | FileCheck %s + +// CHECK-LABEL: _Z19array_decompositioni: +// CHECK-NEXT: File 0, [[@LINE+6]]:32 -> {{[0-9]+}}:2 = #0 +// CHECK-NEXT: File 0, [[@LINE+8]]:20 -> [[@LINE+8]]:25 = #0 +// CHECK-NEXT: Branch,File 0, [[@LINE+7]]:20 -> [[@LINE+7]]:25 = #1, (#0 - #1) +// CHECK-NEXT: Gap,File 0, [[@LINE+6]]:27 -> [[@LINE+6]]:28 = #1 +// CHECK-NEXT: File 0, [[@LINE+5]]:28 -> [[@LINE+5]]:29 = #1 +// CHECK-NEXT: File 0, [[@LINE+4]]:32 -> [[@LINE+4]]:33 = (#0 - #1) +int array_decomposition(int i) { + int a[] = {1, 2, 3}; + int b[] = {4, 5, 6}; + auto [x, y, z] = i > 0 ? a : b; + return x + y + z; +} -- GitLab From aa889d7783af050ce5d19af67c7225ee119d625e Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 15 May 2024 15:41:20 -0700 Subject: [PATCH 036/403] [ELF,test] Fix FileCheck prefixes Most violations are stale and should be removed while a few can be adjusted. Reported at #92238 --- lld/test/ELF/arm-exidx-shared.s | 2 +- lld/test/ELF/mips-tls-hilo.s | 10 ---------- lld/test/ELF/ppc32-reloc-rel.s | 3 ++- lld/test/ELF/ppc64-pcrel-call-to-extern.s | 5 ----- lld/test/ELF/ppc64-toc-relax-ifunc.s | 13 +++++-------- lld/test/ELF/riscv-gp.s | 4 ---- 6 files changed, 8 insertions(+), 29 deletions(-) diff --git a/lld/test/ELF/arm-exidx-shared.s b/lld/test/ELF/arm-exidx-shared.s index fce605d6d96a..2e484e5c065f 100644 --- a/lld/test/ELF/arm-exidx-shared.s +++ b/lld/test/ELF/arm-exidx-shared.s @@ -2,7 +2,7 @@ // RUN: llvm-mc -filetype=obj -arm-add-build-attributes -triple=armv7a-none-linux-gnueabi %s -o %t // RUN: ld.lld --hash-style=sysv %t --shared -o %t2 // RUN: llvm-readobj --relocations %t2 | FileCheck %s -// RUN: llvm-objdump -s --triple=armv7a-none-linux-gnueabi %t2 | FileCheck --check-prefix=CHECK-EXTAB-NEXT %s +// RUN: llvm-objdump -s --triple=armv7a-none-linux-gnueabi %t2 | FileCheck --check-prefix=CHECK-EXTAB %s // Check that the relative R_ARM_PREL31 relocation can access a PLT entry // for when the personality routine is referenced from a shared library. diff --git a/lld/test/ELF/mips-tls-hilo.s b/lld/test/ELF/mips-tls-hilo.s index 6fd2033aac41..9c67f9fe14ba 100644 --- a/lld/test/ELF/mips-tls-hilo.s +++ b/lld/test/ELF/mips-tls-hilo.s @@ -28,16 +28,6 @@ # CHECK-NEXT: ] # CHECK-NOT: Primary GOT -# SO: Relocations [ -# SO-NEXT: ] -# SO: Primary GOT { -# SO: Local entries [ -# SO-NEXT: ] -# SO-NEXT: Global entries [ -# SO-NEXT: ] -# SO-NEXT: Number of TLS and multi-GOT entries: 0 -# SO-NEXT: } - .text .globl __start .type __start,@function diff --git a/lld/test/ELF/ppc32-reloc-rel.s b/lld/test/ELF/ppc32-reloc-rel.s index b89e0b43cb78..d13ebdb7997f 100644 --- a/lld/test/ELF/ppc32-reloc-rel.s +++ b/lld/test/ELF/ppc32-reloc-rel.s @@ -6,6 +6,7 @@ # RUN: llvm-mc -filetype=obj -triple=powerpcle %s -o %t.le.o # RUN: ld.lld %t.le.o -o %t # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s +# RUN: llvm-objdump -s %t | FileCheck %s --check-prefix=HEX .section .R_PPC_REL14,"ax",@progbits beq 1f @@ -23,7 +24,7 @@ .long 1f - . 1: # HEX-LABEL: section .R_PPC_REL32: -# HEX-NEXT: 10010008 00000004 +# HEX-NEXT: 04000000 .section .R_PPC_PLTREL24,"ax",@progbits b 1f@PLT+32768 diff --git a/lld/test/ELF/ppc64-pcrel-call-to-extern.s b/lld/test/ELF/ppc64-pcrel-call-to-extern.s index e5846e80ce23..de05b733e175 100644 --- a/lld/test/ELF/ppc64-pcrel-call-to-extern.s +++ b/lld/test/ELF/ppc64-pcrel-call-to-extern.s @@ -73,9 +73,7 @@ ## DT_PLTGOT points to .plt # SEC: .plt NOBITS 0000000010030168 040168 000028 00 WA 0 0 8 -# SEC-OG: .plt NOBITS 0000000010030158 040158 000028 00 WA 0 0 8 # SEC: 0x0000000000000003 (PLTGOT) 0x10030168 -# SEC-OG: 0x0000000000000003 (PLTGOT) 0x10030168 ## DT_PLTGOT points to .plt # SEC-NOP10: .plt NOBITS 0000000010030168 040168 000028 00 WA 0 0 8 @@ -86,11 +84,8 @@ ## Check that we emit 3 R_PPC64_JMP_SLOT in .rela.plt. # REL: .rela.plt { # REL-NEXT: 0x10030178 R_PPC64_JMP_SLOT callee_global_stother0 0x0 -# REL-NEXT-OG: 0x10030168 R_PPC64_JMP_SLOT callee_global_stother0 0x0 # REL-NEXT: 0x10030180 R_PPC64_JMP_SLOT callee_global_stother1 0x0 -# REL-NEXT-OG: 0x10030170 R_PPC64_JMP_SLOT callee_global_stother1 0x0 # REL-NEXT: 0x10030188 R_PPC64_JMP_SLOT callee_global_TOC 0x0 -# REL-NEXT-OG: 0x10030178 R_PPC64_JMP_SLOT callee_global_TOC 0x0 # REL-NEXT: } # REL-NOP10: .rela.plt { diff --git a/lld/test/ELF/ppc64-toc-relax-ifunc.s b/lld/test/ELF/ppc64-toc-relax-ifunc.s index 9fb1bf0023b6..00a63c7e5b67 100644 --- a/lld/test/ELF/ppc64-toc-relax-ifunc.s +++ b/lld/test/ELF/ppc64-toc-relax-ifunc.s @@ -4,7 +4,7 @@ # RUN: echo '.globl ifunc; .type ifunc, %gnu_indirect_function; ifunc:' | \ # RUN: llvm-mc -filetype=obj -triple=powerpc64le - -o %t1.o # RUN: ld.lld %t.o %t1.o -o %t -# RUN: llvm-readelf -S -s %t | FileCheck --check-prefix=SEC %s +# RUN: llvm-readelf -Ssr %t | FileCheck --check-prefix=SEC %s # RUN: llvm-readelf -x .toc %t | FileCheck --check-prefix=HEX %s # RUN: llvm-objdump -d %t | FileCheck --check-prefix=DIS %s @@ -13,18 +13,15 @@ ## still perform toc-indirect to toc-relative relaxation because the distance ## to the address of the canonical PLT is fixed. -# SEC: .text PROGBITS 00000000100101e0 -# SEC: .plt NOBITS 0000000010030200 -# SEC: 00000000100101e8 0 FUNC GLOBAL DEFAULT 3 ifunc +# SEC: .text PROGBITS [[#%x,TEXT:]] +# SEC: .plt NOBITS [[#%x,PLT:]] +# SEC: {{0*}}[[#PLT]] {{.+}} R_PPC64_IRELATIVE [[#TEXT+8]] +# SEC: {{0*}}[[#TEXT+8]] 0 FUNC GLOBAL DEFAULT 3 ifunc ## .toc[0] stores the address of the canonical PLT. # HEX: section '.toc': # HEX-NEXT: 0x100201f8 e8010110 00000000 -# REL: .rela.dyn { -# REL-NEXT: 0x100301f8 R_PPC64_IRELATIVE - 0x100101e8 -# REL-NEXT: } - # DIS: addi 3, 3, addis 3, 2, .toc@toc@ha diff --git a/lld/test/ELF/riscv-gp.s b/lld/test/ELF/riscv-gp.s index 29411d19b019..e82e36ee9a7a 100644 --- a/lld/test/ELF/riscv-gp.s +++ b/lld/test/ELF/riscv-gp.s @@ -16,10 +16,6 @@ # SEC64: [ [[#SDATA:]]] .sdata PROGBITS {{0*}}000032e0 # SEC64: {{0*}}00003ae0 0 NOTYPE GLOBAL DEFAULT [[#SDATA]] __global_pointer$ -## __global_pointer$ - 0x1000 = 4096*3-2048 -# DIS: 1000: auipc gp, 3 -# DIS-NEXT: addi gp, gp, -2048 - # ERR: error: relocation R_RISCV_PCREL_HI20 cannot be used against symbol '__global_pointer$'; recompile with -fPIC ## -r mode does not define __global_pointer$. -- GitLab From 0585eed9409c1362f7deaabc42c1d3c3f55c4b6c Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Wed, 15 May 2024 15:44:05 -0700 Subject: [PATCH 037/403] [lldb-dap] Support publishing to the VSCode market place (#92320) Update the publisher and add a publish script that we can use from Github actions. --- lldb/tools/lldb-dap/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lldb/tools/lldb-dap/package.json b/lldb/tools/lldb-dap/package.json index 2e8ad074256b..aeb24445551c 100644 --- a/lldb/tools/lldb-dap/package.json +++ b/lldb/tools/lldb-dap/package.json @@ -2,7 +2,7 @@ "name": "lldb-dap", "displayName": "LLDB DAP", "version": "0.2.0", - "publisher": "llvm", + "publisher": "llvm-vs-code-extensions", "homepage": "https://lldb.llvm.org", "description": "LLDB debugging from VSCode", "license": "Apache 2.0 License with LLVM exceptions", @@ -42,6 +42,7 @@ "watch": "tsc -watch -p ./", "format": "npx prettier './src-ts/' --write", "package": "vsce package --out ./out/lldb-dap.vsix", + "publish": "vsce publish", "vscode-uninstall": "code --uninstall-extension llvm.lldb-dap", "vscode-install": "code --install-extension ./out/lldb-dap.vsix" }, -- GitLab From e00a3ccf43563209b71c5b68f56d83f4052dca63 Mon Sep 17 00:00:00 2001 From: Peter Klausler <35819229+klausler@users.noreply.github.com> Date: Wed, 15 May 2024 15:44:37 -0700 Subject: [PATCH 038/403] [flang] New -fdebug-unparse-with-modules option (#91660) This option is a compilation action that parses a source file and performs semantic analysis on it, like the existing -fdebug-unparse option does. Its output, however, is preceded by the effective contents of all of the non-intrinsic modules on which it depends but does not define, transitively preceded by the closure of all of those modules' dependencies. The output from this option is therefore the analyzed parse tree for a source file encapsulated with all of its non-intrinsic module dependencies. This output may be useful for extracting code from large applications for use as an attachment to a bug report, or as input to a test case reduction tool for problem isolation. --- clang/include/clang/Driver/Options.td | 4 +- .../include/flang/Frontend/FrontendActions.h | 4 ++ .../include/flang/Frontend/FrontendOptions.h | 4 ++ .../flang/Semantics/unparse-with-symbols.h | 4 ++ flang/lib/Frontend/CompilerInvocation.cpp | 3 ++ flang/lib/Frontend/FrontendActions.cpp | 9 +++++ .../ExecuteCompilerInvocation.cpp | 2 + flang/lib/Semantics/mod-file.cpp | 20 +++++++++- flang/lib/Semantics/mod-file.h | 3 ++ flang/lib/Semantics/unparse-with-symbols.cpp | 38 +++++++++++++++++++ flang/test/Driver/unparse-with-modules.f90 | 34 +++++++++++++++++ 11 files changed, 122 insertions(+), 3 deletions(-) create mode 100644 flang/test/Driver/unparse-with-modules.f90 diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index c54eb543d658..e579f1a0a366 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -6647,7 +6647,9 @@ def fdebug_unparse : Flag<["-"], "fdebug-unparse">, Group, DocBrief<[{Run the parser and the semantic checks. Then unparse the parse-tree and output the generated Fortran source file.}]>; def fdebug_unparse_with_symbols : Flag<["-"], "fdebug-unparse-with-symbols">, Group, - HelpText<"Unparse and stop.">; + HelpText<"Unparse with symbols and stop.">; +def fdebug_unparse_with_modules : Flag<["-"], "fdebug-unparse-with-modules">, Group, + HelpText<"Unparse with dependent modules and stop.">; def fdebug_dump_symbols : Flag<["-"], "fdebug-dump-symbols">, Group, HelpText<"Dump symbols after the semantic analysis">; def fdebug_dump_parse_tree : Flag<["-"], "fdebug-dump-parse-tree">, Group, diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h index e2e859f3a81b..7823565eb815 100644 --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -108,6 +108,10 @@ class DebugUnparseWithSymbolsAction : public PrescanAndSemaAction { void executeAction() override; }; +class DebugUnparseWithModulesAction : public PrescanAndSemaAction { + void executeAction() override; +}; + class DebugUnparseAction : public PrescanAndSemaAction { void executeAction() override; }; diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h index 06b1318f243b..82ca99672ec6 100644 --- a/flang/include/flang/Frontend/FrontendOptions.h +++ b/flang/include/flang/Frontend/FrontendOptions.h @@ -63,6 +63,10 @@ enum ActionKind { /// Fortran source file DebugUnparseWithSymbols, + /// Parse, run semantics, and output a Fortran source file preceded + /// by all the necessary modules (transitively) + DebugUnparseWithModules, + /// Parse, run semantics and then output symbols from semantics DebugDumpSymbols, diff --git a/flang/include/flang/Semantics/unparse-with-symbols.h b/flang/include/flang/Semantics/unparse-with-symbols.h index d70110245e2b..5e18b3fc3063 100644 --- a/flang/include/flang/Semantics/unparse-with-symbols.h +++ b/flang/include/flang/Semantics/unparse-with-symbols.h @@ -21,8 +21,12 @@ struct Program; } namespace Fortran::semantics { +class SemanticsContext; void UnparseWithSymbols(llvm::raw_ostream &, const parser::Program &, parser::Encoding encoding = parser::Encoding::UTF_8); +void UnparseWithModules(llvm::raw_ostream &, SemanticsContext &, + const parser::Program &, + parser::Encoding encoding = parser::Encoding::UTF_8); } #endif // FORTRAN_SEMANTICS_UNPARSE_WITH_SYMBOLS_H_ diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index db7fd3cccc7a..e8a8c90045d9 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -488,6 +488,9 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args, case clang::driver::options::OPT_fdebug_unparse_with_symbols: opts.programAction = DebugUnparseWithSymbols; break; + case clang::driver::options::OPT_fdebug_unparse_with_modules: + opts.programAction = DebugUnparseWithModules; + break; case clang::driver::options::OPT_fdebug_dump_symbols: opts.programAction = DebugDumpSymbols; break; diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp index 2f65ab6102f4..4341c104a69d 100644 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -477,6 +477,15 @@ void DebugUnparseWithSymbolsAction::executeAction() { reportFatalSemanticErrors(); } +void DebugUnparseWithModulesAction::executeAction() { + auto &parseTree{*getInstance().getParsing().parseTree()}; + CompilerInstance &ci{getInstance()}; + Fortran::semantics::UnparseWithModules( + llvm::outs(), ci.getSemantics().context(), parseTree, + /*encoding=*/Fortran::parser::Encoding::UTF_8); + reportFatalSemanticErrors(); +} + void DebugDumpSymbolsAction::executeAction() { CompilerInstance &ci = this->getInstance(); diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 4cad640562c6..e2cbd5112d6e 100644 --- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -59,6 +59,8 @@ createFrontendAction(CompilerInstance &ci) { return std::make_unique(); case DebugUnparseWithSymbols: return std::make_unique(); + case DebugUnparseWithModules: + return std::make_unique(); case DebugDumpSymbols: return std::make_unique(); case DebugDumpParseTree: diff --git a/flang/lib/Semantics/mod-file.cpp b/flang/lib/Semantics/mod-file.cpp index e9aebe5b08f2..bb8c6c7567b8 100644 --- a/flang/lib/Semantics/mod-file.cpp +++ b/flang/lib/Semantics/mod-file.cpp @@ -132,11 +132,11 @@ static std::string ModFileName(const SourceName &name, // Write the module file for symbol, which must be a module or submodule. void ModFileWriter::Write(const Symbol &symbol) { - auto &module{symbol.get()}; + const auto &module{symbol.get()}; if (module.moduleFileHash()) { return; // already written } - auto *ancestor{module.ancestor()}; + const auto *ancestor{module.ancestor()}; isSubmodule_ = ancestor != nullptr; auto ancestorName{ancestor ? ancestor->GetName().value().ToString() : ""s}; auto path{context_.moduleDirectory() + '/' + @@ -151,6 +151,21 @@ void ModFileWriter::Write(const Symbol &symbol) { const_cast(module).set_moduleFileHash(checkSum); } +void ModFileWriter::WriteClosure(llvm::raw_ostream &out, const Symbol &symbol, + UnorderedSymbolSet &nonIntrinsicModulesWritten) { + if (!symbol.has() || symbol.owner().IsIntrinsicModules() || + !nonIntrinsicModulesWritten.insert(symbol).second) { + return; + } + PutSymbols(DEREF(symbol.scope())); + needsBuf_.clear(); // omit module checksums + auto str{GetAsString(symbol)}; + for (auto depRef : std::move(usedNonIntrinsicModules_)) { + WriteClosure(out, *depRef, nonIntrinsicModulesWritten); + } + out << std::move(str); +} + // Return the entire body of the module file // and clear saved uses, decls, and contains. std::string ModFileWriter::GetAsString(const Symbol &symbol) { @@ -710,6 +725,7 @@ void ModFileWriter::PutUse(const Symbol &symbol) { uses_ << "use,intrinsic::"; } else { uses_ << "use "; + usedNonIntrinsicModules_.insert(module); } uses_ << module.name() << ",only:"; PutGenericName(uses_, symbol); diff --git a/flang/lib/Semantics/mod-file.h b/flang/lib/Semantics/mod-file.h index b4ece4018c05..739add32c2e0 100644 --- a/flang/lib/Semantics/mod-file.h +++ b/flang/lib/Semantics/mod-file.h @@ -35,6 +35,8 @@ class ModFileWriter { public: explicit ModFileWriter(SemanticsContext &context) : context_{context} {} bool WriteAll(); + void WriteClosure(llvm::raw_ostream &, const Symbol &, + UnorderedSymbolSet &nonIntrinsicModulesWritten); private: SemanticsContext &context_; @@ -46,6 +48,7 @@ private: std::string containsBuf_; // Tracks nested DEC structures and fields of that type UnorderedSymbolSet emittedDECStructures_, emittedDECFields_; + UnorderedSymbolSet usedNonIntrinsicModules_; llvm::raw_string_ostream needs_{needsBuf_}; llvm::raw_string_ostream uses_{usesBuf_}; diff --git a/flang/lib/Semantics/unparse-with-symbols.cpp b/flang/lib/Semantics/unparse-with-symbols.cpp index 67016e85777c..c451f885c062 100644 --- a/flang/lib/Semantics/unparse-with-symbols.cpp +++ b/flang/lib/Semantics/unparse-with-symbols.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "flang/Semantics/unparse-with-symbols.h" +#include "mod-file.h" #include "flang/Parser/parse-tree-visitor.h" #include "flang/Parser/parse-tree.h" #include "flang/Parser/unparse.h" @@ -98,4 +99,41 @@ void UnparseWithSymbols(llvm::raw_ostream &out, const parser::Program &program, int indent) { visitor.PrintSymbols(location, out, indent); }}; parser::Unparse(out, program, encoding, false, true, &preStatement); } + +// UnparseWithModules() + +class UsedModuleVisitor { +public: + UnorderedSymbolSet &modulesUsed() { return modulesUsed_; } + UnorderedSymbolSet &modulesDefined() { return modulesDefined_; } + template bool Pre(const T &) { return true; } + template void Post(const T &) {} + void Post(const parser::ModuleStmt &module) { + if (module.v.symbol) { + modulesDefined_.insert(*module.v.symbol); + } + } + void Post(const parser::UseStmt &use) { + if (use.moduleName.symbol) { + modulesUsed_.insert(*use.moduleName.symbol); + } + } + +private: + UnorderedSymbolSet modulesUsed_; + UnorderedSymbolSet modulesDefined_; +}; + +void UnparseWithModules(llvm::raw_ostream &out, SemanticsContext &context, + const parser::Program &program, parser::Encoding encoding) { + UsedModuleVisitor visitor; + parser::Walk(program, visitor); + UnorderedSymbolSet nonIntrinsicModulesWritten{ + std::move(visitor.modulesDefined())}; + ModFileWriter writer{context}; + for (SymbolRef moduleRef : visitor.modulesUsed()) { + writer.WriteClosure(out, *moduleRef, nonIntrinsicModulesWritten); + } + parser::Unparse(out, program, encoding, false, true); +} } // namespace Fortran::semantics diff --git a/flang/test/Driver/unparse-with-modules.f90 b/flang/test/Driver/unparse-with-modules.f90 new file mode 100644 index 000000000000..53997f7804ef --- /dev/null +++ b/flang/test/Driver/unparse-with-modules.f90 @@ -0,0 +1,34 @@ +! RUN: %flang_fc1 -I %S/Inputs/module-dir -fdebug-unparse-with-modules %s | FileCheck %s +module m1 + use iso_fortran_env + use BasicTestModuleTwo + implicit none + type(t2) y + real(real32) x +end + +program test + use m1 + use BasicTestModuleTwo + implicit none + x = 123. + y = t2() +end + +!CHECK-NOT: module iso_fortran_env +!CHECK: module basictestmoduletwo +!CHECK: type::t2 +!CHECK: end type +!CHECK: end +!CHECK: module m1 +!CHECK: use :: iso_fortran_env +!CHECK: implicit none +!CHECK: real(kind=real32) x +!CHECK: end module +!CHECK: program test +!CHECK: use :: m1 +!CHECK: use :: basictestmoduletwo +!CHECK: implicit none +!CHECK: x = 123. +!CHECK: y = t2() +!CHECK: end program -- GitLab From 667d12f86e626173726e87e101626a9060b8d967 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 15 May 2024 18:55:53 -0400 Subject: [PATCH 039/403] [Clang][Sema] Do not mark template parameters in the exception specification as used during partial ordering (#91534) We do not deduce template arguments from the exception specification when determining the primary template of a function template specialization or when taking the address of a function template. Therefore, this patch changes `isAtLeastAsSpecializedAs` such that we do not mark template parameters in the exception specification as 'used' during partial ordering (per [temp.deduct.partial] p12) to prevent the following from being ambiguous: ``` template void f(U) noexcept(noexcept(T())); // #1 template void f(T*) noexcept; // #2 template<> void f(int*) noexcept; // currently ambiguous, selects #2 with this patch applied ``` Although there is no corresponding wording in the standard (see core issue filed here https://github.com/cplusplus/CWG/issues/537), this seems to be the intended behavior given the definition of _deduction substitution loci_ in [temp.deduct.general] p7 (and EDG does the same thing). --- clang/docs/ReleaseNotes.rst | 3 + clang/lib/Sema/SemaTemplateDeduction.cpp | 36 +++++++--- .../temp.deduct/temp.deduct.partial/p3.cpp | 72 +++++++++++++++++++ 3 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.partial/p3.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index ae699ebfc603..6f7e54252150 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -713,6 +713,9 @@ Bug Fixes to C++ Support - Correctly treat the compound statement of an ``if consteval`` as an immediate context. Fixes (#GH91509). - When partial ordering alias templates against template template parameters, allow pack expansions when the alias has a fixed-size parameter list. Fixes (#GH62529). +- Clang now ignores template parameters only used within the exception specification of candidate function + templates during partial ordering when deducing template arguments from a function declaration or when + taking the address of a function template. Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 853c0e1b5061..b5d405111fe4 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5453,7 +5453,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, // is used. if (DeduceTemplateArgumentsByTypeMatch( S, TemplateParams, FD2->getType(), FD1->getType(), Info, Deduced, - TDF_None, + TDF_AllowCompatibleFunctionType, /*PartialOrdering=*/true) != TemplateDeductionResult::Success) return false; break; @@ -5485,20 +5485,40 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc, switch (TPOC) { case TPOC_Call: for (unsigned I = 0, N = Args2.size(); I != N; ++I) - ::MarkUsedTemplateParameters(S.Context, Args2[I], false, - TemplateParams->getDepth(), - UsedParameters); + ::MarkUsedTemplateParameters(S.Context, Args2[I], /*OnlyDeduced=*/false, + TemplateParams->getDepth(), UsedParameters); break; case TPOC_Conversion: - ::MarkUsedTemplateParameters(S.Context, Proto2->getReturnType(), false, + ::MarkUsedTemplateParameters(S.Context, Proto2->getReturnType(), + /*OnlyDeduced=*/false, TemplateParams->getDepth(), UsedParameters); break; case TPOC_Other: - ::MarkUsedTemplateParameters(S.Context, FD2->getType(), false, - TemplateParams->getDepth(), - UsedParameters); + // We do not deduce template arguments from the exception specification + // when determining the primary template of a function template + // specialization or when taking the address of a function template. + // Therefore, we do not mark template parameters in the exception + // specification as used during partial ordering to prevent the following + // from being ambiguous: + // + // template + // void f(U) noexcept(noexcept(T())); // #1 + // + // template + // void f(T*) noexcept; // #2 + // + // template<> + // void f(int*) noexcept; // explicit specialization of #2 + // + // Although there is no corresponding wording in the standard, this seems + // to be the intended behavior given the definition of + // 'deduction substitution loci' in [temp.deduct]. + ::MarkUsedTemplateParameters( + S.Context, + S.Context.getFunctionTypeWithExceptionSpec(FD2->getType(), EST_None), + /*OnlyDeduced=*/false, TemplateParams->getDepth(), UsedParameters); break; } diff --git a/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.partial/p3.cpp b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.partial/p3.cpp new file mode 100644 index 000000000000..cc1d4ecda2ec --- /dev/null +++ b/clang/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.partial/p3.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// expected-no-diagnostics + +template +struct A { }; + +constexpr A a; +constexpr A b; + +constexpr int* x = nullptr; +constexpr short* y = nullptr; + +namespace ExplicitArgs { + template + constexpr int f(U) noexcept(noexcept(T())) { + return 0; + } + + template + constexpr int f(T*) noexcept { + return 1; + } + + template<> + constexpr int f(int*) noexcept { + return 2; + } + + static_assert(f(1) == 0); + static_assert(f(y) == 1); + static_assert(f(x) == 2); + + template + constexpr int g(U*) noexcept(noexcept(T())) { + return 3; + } + + template + constexpr int g(T) noexcept { + return 4; + } + + template<> + constexpr int g(int*) noexcept { + return 5; + } + + static_assert(g(y) == 3); + static_assert(g(1) == 4); + static_assert(g(x) == 5); +} // namespace ExplicitArgs + +namespace DeducedArgs { + template + constexpr int f(T, A) noexcept(B) { + return 0; + } + + template + constexpr int f(T*, A) noexcept(B && B) { + return 1; + } + + template<> + constexpr int f(int*, A) { + return 2; + } + + static_assert(f(x, a) == 0); + static_assert(f(y, a) == 1); + static_assert(f(x, a) == 2); +} // namespace DeducedArgs -- GitLab From 325d1d0b73aa6bff0ce4174b45a7601f6b32a793 Mon Sep 17 00:00:00 2001 From: Peter Klausler <35819229+klausler@users.noreply.github.com> Date: Wed, 15 May 2024 15:58:20 -0700 Subject: [PATCH 040/403] [flang] Fix purity checking for internal subprograms (#91759) ELEMENTAL internal subprograms are pure unless explicitly IMPURE. --- flang/lib/Semantics/check-purity.cpp | 10 +++-- flang/test/Semantics/pure02.f90 | 59 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) create mode 100644 flang/test/Semantics/pure02.f90 diff --git a/flang/lib/Semantics/check-purity.cpp b/flang/lib/Semantics/check-purity.cpp index 5176390f366b..55a9a2f10738 100644 --- a/flang/lib/Semantics/check-purity.cpp +++ b/flang/lib/Semantics/check-purity.cpp @@ -39,12 +39,16 @@ bool PurityChecker::InPureSubprogram() const { bool PurityChecker::HasPurePrefix( const std::list &prefixes) const { + bool result{false}; for (const parser::PrefixSpec &prefix : prefixes) { - if (std::holds_alternative(prefix.u)) { - return true; + if (std::holds_alternative(prefix.u)) { + return false; + } else if (std::holds_alternative(prefix.u) || + std::holds_alternative(prefix.u)) { + result = true; } } - return false; + return result; } void PurityChecker::Entered( diff --git a/flang/test/Semantics/pure02.f90 b/flang/test/Semantics/pure02.f90 new file mode 100644 index 000000000000..11dc0fd26829 --- /dev/null +++ b/flang/test/Semantics/pure02.f90 @@ -0,0 +1,59 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 +pure subroutine s1 + contains + !ERROR: An internal subprogram of a pure subprogram must also be pure + subroutine t1 + end + pure subroutine t2 ! ok + end + elemental subroutine t3(k) ! ok + integer, intent(in) :: k + end + !ERROR: An internal subprogram of a pure subprogram must also be pure + impure elemental subroutine t4(k) + integer, intent(in) :: k + end + !ERROR: An internal subprogram of a pure subprogram must also be pure + elemental impure subroutine t5(k) + integer, intent(in) :: k + end +end + +elemental subroutine s2(j) + integer, intent(in) :: j + contains + !ERROR: An internal subprogram of a pure subprogram must also be pure + subroutine t1 + end + pure subroutine t2 ! ok + end + elemental subroutine t3(k) ! ok + integer, intent(in) :: k + end + !ERROR: An internal subprogram of a pure subprogram must also be pure + impure elemental subroutine t4(k) + integer, intent(in) :: k + end + !ERROR: An internal subprogram of a pure subprogram must also be pure + elemental impure subroutine t5(k) + integer, intent(in) :: k + end +end + +impure elemental subroutine s3(j) + integer, intent(in) :: j + contains + subroutine t1 + end + pure subroutine t2 + end + elemental subroutine t3(k) + integer, intent(in) :: k + end + impure elemental subroutine t4(k) + integer, intent(in) :: k + end + elemental impure subroutine t5(k) + integer, intent(in) :: k + end +end -- GitLab From c227bf1b217598066acd32de8c9a75c2e0928f89 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov Date: Wed, 15 May 2024 20:01:17 -0300 Subject: [PATCH 041/403] [clang] Create new warning group for deprecation of '-fno-relaxed-template-template-args' (#92324) This allows the warning to be disabled in isolation, as it helps when treating them as errors. --- clang/docs/ReleaseNotes.rst | 3 ++- clang/include/clang/Basic/DiagnosticDriverKinds.td | 3 +++ clang/include/clang/Basic/DiagnosticGroups.td | 2 ++ clang/lib/Driver/ToolChains/Clang.cpp | 10 +++++++--- clang/test/Driver/frelaxed-template-template-args.cpp | 4 +++- 5 files changed, 17 insertions(+), 5 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6f7e54252150..089a85c8cb36 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -51,7 +51,8 @@ C++ Specific Potentially Breaking Changes - The behavior controlled by the `-frelaxed-template-template-args` flag is now on by default, and the flag is deprecated. Until the flag is finally removed, it's negative spelling can be used to obtain compatibility with previous - versions of clang. + versions of clang. The deprecation warning for the negative spelling can be + disabled with `-Wno-deprecated-no-relaxed-template-template-args`. - Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906). diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td index 9781fcaa4ff5..9d97a75f696f 100644 --- a/clang/include/clang/Basic/DiagnosticDriverKinds.td +++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td @@ -436,6 +436,9 @@ def warn_drv_clang_unsupported : Warning< "the clang compiler does not support '%0'">; def warn_drv_deprecated_arg : Warning< "argument '%0' is deprecated%select{|, use '%2' instead}1">, InGroup; +def warn_drv_deprecated_arg_no_relaxed_template_template_args : Warning< + "argument '-fno-relaxed-template-template-args' is deprecated">, + InGroup; def warn_drv_deprecated_custom : Warning< "argument '%0' is deprecated, %1">, InGroup; def warn_drv_assuming_mfloat_abi_is : Warning< diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 2beb1d45124b..4cb4f3d999f7 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -104,6 +104,7 @@ def EnumConversion : DiagGroup<"enum-conversion", [EnumEnumConversion, EnumFloatConversion, EnumCompareConditional]>; +def DeprecatedNoRelaxedTemplateTemplateArgs : DiagGroup<"deprecated-no-relaxed-template-template-args">; def ObjCSignedCharBoolImplicitIntConversion : DiagGroup<"objc-signed-char-bool-implicit-int-conversion">; def Shorten64To32 : DiagGroup<"shorten-64-to-32">; @@ -228,6 +229,7 @@ def Deprecated : DiagGroup<"deprecated", [DeprecatedAnonEnumEnumConversion, DeprecatedLiteralOperator, DeprecatedPragma, DeprecatedRegister, + DeprecatedNoRelaxedTemplateTemplateArgs, DeprecatedThisCapture, DeprecatedType, DeprecatedVolatile, diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 42feb1650574..c3e6d563f3bd 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7253,10 +7253,14 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, if (Arg *A = Args.getLastArg(options::OPT_frelaxed_template_template_args, options::OPT_fno_relaxed_template_template_args)) { - D.Diag(diag::warn_drv_deprecated_arg) - << A->getAsString(Args) << /*hasReplacement=*/false; - if (A->getOption().matches(options::OPT_fno_relaxed_template_template_args)) + if (A->getOption().matches( + options::OPT_fno_relaxed_template_template_args)) { + D.Diag(diag::warn_drv_deprecated_arg_no_relaxed_template_template_args); CmdArgs.push_back("-fno-relaxed-template-template-args"); + } else { + D.Diag(diag::warn_drv_deprecated_arg) + << A->getAsString(Args) << /*hasReplacement=*/false; + } } // -fsized-deallocation is off by default, as it is an ABI-breaking change for diff --git a/clang/test/Driver/frelaxed-template-template-args.cpp b/clang/test/Driver/frelaxed-template-template-args.cpp index 57fc4e3da6e5..7a7fd6f0bbc8 100644 --- a/clang/test/Driver/frelaxed-template-template-args.cpp +++ b/clang/test/Driver/frelaxed-template-template-args.cpp @@ -1,7 +1,9 @@ // RUN: %clang -fsyntax-only -### %s 2>&1 | FileCheck --check-prefix=CHECK-DEF %s // RUN: %clang -fsyntax-only -frelaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-ON %s // RUN: %clang -fsyntax-only -fno-relaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-OFF %s +// RUN: %clang -fsyntax-only -fno-relaxed-template-template-args -Wno-deprecated-no-relaxed-template-template-args %s 2>&1 | FileCheck --check-prefix=CHECK-DIS --allow-empty %s // CHECK-DEF-NOT: "-cc1"{{.*}} "-fno-relaxed-template-template-args" // CHECK-ON: warning: argument '-frelaxed-template-template-args' is deprecated [-Wdeprecated] -// CHECK-OFF: warning: argument '-fno-relaxed-template-template-args' is deprecated [-Wdeprecated] +// CHECK-OFF: warning: argument '-fno-relaxed-template-template-args' is deprecated [-Wdeprecated-no-relaxed-template-template-args] +// CHECK-DIS-NOT: warning: argument '-fno-relaxed-template-template-args' is deprecated -- GitLab From 7605ad8a2f95e3b37de83e7fb3d320efc74e0ccc Mon Sep 17 00:00:00 2001 From: Peter Klausler <35819229+klausler@users.noreply.github.com> Date: Wed, 15 May 2024 16:08:06 -0700 Subject: [PATCH 042/403] [flang] Always check procedure characterizability (#92008) When a procedure is defined with a subprogram but never referenced in a compilation unit, it may not be characterized until lowering, and any errors in characterization then may crash the compiler. So always ensure that procedure definitions are characterizable in declaration checking. Fixes https://github.com/llvm/llvm-project/issues/91845. --- flang/lib/Semantics/check-declarations.cpp | 9 +++++++++ flang/lib/Semantics/resolve-names.cpp | 3 +-- flang/test/Semantics/entry01.f90 | 2 ++ flang/test/Semantics/resolve102.f90 | 23 +++++----------------- 4 files changed, 17 insertions(+), 20 deletions(-) diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp index ce7870b8d54e..8d17989ac279 100644 --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -1357,6 +1357,15 @@ bool CheckHelper::IsResultOkToDiffer(const FunctionResult &result) { void CheckHelper::CheckSubprogram( const Symbol &symbol, const SubprogramDetails &details) { + // Evaluate a procedure definition's characteristics to flush out + // any errors that analysis might expose, in case this subprogram hasn't + // had any calls in this compilation unit that would have validated them. + if (!context_.HasError(symbol) && !details.isDummy() && + !details.isInterface() && !details.stmtFunction()) { + if (!Procedure::Characterize(symbol, foldingContext_)) { + context_.SetError(symbol); + } + } if (const Symbol *iface{FindSeparateModuleSubprogramInterface(&symbol)}) { SubprogramMatchHelper{*this}.Check(symbol, *iface); } diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index e2875081b732..5626f2a8be97 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -5013,8 +5013,7 @@ bool DeclarationVisitor::HasCycle( if (procsInCycle.count(*interface) > 0) { for (const auto &procInCycle : procsInCycle) { Say(procInCycle->name(), - "The interface for procedure '%s' is recursively " - "defined"_err_en_US, + "The interface for procedure '%s' is recursively defined"_err_en_US, procInCycle->name()); context().SetError(*procInCycle); } diff --git a/flang/test/Semantics/entry01.f90 b/flang/test/Semantics/entry01.f90 index 970cd109921a..765b18c2e81a 100644 --- a/flang/test/Semantics/entry01.f90 +++ b/flang/test/Semantics/entry01.f90 @@ -83,6 +83,7 @@ function ifunc() !ERROR: 'ibad1' is already declared in this scoping unit entry ibad1() result(ibad1res) ! C1570 !ERROR: 'ibad2' is already declared in this scoping unit + !ERROR: Procedure 'ibad2' is referenced before being sufficiently defined in a context where it must be so entry ibad2() !ERROR: ENTRY in a function may not have an alternate return dummy argument entry ibadalt(*) ! C1573 @@ -91,6 +92,7 @@ function ifunc() entry iok() !ERROR: Explicit RESULT('iok') of function 'isameres2' cannot have the same name as a distinct ENTRY into the same scope entry isameres2() result(iok) ! C1574 + !ERROR: Procedure 'iok2' is referenced before being sufficiently defined in a context where it must be so !ERROR: Explicit RESULT('iok2') of function 'isameres3' cannot have the same name as a distinct ENTRY into the same scope entry isameres3() result(iok2) ! C1574 !ERROR: 'iok2' is already declared in this scoping unit diff --git a/flang/test/Semantics/resolve102.f90 b/flang/test/Semantics/resolve102.f90 index 8f6e2246a57e..33cf6fa245ea 100644 --- a/flang/test/Semantics/resolve102.f90 +++ b/flang/test/Semantics/resolve102.f90 @@ -4,17 +4,12 @@ !ERROR: Procedure 'sub' is recursively defined. Procedures in the cycle: 'sub', 'p2' subroutine sub(p2) PROCEDURE(sub) :: p2 - - call sub() end subroutine subroutine circular - !ERROR: Procedure 'p' is recursively defined. Procedures in the cycle: 'p', 'sub', 'p2' procedure(sub) :: p - - call p(sub) - contains + !ERROR: Procedure 'sub' is recursively defined. Procedures in the cycle: 'p', 'sub', 'p2' subroutine sub(p2) procedure(p) :: p2 end subroutine @@ -41,11 +36,10 @@ end subroutine subroutine mutual Procedure(sub1) :: p - - Call p(sub) - contains !ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'p', 'sub1', 'arg' + !ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'sub1', 'arg', 'sub', 'p2' + !ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'sub1', 'arg' Subroutine sub1(arg) procedure(sub1) :: arg End Subroutine @@ -57,15 +51,14 @@ End subroutine subroutine mutual1 Procedure(sub1) :: p - - Call p(sub) - contains !ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'p', 'sub1', 'arg', 'sub', 'p2' + !ERROR: Procedure 'sub1' is recursively defined. Procedures in the cycle: 'sub1', 'arg', 'sub', 'p2' Subroutine sub1(arg) procedure(sub) :: arg End Subroutine + !ERROR: Procedure 'sub' is recursively defined. Procedures in the cycle: 'sub1', 'arg', 'sub', 'p2' Subroutine sub(p2) Procedure(sub1) :: p2 End Subroutine @@ -76,8 +69,6 @@ subroutine twoCycle !ERROR: The interface for procedure 'p2' is recursively defined procedure(p1) p2 procedure(p2) p1 - call p1 - call p2 end subroutine subroutine threeCycle @@ -87,9 +78,6 @@ subroutine threeCycle !ERROR: The interface for procedure 'p3' is recursively defined procedure(p2) p3 procedure(p3) p1 - call p1 - call p2 - call p3 end subroutine module mutualSpecExprs @@ -118,4 +106,3 @@ module genericInSpec ifunc = x end end - -- GitLab From 463f58a564a8d136b3e5d56d23bb86b99ab75245 Mon Sep 17 00:00:00 2001 From: Peter Klausler <35819229+klausler@users.noreply.github.com> Date: Wed, 15 May 2024 16:18:47 -0700 Subject: [PATCH 043/403] [flang] Further work on relaxing BIND(C) enforcement (#92029) When a BIND(C) interface or subprogram has a dummy argument whose derived type is not BIND(C) but meets the constraints and requirements of a BIND(C) type, accept it with a warning. --- flang/lib/Semantics/check-declarations.cpp | 16 +++++--- flang/test/Semantics/bind-c15.f90 | 45 ++++++++++++++++++++++ 2 files changed, 56 insertions(+), 5 deletions(-) create mode 100644 flang/test/Semantics/bind-c15.f90 diff --git a/flang/lib/Semantics/check-declarations.cpp b/flang/lib/Semantics/check-declarations.cpp index 8d17989ac279..527a1a9539aa 100644 --- a/flang/lib/Semantics/check-declarations.cpp +++ b/flang/lib/Semantics/check-declarations.cpp @@ -2891,7 +2891,8 @@ parser::Messages CheckHelper::WhyNotInteroperableDerivedType( } else { bool interoperableParent{true}; if (parent->symbol()) { - auto bad{WhyNotInteroperableDerivedType(*parent->symbol(), false)}; + auto bad{WhyNotInteroperableDerivedType( + *parent->symbol(), /*isError=*/false)}; if (bad.AnyFatalError()) { auto &msg{msgs.Say(symbol.name(), "The parent of an interoperable type is not interoperable"_err_en_US)}; @@ -2981,6 +2982,9 @@ parser::Messages CheckHelper::WhyNotInteroperableDerivedType( } } } + if (msgs.AnyFatalError()) { + examinedByWhyNotInteroperableDerivedType_.erase(symbol); + } return msgs; } @@ -3068,8 +3072,8 @@ void CheckHelper::CheckBindC(const Symbol &symbol) { } context_.SetError(symbol); } else if (auto bad{WhyNotInteroperableDerivedType( - derived->typeSymbol(), false)}; - !bad.empty()) { + derived->typeSymbol(), /*isError=*/false)}; + bad.AnyFatalError()) { if (auto *msg{messages_.Say(symbol.name(), "The derived type of an interoperable object must be interoperable, but is not"_err_en_US)}) { msg->Attach( @@ -3077,7 +3081,9 @@ void CheckHelper::CheckBindC(const Symbol &symbol) { bad.AttachTo(*msg, parser::Severity::None); } context_.SetError(symbol); - } else { + } else if (context_.ShouldWarn( + common::LanguageFeature::NonBindCInteroperability) && + !InModuleFile()) { if (auto *msg{messages_.Say(symbol.name(), "The derived type of an interoperable object should be BIND(C)"_warn_en_US)}) { msg->Attach(derived->typeSymbol().name(), "Non-BIND(C) type"_en_US); @@ -3151,7 +3157,7 @@ void CheckHelper::CheckBindC(const Symbol &symbol) { } } } else if (symbol.has()) { - if (auto msgs{WhyNotInteroperableDerivedType(symbol, false)}; + if (auto msgs{WhyNotInteroperableDerivedType(symbol, /*isError=*/false)}; !msgs.empty()) { bool anyFatal{msgs.AnyFatalError()}; if (msgs.AnyFatalError() || diff --git a/flang/test/Semantics/bind-c15.f90 b/flang/test/Semantics/bind-c15.f90 new file mode 100644 index 000000000000..9aaad52cc0e0 --- /dev/null +++ b/flang/test/Semantics/bind-c15.f90 @@ -0,0 +1,45 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic + +module m + type, bind(c) :: explicit_bind_c + real a + end type + type :: interoperable1 + type(explicit_bind_c) a + end type + type, extends(interoperable1) :: interoperable2 + real b + end type + type :: non_interoperable1 + real, allocatable :: a + end type + type :: non_interoperable2 + type(non_interoperable1) b + end type + interface + subroutine sub_bind_c_1(x_bind_c) bind(c) + import explicit_bind_c + type(explicit_bind_c), intent(in) :: x_bind_c + end + subroutine sub_bind_c_2(x_interop1) bind(c) + import interoperable1 + !WARNING: The derived type of an interoperable object should be BIND(C) + type(interoperable1), intent(in) :: x_interop1 + end + subroutine sub_bind_c_3(x_interop2) bind(c) + import interoperable2 + !WARNING: The derived type of an interoperable object should be BIND(C) + type(interoperable2), intent(in) :: x_interop2 + end + subroutine sub_bind_c_4(x_non_interop1) bind(c) + import non_interoperable1 + !ERROR: The derived type of an interoperable object must be interoperable, but is not + type(non_interoperable1), intent(in) :: x_non_interop1 + end + subroutine sub_bind_c_5(x_non_interop2) bind(c) + import non_interoperable2 + !ERROR: The derived type of an interoperable object must be interoperable, but is not + type(non_interoperable2), intent(in) :: x_non_interop2 + end + end interface +end -- GitLab From 5bbb63bd6d6d3929de643fcd88babbda20c97b69 Mon Sep 17 00:00:00 2001 From: Peter Klausler <35819229+klausler@users.noreply.github.com> Date: Wed, 15 May 2024 16:28:58 -0700 Subject: [PATCH 044/403] [flang] Parse REDUCE clauses in !$CUF KERNEL DO (#92154) A !$CUF KERNEL DO directive is allowed to have advisory REDUCE clauses similar to those in OpenACC and DO CONCURRENT. Parse and represent them. Semantic validation will follow. --- flang/include/flang/Parser/dump-parse-tree.h | 1 + flang/include/flang/Parser/parse-tree.h | 18 ++++- flang/lib/Parser/executable-parsers.cpp | 23 +++++-- flang/lib/Parser/openacc-parsers.cpp | 6 +- flang/lib/Parser/unparse.cpp | 36 +++++++++- flang/lib/Semantics/check-cuda.cpp | 44 ++++++++++++ flang/lib/Semantics/resolve-directives.h | 2 +- flang/lib/Semantics/resolve-names.cpp | 2 +- flang/test/Parser/cuf-sanity-common | 7 ++ flang/test/Parser/cuf-sanity-unparse.CUF | 6 ++ flang/test/Semantics/reduce.cuf | 72 ++++++++++++++++++++ 11 files changed, 199 insertions(+), 18 deletions(-) create mode 100644 flang/test/Semantics/reduce.cuf diff --git a/flang/include/flang/Parser/dump-parse-tree.h b/flang/include/flang/Parser/dump-parse-tree.h index 477d391277ee..68ae50c312cd 100644 --- a/flang/include/flang/Parser/dump-parse-tree.h +++ b/flang/include/flang/Parser/dump-parse-tree.h @@ -236,6 +236,7 @@ public: NODE(parser, CUFKernelDoConstruct) NODE(CUFKernelDoConstruct, StarOrExpr) NODE(CUFKernelDoConstruct, Directive) + NODE(parser, CUFReduction) NODE(parser, CycleStmt) NODE(parser, DataComponentDefStmt) NODE(parser, DataIDoObject) diff --git a/flang/include/flang/Parser/parse-tree.h b/flang/include/flang/Parser/parse-tree.h index c06354458379..0a40aa8b8f61 100644 --- a/flang/include/flang/Parser/parse-tree.h +++ b/flang/include/flang/Parser/parse-tree.h @@ -4303,12 +4303,23 @@ struct OpenACCConstruct { }; // CUF-kernel-do-construct -> -// !$CUF KERNEL DO [ (scalar-int-constant-expr) ] <<< grid, block [, stream] -// >>> do-construct +// !$CUF KERNEL DO [ (scalar-int-constant-expr) ] +// <<< grid, block [, stream] >>> +// [ cuf-reduction... ] +// do-construct // star-or-expr -> * | scalar-int-expr // grid -> * | scalar-int-expr | ( star-or-expr-list ) // block -> * | scalar-int-expr | ( star-or-expr-list ) // stream -> 0, scalar-int-expr | STREAM = scalar-int-expr +// cuf-reduction -> [ REDUCE | REDUCTION ] ( +// acc-reduction-op : scalar-variable-list ) + +struct CUFReduction { + TUPLE_CLASS_BOILERPLATE(CUFReduction); + using Operator = AccReductionOperator; + std::tuple>> t; +}; + struct CUFKernelDoConstruct { TUPLE_CLASS_BOILERPLATE(CUFKernelDoConstruct); WRAPPER_CLASS(StarOrExpr, std::optional); @@ -4316,7 +4327,8 @@ struct CUFKernelDoConstruct { TUPLE_CLASS_BOILERPLATE(Directive); CharBlock source; std::tuple, std::list, - std::list, std::optional> + std::list, std::optional, + std::list> t; }; std::tuple> t; diff --git a/flang/lib/Parser/executable-parsers.cpp b/flang/lib/Parser/executable-parsers.cpp index 07a570bd61e9..382a59341687 100644 --- a/flang/lib/Parser/executable-parsers.cpp +++ b/flang/lib/Parser/executable-parsers.cpp @@ -538,25 +538,34 @@ TYPE_CONTEXT_PARSER("UNLOCK statement"_en_US, construct("UNLOCK (" >> lockVariable, defaulted("," >> nonemptyList(statOrErrmsg)) / ")")) -// CUF-kernel-do-construct -> CUF-kernel-do-directive do-construct -// CUF-kernel-do-directive -> -// !$CUF KERNEL DO [ (scalar-int-constant-expr) ] <<< grid, block [, stream] -// >>> do-construct +// CUF-kernel-do-construct -> +// !$CUF KERNEL DO [ (scalar-int-constant-expr) ] +// <<< grid, block [, stream] >>> +// [ cuf-reduction... ] +// do-construct // star-or-expr -> * | scalar-int-expr // grid -> * | scalar-int-expr | ( star-or-expr-list ) // block -> * | scalar-int-expr | ( star-or-expr-list ) -// stream -> ( 0, | STREAM = ) scalar-int-expr +// stream -> 0, scalar-int-expr | STREAM = scalar-int-expr +// cuf-reduction -> [ REDUCTION | REDUCE ] ( +// acc-reduction-op : scalar-variable-list ) + constexpr auto starOrExpr{construct( "*" >> pure>() || applyFunction(presentOptional, scalarIntExpr))}; constexpr auto gridOrBlock{parenthesized(nonemptyList(starOrExpr)) || applyFunction(singletonList, starOrExpr)}; + +TYPE_PARSER(("REDUCTION"_tok || "REDUCE"_tok) >> + parenthesized(construct(Parser{}, + ":" >> nonemptyList(scalar(variable))))) + TYPE_PARSER(sourced(beginDirective >> "$CUF KERNEL DO"_tok >> construct( maybe(parenthesized(scalarIntConstantExpr)), "<<<" >> gridOrBlock, "," >> gridOrBlock, - maybe((", 0 ,"_tok || ", STREAM ="_tok) >> scalarIntExpr) / ">>>" / - endDirective))) + maybe((", 0 ,"_tok || ", STREAM ="_tok) >> scalarIntExpr) / ">>>", + many(Parser{}) / endDirective))) TYPE_CONTEXT_PARSER("!$CUF KERNEL DO construct"_en_US, extension(construct( Parser{}, diff --git a/flang/lib/Parser/openacc-parsers.cpp b/flang/lib/Parser/openacc-parsers.cpp index 946b33d0084a..3d919e29a248 100644 --- a/flang/lib/Parser/openacc-parsers.cpp +++ b/flang/lib/Parser/openacc-parsers.cpp @@ -19,9 +19,9 @@ // OpenACC Directives and Clauses namespace Fortran::parser { -constexpr auto startAccLine = skipStuffBeforeStatement >> - ("!$ACC "_sptok || "C$ACC "_sptok || "*$ACC "_sptok); -constexpr auto endAccLine = space >> endOfLine; +constexpr auto startAccLine{skipStuffBeforeStatement >> + ("!$ACC "_sptok || "C$ACC "_sptok || "*$ACC "_sptok)}; +constexpr auto endAccLine{space >> endOfLine}; // Autogenerated clauses parser. Information is taken from ACC.td and the // parser is generated by tablegen. diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index 3398b395f198..1639e900903f 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -2705,7 +2705,6 @@ public: void Unparse(const CLASS::ENUM &x) { Word(CLASS::EnumToString(x)); } WALK_NESTED_ENUM(AccDataModifier, Modifier) WALK_NESTED_ENUM(AccessSpec, Kind) // R807 - WALK_NESTED_ENUM(AccReductionOperator, Operator) WALK_NESTED_ENUM(common, TypeParamAttr) // R734 WALK_NESTED_ENUM(common, CUDADataAttr) // CUDA WALK_NESTED_ENUM(common, CUDASubprogramAttrs) // CUDA @@ -2736,6 +2735,31 @@ public: WALK_NESTED_ENUM(OmpOrderClause, Type) // OMP order-type WALK_NESTED_ENUM(OmpOrderModifier, Kind) // OMP order-modifier #undef WALK_NESTED_ENUM + void Unparse(const AccReductionOperator::Operator x) { + switch (x) { + case AccReductionOperator::Operator::Plus: + Word("+"); + break; + case AccReductionOperator::Operator::Multiply: + Word("*"); + break; + case AccReductionOperator::Operator::And: + Word(".AND."); + break; + case AccReductionOperator::Operator::Or: + Word(".OR."); + break; + case AccReductionOperator::Operator::Eqv: + Word(".EQV."); + break; + case AccReductionOperator::Operator::Neqv: + Word(".NEQV."); + break; + default: + Word(AccReductionOperator::EnumToString(x)); + break; + } + } void Unparse(const CUFKernelDoConstruct::StarOrExpr &x) { if (x.v) { @@ -2768,13 +2792,19 @@ public: if (const auto &stream{std::get<3>(x.t)}) { Word(",STREAM="), Walk(*stream); } - Word(">>>\n"); + Word(">>>"); + Walk(" ", std::get>(x.t), " "); + Word("\n"); } - void Unparse(const CUFKernelDoConstruct &x) { Walk(std::get(x.t)); Walk(std::get>(x.t)); } + void Unparse(const CUFReduction &x) { + Word("REDUCE("); + Walk(std::get(x.t)); + Walk(":", std::get>>(x.t), ",", ")"); + } void Done() const { CHECK(indent_ == 0); } diff --git a/flang/lib/Semantics/check-cuda.cpp b/flang/lib/Semantics/check-cuda.cpp index 96ab90239263..45217ed2e3cc 100644 --- a/flang/lib/Semantics/check-cuda.cpp +++ b/flang/lib/Semantics/check-cuda.cpp @@ -463,6 +463,46 @@ static int DoConstructTightNesting( return 1; } +static void CheckReduce( + SemanticsContext &context, const parser::CUFReduction &reduce) { + auto op{std::get(reduce.t).v}; + for (const auto &var : + std::get>>(reduce.t)) { + if (const auto &typedExprPtr{var.thing.typedExpr}; + typedExprPtr && typedExprPtr->v) { + const auto &expr{*typedExprPtr->v}; + if (auto type{expr.GetType()}) { + auto cat{type->category()}; + bool isOk{false}; + switch (op) { + case parser::AccReductionOperator::Operator::Plus: + case parser::AccReductionOperator::Operator::Multiply: + case parser::AccReductionOperator::Operator::Max: + case parser::AccReductionOperator::Operator::Min: + isOk = cat == TypeCategory::Integer || cat == TypeCategory::Real; + break; + case parser::AccReductionOperator::Operator::Iand: + case parser::AccReductionOperator::Operator::Ior: + case parser::AccReductionOperator::Operator::Ieor: + isOk = cat == TypeCategory::Integer; + break; + case parser::AccReductionOperator::Operator::And: + case parser::AccReductionOperator::Operator::Or: + case parser::AccReductionOperator::Operator::Eqv: + case parser::AccReductionOperator::Operator::Neqv: + isOk = cat == TypeCategory::Logical; + break; + } + if (!isOk) { + context.Say(var.thing.GetSource(), + "!$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type %s"_err_en_US, + type->AsFortran()); + } + } + } + } +} + void CUDAChecker::Enter(const parser::CUFKernelDoConstruct &x) { auto source{std::get(x.t).source}; const auto &directive{std::get(x.t)}; @@ -489,6 +529,10 @@ void CUDAChecker::Enter(const parser::CUFKernelDoConstruct &x) { if (innerBlock) { DeviceContextChecker{context_}.Check(*innerBlock); } + for (const auto &reduce : + std::get>(directive.t)) { + CheckReduce(context_, reduce); + } } void CUDAChecker::Enter(const parser::AssignmentStmt &x) { diff --git a/flang/lib/Semantics/resolve-directives.h b/flang/lib/Semantics/resolve-directives.h index 4aef8ad6c400..5a890c26aa33 100644 --- a/flang/lib/Semantics/resolve-directives.h +++ b/flang/lib/Semantics/resolve-directives.h @@ -21,7 +21,7 @@ class SemanticsContext; // Name resolution for OpenACC and OpenMP directives void ResolveAccParts( - SemanticsContext &, const parser::ProgramUnit &, Scope *topScope = {}); + SemanticsContext &, const parser::ProgramUnit &, Scope *topScope); void ResolveOmpParts(SemanticsContext &, const parser::ProgramUnit &); void ResolveOmpTopLevelParts(SemanticsContext &, const parser::Program &); diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index 5626f2a8be97..40eee89de131 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -8940,7 +8940,7 @@ bool ResolveNamesVisitor::Pre(const parser::ProgramUnit &x) { FinishSpecificationParts(root); ResolveExecutionParts(root); FinishExecutionParts(root); - ResolveAccParts(context(), x); + ResolveAccParts(context(), x, /*topScope=*/nullptr); ResolveOmpParts(context(), x); return false; } diff --git a/flang/test/Parser/cuf-sanity-common b/flang/test/Parser/cuf-sanity-common index b097a6aa3004..9d73204e3f5f 100644 --- a/flang/test/Parser/cuf-sanity-common +++ b/flang/test/Parser/cuf-sanity-common @@ -23,12 +23,19 @@ module m end subroutine subroutine test logical isPinned + real a(10), x, y, z !$cuf kernel do(1) <<<*, *, stream = 1>>> do j = 1, 10 end do !$cuf kernel do <<<1, (2, 3), stream = 1>>> do j = 1, 10 end do + !$cuf kernel do <<<*, *>>> reduce(+:x,y) reduce(*:z) + do j = 1, 10 + x = x + a(j) + y = y + a(j) + z = z * a(j) + end do call globalsub<<<1, 2>>> call globalsub<<<1, 2, 3>>> call globalsub<<<1, 2, 3, 4>>> diff --git a/flang/test/Parser/cuf-sanity-unparse.CUF b/flang/test/Parser/cuf-sanity-unparse.CUF index b6921e74fc05..d4be347dd044 100644 --- a/flang/test/Parser/cuf-sanity-unparse.CUF +++ b/flang/test/Parser/cuf-sanity-unparse.CUF @@ -34,6 +34,12 @@ include "cuf-sanity-common" !CHECK: !$CUF KERNEL DO <<<1_4,(2_4,3_4),STREAM=1_4>>> !CHECK: DO j=1_4,10_4 !CHECK: END DO +!CHECK: !$CUF KERNEL DO <<<*,*>>> REDUCE(+:x,y) REDUCE(*:z) +!CHECK: DO j=1_4,10_4 +!CHECK: x=x+a(int(j,kind=8)) +!CHECK: y=y+a(int(j,kind=8)) +!CHECK: z=z*a(int(j,kind=8)) +!CHECK: END DO !CHECK: CALL globalsub<<<1_4,2_4>>>() !CHECK: CALL globalsub<<<1_4,2_4,3_4>>>() !CHECK: CALL globalsub<<<1_4,2_4,3_4,4_4>>>() diff --git a/flang/test/Semantics/reduce.cuf b/flang/test/Semantics/reduce.cuf new file mode 100644 index 000000000000..95ff2e87c09b --- /dev/null +++ b/flang/test/Semantics/reduce.cuf @@ -0,0 +1,72 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 +subroutine s(n,m,a,l) + integer, intent(in) :: n + integer, intent(in) :: m(n) + real, intent(in) :: a(n) + logical, intent(in) :: l(n) + integer j, mr + real ar + logical lr +!$cuf kernel do <<<*,*>>> reduce (+:mr,ar) + do j=1,n; mr = mr + m(j); ar = ar + a(j); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type LOGICAL(4) +!$cuf kernel do <<<*,*>>> reduce (+:lr) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (*:mr,ar) + do j=1,n; mr = mr * m(j); ar = ar * a(j); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type LOGICAL(4) +!$cuf kernel do <<<*,*>>> reduce (*:lr) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (max:mr,ar) + do j=1,n; mr = max(mr,m(j)); ar = max(ar,a(j)); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type LOGICAL(4) +!$cuf kernel do <<<*,*>>> reduce (max:lr) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (min:mr,ar) + do j=1,n; mr = min(mr,m(j)); ar = min(ar,a(j)); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type LOGICAL(4) +!$cuf kernel do <<<*,*>>> reduce (min:lr) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (iand:mr) + do j=1,n; mr = iand(mr,m(j)); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type REAL(4) +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type LOGICAL(4) +!$cuf kernel do <<<*,*>>> reduce (iand:ar,lr) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (ieor:mr) + do j=1,n; mr = ieor(mr,m(j)); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type REAL(4) +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type LOGICAL(4) +!$cuf kernel do <<<*,*>>> reduce (ieor:ar,lr) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (ior:mr) + do j=1,n; mr = ior(mr,m(j)); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type REAL(4) +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type LOGICAL(4) +!$cuf kernel do <<<*,*>>> reduce (ior:ar,lr) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (.and.:lr) + do j=1,n; lr = lr .and. l(j); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type INTEGER(4) +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type REAL(4) +!$cuf kernel do <<<*,*>>> reduce (.and.:mr,ar) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (.eqv.:lr) + do j=1,n; lr = lr .eqv. l(j); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type INTEGER(4) +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type REAL(4) +!$cuf kernel do <<<*,*>>> reduce (.eqv.:mr,ar) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (.neqv.:lr) + do j=1,n; lr = lr .neqv. l(j); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type INTEGER(4) +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type REAL(4) +!$cuf kernel do <<<*,*>>> reduce (.neqv.:mr,ar) + do j=1,n; end do +!$cuf kernel do <<<*,*>>> reduce (.or.:lr) + do j=1,n; lr = lr .or. l(j); end do +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type INTEGER(4) +!ERROR: !$CUF KERNEL DO REDUCE operation is not acceptable for a variable with type REAL(4) +!$cuf kernel do <<<*,*>>> reduce (.or.:mr,ar) + do j=1,n; end do +end -- GitLab From 3ddfb6807e905868a3a9df71fa5ea87309181270 Mon Sep 17 00:00:00 2001 From: Peter Klausler <35819229+klausler@users.noreply.github.com> Date: Wed, 15 May 2024 16:41:12 -0700 Subject: [PATCH 045/403] [flang] Prevent crash from unfoldable TRANSFER() (#92282) When the MOLD= argument's type is polymorphic, the type of the result cannot be known at compilation time, so the call cannot be folded even when the SOURCE= is constant. Fixes https://github.com/llvm/llvm-project/issues/92264. --- flang/lib/Evaluate/fold.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/flang/lib/Evaluate/fold.cpp b/flang/lib/Evaluate/fold.cpp index ed8829581998..cf6262d9a7c6 100644 --- a/flang/lib/Evaluate/fold.cpp +++ b/flang/lib/Evaluate/fold.cpp @@ -272,6 +272,7 @@ std::optional> FoldTransfer( } } if (sourceBytes && IsActuallyConstant(*source) && moldType && extents && + !moldType->IsPolymorphic() && (moldLength || moldType->category() != TypeCategory::Character)) { std::size_t elements{ extents->empty() ? 1 : static_cast((*extents)[0])}; -- GitLab From c87b1ca4edefe3c267a20f28eaf79f6b83d36c66 Mon Sep 17 00:00:00 2001 From: Ellis Hoag Date: Wed, 15 May 2024 18:41:25 -0500 Subject: [PATCH 046/403] [InstrProf] Fix bug when clearing traces with samples (#92310) The `--temporal-profile-max-trace-length=0` flag in the `llvm-profdata merge` command is used to remove traces from a profile. There was a bug where traces would not be cleared if the profile was already sampled. This patch fixes that. --- llvm/lib/ProfileData/InstrProfWriter.cpp | 11 ++++++----- llvm/test/tools/llvm-profdata/trace-limit.proftext | 6 +++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/llvm/lib/ProfileData/InstrProfWriter.cpp b/llvm/lib/ProfileData/InstrProfWriter.cpp index b61c59aacc0f..c941b9d89df3 100644 --- a/llvm/lib/ProfileData/InstrProfWriter.cpp +++ b/llvm/lib/ProfileData/InstrProfWriter.cpp @@ -320,11 +320,8 @@ void InstrProfWriter::addBinaryIds(ArrayRef BIs) { } void InstrProfWriter::addTemporalProfileTrace(TemporalProfTraceTy Trace) { - if (Trace.FunctionNameRefs.size() > MaxTemporalProfTraceLength) - Trace.FunctionNameRefs.resize(MaxTemporalProfTraceLength); - if (Trace.FunctionNameRefs.empty()) - return; - + assert(Trace.FunctionNameRefs.size() <= MaxTemporalProfTraceLength); + assert(!Trace.FunctionNameRefs.empty()); if (TemporalProfTraceStreamSize < TemporalProfTraceReservoirSize) { // Simply append the trace if we have not yet hit our reservoir size limit. TemporalProfTraces.push_back(std::move(Trace)); @@ -341,6 +338,10 @@ void InstrProfWriter::addTemporalProfileTrace(TemporalProfTraceTy Trace) { void InstrProfWriter::addTemporalProfileTraces( SmallVectorImpl &SrcTraces, uint64_t SrcStreamSize) { + for (auto &Trace : SrcTraces) + if (Trace.FunctionNameRefs.size() > MaxTemporalProfTraceLength) + Trace.FunctionNameRefs.resize(MaxTemporalProfTraceLength); + llvm::erase_if(SrcTraces, [](auto &T) { return T.FunctionNameRefs.empty(); }); // Assume that the source has the same reservoir size as the destination to // avoid needing to record it in the indexed profile format. bool IsDestSampled = diff --git a/llvm/test/tools/llvm-profdata/trace-limit.proftext b/llvm/test/tools/llvm-profdata/trace-limit.proftext index cf6edd648b23..e246ee890ba3 100644 --- a/llvm/test/tools/llvm-profdata/trace-limit.proftext +++ b/llvm/test/tools/llvm-profdata/trace-limit.proftext @@ -1,13 +1,17 @@ # RUN: llvm-profdata merge --temporal-profile-max-trace-length=0 %s -o %t.profdata # RUN: llvm-profdata show --temporal-profile-traces %t.profdata | FileCheck %s --check-prefix=NONE +# RUN: llvm-profdata merge --temporal-profile-trace-reservoir-size=2 %s %s %s %s -o %t.profdata +# RUN: llvm-profdata merge --temporal-profile-trace-reservoir-size=2 --temporal-profile-max-trace-length=0 %t.profdata -o %t.profdata +# RUN: llvm-profdata show --temporal-profile-traces %t.profdata | FileCheck %s --check-prefix=NONE + # RUN: llvm-profdata merge --temporal-profile-max-trace-length=2 %s -o %t.profdata # RUN: llvm-profdata show --temporal-profile-traces %t.profdata | FileCheck %s --check-prefixes=CHECK,SOME # RUN: llvm-profdata merge --temporal-profile-max-trace-length=1000 %s -o %t.profdata # RUN: llvm-profdata show --temporal-profile-traces %t.profdata | FileCheck %s --check-prefixes=CHECK,ALL -# NONE: Temporal Profile Traces (samples=0 seen=0): +# NONE: Temporal Profile Traces (samples=0 # CHECK: Temporal Profile Traces (samples=1 seen=1): # SOME: Trace 0 (weight=1 count=2): # ALL: Trace 0 (weight=1 count=3): -- GitLab From c00e012bcf5da384a3e7339dc2e046779b339063 Mon Sep 17 00:00:00 2001 From: Mircea Trofin Date: Wed, 15 May 2024 17:03:09 -0700 Subject: [PATCH 047/403] [ctx_profile] Follow the pattern elsewhere for choosing the block IDs This was an oversight in #91859. Using the subblock ID mechanism other places that use the bitstream APIs (e.g. `BitstreamRemarkSerializer`) use. --- llvm/include/llvm/ProfileData/PGOCtxProfWriter.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h index 15578c51a495..edcf02c09469 100644 --- a/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h +++ b/llvm/include/llvm/ProfileData/PGOCtxProfWriter.h @@ -13,6 +13,7 @@ #ifndef LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_ #define LLVM_PROFILEDATA_PGOCTXPROFWRITER_H_ +#include "llvm/Bitstream/BitCodeEnums.h" #include "llvm/Bitstream/BitstreamWriter.h" #include "llvm/ProfileData/CtxInstrContextNode.h" @@ -20,7 +21,7 @@ namespace llvm { enum PGOCtxProfileRecords { Invalid = 0, Version, Guid, CalleeIndex, Counters }; enum PGOCtxProfileBlockIDs { - ProfileMetadataBlockID = 100, + ProfileMetadataBlockID = bitc::FIRST_APPLICATION_BLOCKID, ContextNodeBlockID = ProfileMetadataBlockID + 1 }; -- GitLab From 772b1b0cb26c66804d0a7e416dc7a5742b7f8db2 Mon Sep 17 00:00:00 2001 From: ChiaHungDuan Date: Wed, 15 May 2024 17:13:08 -0700 Subject: [PATCH 048/403] [scudo] Move the chunk update into functions (#83493) The code paths for mte enabled and disabled were interleaving and which increases the difficulty of reading each path in both source level and assembly level. In this change, we move the parts that they have different logic into functions and minor refactors on the code structure. --- compiler-rt/lib/scudo/standalone/combined.h | 371 ++++++++++++-------- 1 file changed, 221 insertions(+), 150 deletions(-) diff --git a/compiler-rt/lib/scudo/standalone/combined.h b/compiler-rt/lib/scudo/standalone/combined.h index 927513dea92d..15a199ae0349 100644 --- a/compiler-rt/lib/scudo/standalone/combined.h +++ b/compiler-rt/lib/scudo/standalone/combined.h @@ -410,133 +410,18 @@ public: reportOutOfMemory(NeededSize); } - const uptr BlockUptr = reinterpret_cast(Block); - const uptr UnalignedUserPtr = BlockUptr + Chunk::getHeaderSize(); - const uptr UserPtr = roundUp(UnalignedUserPtr, Alignment); - - void *Ptr = reinterpret_cast(UserPtr); - void *TaggedPtr = Ptr; - if (LIKELY(ClassId)) { - // We only need to zero or tag the contents for Primary backed - // allocations. We only set tags for primary allocations in order to avoid - // faulting potentially large numbers of pages for large secondary - // allocations. We assume that guard pages are enough to protect these - // allocations. - // - // FIXME: When the kernel provides a way to set the background tag of a - // mapping, we should be able to tag secondary allocations as well. - // - // When memory tagging is enabled, zeroing the contents is done as part of - // setting the tag. - if (UNLIKELY(useMemoryTagging(Options))) { - uptr PrevUserPtr; - Chunk::UnpackedHeader Header; - const uptr BlockSize = PrimaryT::getSizeByClassId(ClassId); - const uptr BlockEnd = BlockUptr + BlockSize; - // If possible, try to reuse the UAF tag that was set by deallocate(). - // For simplicity, only reuse tags if we have the same start address as - // the previous allocation. This handles the majority of cases since - // most allocations will not be more aligned than the minimum alignment. - // - // We need to handle situations involving reclaimed chunks, and retag - // the reclaimed portions if necessary. In the case where the chunk is - // fully reclaimed, the chunk's header will be zero, which will trigger - // the code path for new mappings and invalid chunks that prepares the - // chunk from scratch. There are three possibilities for partial - // reclaiming: - // - // (1) Header was reclaimed, data was partially reclaimed. - // (2) Header was not reclaimed, all data was reclaimed (e.g. because - // data started on a page boundary). - // (3) Header was not reclaimed, data was partially reclaimed. - // - // Case (1) will be handled in the same way as for full reclaiming, - // since the header will be zero. - // - // We can detect case (2) by loading the tag from the start - // of the chunk. If it is zero, it means that either all data was - // reclaimed (since we never use zero as the chunk tag), or that the - // previous allocation was of size zero. Either way, we need to prepare - // a new chunk from scratch. - // - // We can detect case (3) by moving to the next page (if covered by the - // chunk) and loading the tag of its first granule. If it is zero, it - // means that all following pages may need to be retagged. On the other - // hand, if it is nonzero, we can assume that all following pages are - // still tagged, according to the logic that if any of the pages - // following the next page were reclaimed, the next page would have been - // reclaimed as well. - uptr TaggedUserPtr; - if (getChunkFromBlock(BlockUptr, &PrevUserPtr, &Header) && - PrevUserPtr == UserPtr && - (TaggedUserPtr = loadTag(UserPtr)) != UserPtr) { - uptr PrevEnd = TaggedUserPtr + Header.SizeOrUnusedBytes; - const uptr NextPage = roundUp(TaggedUserPtr, getPageSizeCached()); - if (NextPage < PrevEnd && loadTag(NextPage) != NextPage) - PrevEnd = NextPage; - TaggedPtr = reinterpret_cast(TaggedUserPtr); - resizeTaggedChunk(PrevEnd, TaggedUserPtr + Size, Size, BlockEnd); - if (UNLIKELY(FillContents != NoFill && !Header.OriginOrWasZeroed)) { - // If an allocation needs to be zeroed (i.e. calloc) we can normally - // avoid zeroing the memory now since we can rely on memory having - // been zeroed on free, as this is normally done while setting the - // UAF tag. But if tagging was disabled per-thread when the memory - // was freed, it would not have been retagged and thus zeroed, and - // therefore it needs to be zeroed now. - memset(TaggedPtr, 0, - Min(Size, roundUp(PrevEnd - TaggedUserPtr, - archMemoryTagGranuleSize()))); - } else if (Size) { - // Clear any stack metadata that may have previously been stored in - // the chunk data. - memset(TaggedPtr, 0, archMemoryTagGranuleSize()); - } - } else { - const uptr OddEvenMask = - computeOddEvenMaskForPointerMaybe(Options, BlockUptr, ClassId); - TaggedPtr = prepareTaggedChunk(Ptr, Size, OddEvenMask, BlockEnd); - } - storePrimaryAllocationStackMaybe(Options, Ptr); - } else { - Block = addHeaderTag(Block); - Ptr = addHeaderTag(Ptr); - if (UNLIKELY(FillContents != NoFill)) { - // This condition is not necessarily unlikely, but since memset is - // costly, we might as well mark it as such. - memset(Block, FillContents == ZeroFill ? 0 : PatternFillByte, - PrimaryT::getSizeByClassId(ClassId)); - } - } - } else { - Block = addHeaderTag(Block); - Ptr = addHeaderTag(Ptr); - if (UNLIKELY(useMemoryTagging(Options))) { - storeTags(reinterpret_cast(Block), reinterpret_cast(Ptr)); - storeSecondaryAllocationStackMaybe(Options, Ptr, Size); - } + const uptr UserPtr = roundUp( + reinterpret_cast(Block) + Chunk::getHeaderSize(), Alignment); + const uptr SizeOrUnusedBytes = + ClassId ? Size : SecondaryBlockEnd - (UserPtr + Size); + + if (LIKELY(!useMemoryTagging(Options))) { + return initChunk(ClassId, Origin, Block, UserPtr, SizeOrUnusedBytes, + FillContents); } - Chunk::UnpackedHeader Header = {}; - if (UNLIKELY(UnalignedUserPtr != UserPtr)) { - const uptr Offset = UserPtr - UnalignedUserPtr; - DCHECK_GE(Offset, 2 * sizeof(u32)); - // The BlockMarker has no security purpose, but is specifically meant for - // the chunk iteration function that can be used in debugging situations. - // It is the only situation where we have to locate the start of a chunk - // based on its block address. - reinterpret_cast(Block)[0] = BlockMarker; - reinterpret_cast(Block)[1] = static_cast(Offset); - Header.Offset = (Offset >> MinAlignmentLog) & Chunk::OffsetMask; - } - Header.ClassId = ClassId & Chunk::ClassIdMask; - Header.State = Chunk::State::Allocated; - Header.OriginOrWasZeroed = Origin & Chunk::OriginMask; - Header.SizeOrUnusedBytes = - (ClassId ? Size : SecondaryBlockEnd - (UserPtr + Size)) & - Chunk::SizeOrUnusedBytesMask; - Chunk::storeHeader(Cookie, Ptr, &Header); - - return TaggedPtr; + return initChunkWithMemoryTagging(ClassId, Origin, Block, UserPtr, Size, + SizeOrUnusedBytes, FillContents); } NOINLINE void deallocate(void *Ptr, Chunk::Origin Origin, uptr DeleteSize = 0, @@ -1163,6 +1048,175 @@ private: reinterpret_cast(Ptr) - SizeOrUnusedBytes; } + ALWAYS_INLINE void *initChunk(const uptr ClassId, const Chunk::Origin Origin, + void *Block, const uptr UserPtr, + const uptr SizeOrUnusedBytes, + const FillContentsMode FillContents) { + Block = addHeaderTag(Block); + // Only do content fill when it's from primary allocator because secondary + // allocator has filled the content. + if (ClassId != 0 && UNLIKELY(FillContents != NoFill)) { + // This condition is not necessarily unlikely, but since memset is + // costly, we might as well mark it as such. + memset(Block, FillContents == ZeroFill ? 0 : PatternFillByte, + PrimaryT::getSizeByClassId(ClassId)); + } + + Chunk::UnpackedHeader Header = {}; + + const uptr DefaultAlignedPtr = + reinterpret_cast(Block) + Chunk::getHeaderSize(); + if (UNLIKELY(DefaultAlignedPtr != UserPtr)) { + const uptr Offset = UserPtr - DefaultAlignedPtr; + DCHECK_GE(Offset, 2 * sizeof(u32)); + // The BlockMarker has no security purpose, but is specifically meant for + // the chunk iteration function that can be used in debugging situations. + // It is the only situation where we have to locate the start of a chunk + // based on its block address. + reinterpret_cast(Block)[0] = BlockMarker; + reinterpret_cast(Block)[1] = static_cast(Offset); + Header.Offset = (Offset >> MinAlignmentLog) & Chunk::OffsetMask; + } + + Header.ClassId = ClassId & Chunk::ClassIdMask; + Header.State = Chunk::State::Allocated; + Header.OriginOrWasZeroed = Origin & Chunk::OriginMask; + Header.SizeOrUnusedBytes = SizeOrUnusedBytes & Chunk::SizeOrUnusedBytesMask; + Chunk::storeHeader(Cookie, reinterpret_cast(addHeaderTag(UserPtr)), + &Header); + + return reinterpret_cast(UserPtr); + } + + NOINLINE void * + initChunkWithMemoryTagging(const uptr ClassId, const Chunk::Origin Origin, + void *Block, const uptr UserPtr, const uptr Size, + const uptr SizeOrUnusedBytes, + const FillContentsMode FillContents) { + const Options Options = Primary.Options.load(); + DCHECK(useMemoryTagging(Options)); + + void *Ptr = reinterpret_cast(UserPtr); + void *TaggedPtr = Ptr; + + if (LIKELY(ClassId)) { + // Init the primary chunk. + // + // We only need to zero or tag the contents for Primary backed + // allocations. We only set tags for primary allocations in order to avoid + // faulting potentially large numbers of pages for large secondary + // allocations. We assume that guard pages are enough to protect these + // allocations. + // + // FIXME: When the kernel provides a way to set the background tag of a + // mapping, we should be able to tag secondary allocations as well. + // + // When memory tagging is enabled, zeroing the contents is done as part of + // setting the tag. + + Chunk::UnpackedHeader Header; + const uptr BlockSize = PrimaryT::getSizeByClassId(ClassId); + const uptr BlockUptr = reinterpret_cast(Block); + const uptr BlockEnd = BlockUptr + BlockSize; + // If possible, try to reuse the UAF tag that was set by deallocate(). + // For simplicity, only reuse tags if we have the same start address as + // the previous allocation. This handles the majority of cases since + // most allocations will not be more aligned than the minimum alignment. + // + // We need to handle situations involving reclaimed chunks, and retag + // the reclaimed portions if necessary. In the case where the chunk is + // fully reclaimed, the chunk's header will be zero, which will trigger + // the code path for new mappings and invalid chunks that prepares the + // chunk from scratch. There are three possibilities for partial + // reclaiming: + // + // (1) Header was reclaimed, data was partially reclaimed. + // (2) Header was not reclaimed, all data was reclaimed (e.g. because + // data started on a page boundary). + // (3) Header was not reclaimed, data was partially reclaimed. + // + // Case (1) will be handled in the same way as for full reclaiming, + // since the header will be zero. + // + // We can detect case (2) by loading the tag from the start + // of the chunk. If it is zero, it means that either all data was + // reclaimed (since we never use zero as the chunk tag), or that the + // previous allocation was of size zero. Either way, we need to prepare + // a new chunk from scratch. + // + // We can detect case (3) by moving to the next page (if covered by the + // chunk) and loading the tag of its first granule. If it is zero, it + // means that all following pages may need to be retagged. On the other + // hand, if it is nonzero, we can assume that all following pages are + // still tagged, according to the logic that if any of the pages + // following the next page were reclaimed, the next page would have been + // reclaimed as well. + uptr TaggedUserPtr; + uptr PrevUserPtr; + if (getChunkFromBlock(BlockUptr, &PrevUserPtr, &Header) && + PrevUserPtr == UserPtr && + (TaggedUserPtr = loadTag(UserPtr)) != UserPtr) { + uptr PrevEnd = TaggedUserPtr + Header.SizeOrUnusedBytes; + const uptr NextPage = roundUp(TaggedUserPtr, getPageSizeCached()); + if (NextPage < PrevEnd && loadTag(NextPage) != NextPage) + PrevEnd = NextPage; + TaggedPtr = reinterpret_cast(TaggedUserPtr); + resizeTaggedChunk(PrevEnd, TaggedUserPtr + Size, Size, BlockEnd); + if (UNLIKELY(FillContents != NoFill && !Header.OriginOrWasZeroed)) { + // If an allocation needs to be zeroed (i.e. calloc) we can normally + // avoid zeroing the memory now since we can rely on memory having + // been zeroed on free, as this is normally done while setting the + // UAF tag. But if tagging was disabled per-thread when the memory + // was freed, it would not have been retagged and thus zeroed, and + // therefore it needs to be zeroed now. + memset(TaggedPtr, 0, + Min(Size, roundUp(PrevEnd - TaggedUserPtr, + archMemoryTagGranuleSize()))); + } else if (Size) { + // Clear any stack metadata that may have previously been stored in + // the chunk data. + memset(TaggedPtr, 0, archMemoryTagGranuleSize()); + } + } else { + const uptr OddEvenMask = + computeOddEvenMaskForPointerMaybe(Options, BlockUptr, ClassId); + TaggedPtr = prepareTaggedChunk(Ptr, Size, OddEvenMask, BlockEnd); + } + storePrimaryAllocationStackMaybe(Options, Ptr); + } else { + // Init the secondary chunk. + + Block = addHeaderTag(Block); + Ptr = addHeaderTag(Ptr); + storeTags(reinterpret_cast(Block), reinterpret_cast(Ptr)); + storeSecondaryAllocationStackMaybe(Options, Ptr, Size); + } + + Chunk::UnpackedHeader Header = {}; + + const uptr DefaultAlignedPtr = + reinterpret_cast(Block) + Chunk::getHeaderSize(); + if (UNLIKELY(DefaultAlignedPtr != UserPtr)) { + const uptr Offset = UserPtr - DefaultAlignedPtr; + DCHECK_GE(Offset, 2 * sizeof(u32)); + // The BlockMarker has no security purpose, but is specifically meant for + // the chunk iteration function that can be used in debugging situations. + // It is the only situation where we have to locate the start of a chunk + // based on its block address. + reinterpret_cast(Block)[0] = BlockMarker; + reinterpret_cast(Block)[1] = static_cast(Offset); + Header.Offset = (Offset >> MinAlignmentLog) & Chunk::OffsetMask; + } + + Header.ClassId = ClassId & Chunk::ClassIdMask; + Header.State = Chunk::State::Allocated; + Header.OriginOrWasZeroed = Origin & Chunk::OriginMask; + Header.SizeOrUnusedBytes = SizeOrUnusedBytes & Chunk::SizeOrUnusedBytesMask; + Chunk::storeHeader(Cookie, Ptr, &Header); + + return TaggedPtr; + } + void quarantineOrDeallocateChunk(const Options &Options, void *TaggedPtr, Chunk::UnpackedHeader *Header, uptr Size) NO_THREAD_SAFETY_ANALYSIS { @@ -1177,31 +1231,23 @@ private: Header->State = Chunk::State::Available; else Header->State = Chunk::State::Quarantined; - Header->OriginOrWasZeroed = useMemoryTagging(Options) && - Header->ClassId && - !TSDRegistry.getDisableMemInit(); - Chunk::storeHeader(Cookie, Ptr, Header); - if (UNLIKELY(useMemoryTagging(Options))) { - u8 PrevTag = extractTag(reinterpret_cast(TaggedPtr)); - storeDeallocationStackMaybe(Options, Ptr, PrevTag, Size); - if (Header->ClassId) { - if (!TSDRegistry.getDisableMemInit()) { - uptr TaggedBegin, TaggedEnd; - const uptr OddEvenMask = computeOddEvenMaskForPointerMaybe( - Options, reinterpret_cast(getBlockBegin(Ptr, Header)), - Header->ClassId); - // Exclude the previous tag so that immediate use after free is - // detected 100% of the time. - setRandomTag(Ptr, Size, OddEvenMask | (1UL << PrevTag), &TaggedBegin, - &TaggedEnd); - } - } + void *BlockBegin; + if (LIKELY(!useMemoryTagging(Options))) { + Header->OriginOrWasZeroed = 0U; + if (BypassQuarantine && allocatorSupportsMemoryTagging()) + Ptr = untagPointer(Ptr); + BlockBegin = getBlockBegin(Ptr, Header); + } else { + Header->OriginOrWasZeroed = + Header->ClassId && !TSDRegistry.getDisableMemInit(); + BlockBegin = + retagBlock(Options, TaggedPtr, Ptr, Header, Size, BypassQuarantine); } + + Chunk::storeHeader(Cookie, Ptr, Header); + if (BypassQuarantine) { - if (allocatorSupportsMemoryTagging()) - Ptr = untagPointer(Ptr); - void *BlockBegin = getBlockBegin(Ptr, Header); const uptr ClassId = Header->ClassId; if (LIKELY(ClassId)) { bool CacheDrained; @@ -1216,9 +1262,6 @@ private: if (CacheDrained) Primary.tryReleaseToOS(ClassId, ReleaseToOS::Normal); } else { - if (UNLIKELY(useMemoryTagging(Options))) - storeTags(reinterpret_cast(BlockBegin), - reinterpret_cast(Ptr)); Secondary.deallocate(Options, BlockBegin); } } else { @@ -1228,6 +1271,34 @@ private: } } + NOINLINE void *retagBlock(const Options &Options, void *TaggedPtr, void *&Ptr, + Chunk::UnpackedHeader *Header, const uptr Size, + bool BypassQuarantine) { + DCHECK(useMemoryTagging(Options)); + + const u8 PrevTag = extractTag(reinterpret_cast(TaggedPtr)); + storeDeallocationStackMaybe(Options, Ptr, PrevTag, Size); + if (Header->ClassId && !TSDRegistry.getDisableMemInit()) { + uptr TaggedBegin, TaggedEnd; + const uptr OddEvenMask = computeOddEvenMaskForPointerMaybe( + Options, reinterpret_cast(getBlockBegin(Ptr, Header)), + Header->ClassId); + // Exclude the previous tag so that immediate use after free is + // detected 100% of the time. + setRandomTag(Ptr, Size, OddEvenMask | (1UL << PrevTag), &TaggedBegin, + &TaggedEnd); + } + + Ptr = untagPointer(Ptr); + void *BlockBegin = getBlockBegin(Ptr, Header); + if (BypassQuarantine && !Header->ClassId) { + storeTags(reinterpret_cast(BlockBegin), + reinterpret_cast(Ptr)); + } + + return BlockBegin; + } + bool getChunkFromBlock(uptr Block, uptr *Chunk, Chunk::UnpackedHeader *Header) { *Chunk = -- GitLab From c6e787f771d1f9d6a846b2d9b8db6adcd87e8dba Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 15 May 2024 17:52:59 -0700 Subject: [PATCH 049/403] [MCAsmParser] .rept/.irp/.irpc: remove excess tail EOL in expansion ``` .irp foo,1 nop .endr nop ``` expands to an excess EOL between two nop lines. Remove the excess EOL. --- llvm/lib/MC/MCParser/AsmParser.cpp | 31 +++++++++++------------------ llvm/test/MC/AsmParser/macro-rept.s | 14 ++++++------- 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 8d9acd54e879..46c1caa940c5 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -5629,27 +5629,20 @@ MCAsmMacro *AsmParser::parseMacroLikeBody(SMLoc DirectiveLoc) { return nullptr; } - if (Lexer.is(AsmToken::Identifier) && - (getTok().getIdentifier() == ".rep" || - getTok().getIdentifier() == ".rept" || - getTok().getIdentifier() == ".irp" || - getTok().getIdentifier() == ".irpc")) { - ++NestLevel; - } - - // Otherwise, check whether we have reached the .endr. - if (Lexer.is(AsmToken::Identifier) && getTok().getIdentifier() == ".endr") { - if (NestLevel == 0) { - EndToken = getTok(); - Lex(); - if (Lexer.isNot(AsmToken::EndOfStatement)) { - printError(getTok().getLoc(), - "unexpected token in '.endr' directive"); - return nullptr; + if (Lexer.is(AsmToken::Identifier)) { + StringRef Ident = getTok().getIdentifier(); + if (Ident == ".rep" || Ident == ".rept" || Ident == ".irp" || + Ident == ".irpc") { + ++NestLevel; + } else if (Ident == ".endr") { + if (NestLevel == 0) { + EndToken = getTok(); + Lex(); + if (!parseEOL()) + break; } - break; + --NestLevel; } - --NestLevel; } // Otherwise, scan till the end of the statement. diff --git a/llvm/test/MC/AsmParser/macro-rept.s b/llvm/test/MC/AsmParser/macro-rept.s index 1dc8060e1d87..2a6a4070bff5 100644 --- a/llvm/test/MC/AsmParser/macro-rept.s +++ b/llvm/test/MC/AsmParser/macro-rept.s @@ -13,10 +13,10 @@ // CHECK: .long 1 // CHECK: .long 1 -// CHECK: .long 0 -// CHECK: .long 0 -// CHECK: .long 0 - -// CHECK: .long 0 -// CHECK: .long 0 -// CHECK: .long 0 +// CHECK: .long 0 +// CHECK-NEXT: .long 0 +// CHECK-NEXT: .long 0 +// CHECK-NEXT: .long 0 +// CHECK-NEXT: .long 0 +// CHECK-NEXT: .long 0 +// CHECK-EMPTY: -- GitLab From 26fabdded34f8cea490060a70188a07ad6b76b8b Mon Sep 17 00:00:00 2001 From: Kazu Hirata Date: Wed, 15 May 2024 17:53:28 -0700 Subject: [PATCH 050/403] [memprof] Pass FrameIdConverter and CallStackIdConverter by reference (#92327) CallStackIdConverter sets LastUnmappedId when a mapping failure occurs. Now, since toMemProfRecord takes an instance of CallStackIdConverter by value, namely std::function, the caller of toMemProfRecord never receives the mapping failure that occurs inside toMemProfRecord. The same problem applies to FrameIdConverter. The patch fixes the problem by passing FrameIdConverter and CallStackIdConverter by reference, namely llvm::function_ref. While I am it, this patch deletes the copy constructor and copy assignment operator to avoid accidental copies. --- llvm/include/llvm/ProfileData/MemProf.h | 21 +++++-- llvm/lib/ProfileData/MemProf.cpp | 4 +- llvm/unittests/ProfileData/MemProfTest.cpp | 66 ++++++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/llvm/include/llvm/ProfileData/MemProf.h b/llvm/include/llvm/ProfileData/MemProf.h index 3ef6ca8586fb..60bff76d0e46 100644 --- a/llvm/include/llvm/ProfileData/MemProf.h +++ b/llvm/include/llvm/ProfileData/MemProf.h @@ -426,8 +426,8 @@ struct IndexedMemProfRecord { // Convert IndexedMemProfRecord to MemProfRecord. Callback is used to // translate CallStackId to call stacks with frames inline. MemProfRecord toMemProfRecord( - std::function(const CallStackId)> Callback) - const; + llvm::function_ref(const CallStackId)> + Callback) const; // Returns the GUID for the function name after canonicalization. For // memprof, we remove any .llvm suffix added by LTO. MemProfRecords are @@ -784,6 +784,12 @@ template struct FrameIdConverter { FrameIdConverter() = delete; FrameIdConverter(MapTy &Map) : Map(Map) {} + // Delete the copy constructor and copy assignment operator to avoid a + // situation where a copy of FrameIdConverter gets an error in LastUnmappedId + // while the original instance doesn't. + FrameIdConverter(const FrameIdConverter &) = delete; + FrameIdConverter &operator=(const FrameIdConverter &) = delete; + Frame operator()(FrameId Id) { auto Iter = Map.find(Id); if (Iter == Map.end()) { @@ -798,12 +804,19 @@ template struct FrameIdConverter { template struct CallStackIdConverter { std::optional LastUnmappedId; MapTy ⤅ - std::function FrameIdToFrame; + llvm::function_ref FrameIdToFrame; CallStackIdConverter() = delete; - CallStackIdConverter(MapTy &Map, std::function FrameIdToFrame) + CallStackIdConverter(MapTy &Map, + llvm::function_ref FrameIdToFrame) : Map(Map), FrameIdToFrame(FrameIdToFrame) {} + // Delete the copy constructor and copy assignment operator to avoid a + // situation where a copy of CallStackIdConverter gets an error in + // LastUnmappedId while the original instance doesn't. + CallStackIdConverter(const CallStackIdConverter &) = delete; + CallStackIdConverter &operator=(const CallStackIdConverter &) = delete; + llvm::SmallVector operator()(CallStackId CSId) { llvm::SmallVector Frames; auto CSIter = Map.find(CSId); diff --git a/llvm/lib/ProfileData/MemProf.cpp b/llvm/lib/ProfileData/MemProf.cpp index 4667778ca11d..f5789186094c 100644 --- a/llvm/lib/ProfileData/MemProf.cpp +++ b/llvm/lib/ProfileData/MemProf.cpp @@ -243,8 +243,8 @@ IndexedMemProfRecord::deserialize(const MemProfSchema &Schema, } MemProfRecord IndexedMemProfRecord::toMemProfRecord( - std::function(const CallStackId)> Callback) - const { + llvm::function_ref(const CallStackId)> + Callback) const { MemProfRecord Record; for (const memprof::IndexedAllocationInfo &IndexedAI : AllocSites) { diff --git a/llvm/unittests/ProfileData/MemProfTest.cpp b/llvm/unittests/ProfileData/MemProfTest.cpp index 8b97866e403f..a913718d0fe0 100644 --- a/llvm/unittests/ProfileData/MemProfTest.cpp +++ b/llvm/unittests/ProfileData/MemProfTest.cpp @@ -596,4 +596,70 @@ TEST(MemProf, IndexedMemProfRecordToMemProfRecord) { EXPECT_EQ(Record.CallSites[1][0].hash(), F2.hash()); EXPECT_EQ(Record.CallSites[1][1].hash(), F4.hash()); } + +using FrameIdMapTy = + llvm::DenseMap<::llvm::memprof::FrameId, ::llvm::memprof::Frame>; +using CallStackIdMapTy = + llvm::DenseMap<::llvm::memprof::CallStackId, + ::llvm::SmallVector<::llvm::memprof::FrameId>>; + +// Populate those fields returned by getHotColdSchema. +MemInfoBlock makePartialMIB() { + MemInfoBlock MIB; + MIB.AllocCount = 1; + MIB.TotalSize = 5; + MIB.TotalLifetime = 10; + MIB.TotalLifetimeAccessDensity = 23; + return MIB; +} + +TEST(MemProf, MissingCallStackId) { + // Use a non-existent CallStackId to trigger a mapping error in + // toMemProfRecord. + llvm::memprof::IndexedAllocationInfo AI({}, 0xdeadbeefU, makePartialMIB(), + llvm::memprof::getHotColdSchema()); + + IndexedMemProfRecord IndexedMR; + IndexedMR.AllocSites.push_back(AI); + + // Create empty maps. + const FrameIdMapTy IdToFrameMap; + const CallStackIdMapTy CSIdToCallStackMap; + llvm::memprof::FrameIdConverter FrameIdConv( + IdToFrameMap); + llvm::memprof::CallStackIdConverter CSIdConv( + CSIdToCallStackMap, FrameIdConv); + + // We are only interested in errors, not the return value. + (void)IndexedMR.toMemProfRecord(CSIdConv); + + ASSERT_TRUE(CSIdConv.LastUnmappedId.has_value()); + EXPECT_EQ(*CSIdConv.LastUnmappedId, 0xdeadbeefU); + EXPECT_EQ(FrameIdConv.LastUnmappedId, std::nullopt); +} + +TEST(MemProf, MissingFrameId) { + llvm::memprof::IndexedAllocationInfo AI({}, 0x222, makePartialMIB(), + llvm::memprof::getHotColdSchema()); + + IndexedMemProfRecord IndexedMR; + IndexedMR.AllocSites.push_back(AI); + + // An empty map to trigger a mapping error. + const FrameIdMapTy IdToFrameMap; + CallStackIdMapTy CSIdToCallStackMap; + CSIdToCallStackMap.insert({0x222, {2, 3}}); + + llvm::memprof::FrameIdConverter FrameIdConv( + IdToFrameMap); + llvm::memprof::CallStackIdConverter CSIdConv( + CSIdToCallStackMap, FrameIdConv); + + // We are only interested in errors, not the return value. + (void)IndexedMR.toMemProfRecord(CSIdConv); + + EXPECT_EQ(CSIdConv.LastUnmappedId, std::nullopt); + ASSERT_TRUE(FrameIdConv.LastUnmappedId.has_value()); + EXPECT_EQ(*FrameIdConv.LastUnmappedId, 3U); +} } // namespace -- GitLab From fa750f09be6966de7423ddce1af7d1eaf817182c Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 16 May 2024 09:49:34 +0900 Subject: [PATCH 051/403] Revert "[MC] Remove UseAssemblerInfoForParsing" This reverts commit 03c53c69a367008da689f0d2940e2197eb4a955c. This causes very large compile-time regressions in some cases, e.g. sqlite3 at O0 regresses by 5%. --- clang/tools/driver/cc1as_main.cpp | 3 +++ llvm/include/llvm/MC/MCStreamer.h | 7 +++++-- .../CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp | 3 +++ llvm/lib/MC/MCObjectStreamer.cpp | 9 ++++++++- llvm/lib/MC/MCStreamer.cpp | 2 +- llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp | 7 +++++-- llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp | 3 +++ .../AsmParser/assembler-expressions-inlineasm.ll | 16 ++++++---------- llvm/tools/llvm-mc/llvm-mc.cpp | 3 +++ llvm/tools/llvm-ml/llvm-ml.cpp | 3 +++ 10 files changed, 40 insertions(+), 16 deletions(-) diff --git a/clang/tools/driver/cc1as_main.cpp b/clang/tools/driver/cc1as_main.cpp index 4eb753a7297a..86afe22fac24 100644 --- a/clang/tools/driver/cc1as_main.cpp +++ b/clang/tools/driver/cc1as_main.cpp @@ -576,6 +576,9 @@ static bool ExecuteAssemblerImpl(AssemblerInvocation &Opts, Str.get()->emitZeros(1); } + // Assembly to object compilation should leverage assembly info. + Str->setUseAssemblerInfoForParsing(true); + bool Failed = false; std::unique_ptr Parser( diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h index 50986e6bde88..69867620e1bf 100644 --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -245,6 +245,8 @@ class MCStreamer { /// requires. unsigned NextWinCFIID = 0; + bool UseAssemblerInfoForParsing; + /// Is the assembler allowed to insert padding automatically? For /// correctness reasons, we sometimes need to ensure instructions aren't /// separated in unexpected ways. At the moment, this feature is only @@ -294,10 +296,11 @@ public: MCContext &getContext() const { return Context; } - // MCObjectStreamer has an MCAssembler and allows more expression folding at - // parse time. virtual MCAssembler *getAssemblerPtr() { return nullptr; } + void setUseAssemblerInfoForParsing(bool v) { UseAssemblerInfoForParsing = v; } + bool getUseAssemblerInfoForParsing() { return UseAssemblerInfoForParsing; } + MCTargetStreamer *getTargetStreamer() { return TargetStreamer.get(); } diff --git a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp index 08e3c208ba4d..d0ef3e5a1939 100644 --- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp +++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinterInlineAsm.cpp @@ -102,6 +102,9 @@ void AsmPrinter::emitInlineAsm(StringRef Str, const MCSubtargetInfo &STI, std::unique_ptr Parser( createMCAsmParser(SrcMgr, OutContext, *OutStreamer, *MAI, BufNum)); + // Do not use assembler-level information for parsing inline assembly. + OutStreamer->setUseAssemblerInfoForParsing(false); + // We create a new MCInstrInfo here since we might be at the module level // and not have a MachineFunction to initialize the TargetInstrInfo from and // we only need MCInstrInfo for asm parsing. We create one unconditionally diff --git a/llvm/lib/MC/MCObjectStreamer.cpp b/llvm/lib/MC/MCObjectStreamer.cpp index a9003a164b30..d2da5d0d3f90 100644 --- a/llvm/lib/MC/MCObjectStreamer.cpp +++ b/llvm/lib/MC/MCObjectStreamer.cpp @@ -40,7 +40,14 @@ MCObjectStreamer::MCObjectStreamer(MCContext &Context, MCObjectStreamer::~MCObjectStreamer() = default; -MCAssembler *MCObjectStreamer::getAssemblerPtr() { return Assembler.get(); } +// AssemblerPtr is used for evaluation of expressions and causes +// difference between asm and object outputs. Return nullptr to in +// inline asm mode to limit divergence to assembly inputs. +MCAssembler *MCObjectStreamer::getAssemblerPtr() { + if (getUseAssemblerInfoForParsing()) + return Assembler.get(); + return nullptr; +} void MCObjectStreamer::addPendingLabel(MCSymbol* S) { MCSection *CurSection = getCurrentSectionOnly(); diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp index 199d865ea349..176d55aa890b 100644 --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -93,7 +93,7 @@ void MCTargetStreamer::emitAssignment(MCSymbol *Symbol, const MCExpr *Value) {} MCStreamer::MCStreamer(MCContext &Ctx) : Context(Ctx), CurrentWinFrameInfo(nullptr), - CurrentProcWinFrameInfoStartIndex(0) { + CurrentProcWinFrameInfoStartIndex(0), UseAssemblerInfoForParsing(false) { SectionStack.push_back(std::pair()); } diff --git a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp index bd48a5f80c82..b7388ed9e85a 100644 --- a/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp +++ b/llvm/lib/Target/AMDGPU/AMDGPUAsmPrinter.cpp @@ -517,9 +517,12 @@ bool AMDGPUAsmPrinter::runOnMachineFunction(MachineFunction &MF) { DumpCodeInstEmitter = nullptr; if (STM.dumpCode()) { - // For -dumpcode, get the assembler out of the streamer. This only works - // with -filetype=obj. + // For -dumpcode, get the assembler out of the streamer, even if it does + // not really want to let us have it. This only works with -filetype=obj. + bool SaveFlag = OutStreamer->getUseAssemblerInfoForParsing(); + OutStreamer->setUseAssemblerInfoForParsing(true); MCAssembler *Assembler = OutStreamer->getAssemblerPtr(); + OutStreamer->setUseAssemblerInfoForParsing(SaveFlag); if (Assembler) DumpCodeInstEmitter = Assembler->getEmitterPtr(); } diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp index ad0158086044..2ebe5bdc4771 100644 --- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp @@ -114,9 +114,12 @@ void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) { // Bound is an approximation that accounts for the maximum used register // number and number of generated OpLabels unsigned Bound = 2 * (ST->getBound() + 1) + NLabels; + bool FlagToRestore = OutStreamer->getUseAssemblerInfoForParsing(); + OutStreamer->setUseAssemblerInfoForParsing(true); if (MCAssembler *Asm = OutStreamer->getAssemblerPtr()) Asm->setBuildVersion(static_cast(0), Major, Minor, Bound, VersionTuple(Major, Minor, 0, Bound)); + OutStreamer->setUseAssemblerInfoForParsing(FlagToRestore); } void SPIRVAsmPrinter::emitFunctionHeader() { diff --git a/llvm/test/MC/AsmParser/assembler-expressions-inlineasm.ll b/llvm/test/MC/AsmParser/assembler-expressions-inlineasm.ll index 9d9a38f5b5a5..35f110f37e2f 100644 --- a/llvm/test/MC/AsmParser/assembler-expressions-inlineasm.ll +++ b/llvm/test/MC/AsmParser/assembler-expressions-inlineasm.ll @@ -1,16 +1,12 @@ -; RUN: not llc -mtriple=x86_64 %s -o /dev/null 2>&1 | FileCheck %s -; RUN: llc -mtriple=x86_64 -no-integrated-as < %s | FileCheck %s --check-prefix=GAS -; RUN: llc -mtriple=x86_64 -filetype=obj %s -o - | llvm-objdump -d - | FileCheck %s --check-prefix=DISASM +; RUN: not llc -mtriple x86_64-unknown-linux-gnu -o %t.s -filetype=asm %s 2>&1 | FileCheck %s +; RUN: not llc -mtriple x86_64-unknown-linux-gnu -o %t.o -filetype=obj %s 2>&1 | FileCheck %s -; GAS: nop; .if . - foo==1; nop;.endif +; Assembler-aware expression evaluation should be disabled in inline +; assembly to prevent differences in behavior between object and +; assembly output. -; CHECK: :1:17: error: expected absolute expression -; DISASM:
: -; DISASM-NEXT: nop -; DISASM-NEXT: nop -; DISASM-NEXT: xorl %eax, %eax -; DISASM-NEXT: retq +; CHECK: :1:17: error: expected absolute expression define i32 @main() local_unnamed_addr { tail call void asm sideeffect "foo: nop; .if . - foo==1; nop;.endif", "~{dirflag},~{fpsr},~{flags}"() diff --git a/llvm/tools/llvm-mc/llvm-mc.cpp b/llvm/tools/llvm-mc/llvm-mc.cpp index 506e4f22ef8f..807071a7b9a1 100644 --- a/llvm/tools/llvm-mc/llvm-mc.cpp +++ b/llvm/tools/llvm-mc/llvm-mc.cpp @@ -569,6 +569,9 @@ int main(int argc, char **argv) { Str->initSections(true, *STI); } + // Use Assembler information for parsing. + Str->setUseAssemblerInfoForParsing(true); + int Res = 1; bool disassemble = false; switch (Action) { diff --git a/llvm/tools/llvm-ml/llvm-ml.cpp b/llvm/tools/llvm-ml/llvm-ml.cpp index f1f39af059aa..1cac576f54e7 100644 --- a/llvm/tools/llvm-ml/llvm-ml.cpp +++ b/llvm/tools/llvm-ml/llvm-ml.cpp @@ -428,6 +428,9 @@ int llvm_ml_main(int Argc, char **Argv, const llvm::ToolContext &) { Str->emitAssignment(Feat00Sym, MCConstantExpr::create(Feat00Flags, Ctx)); } + // Use Assembler information for parsing. + Str->setUseAssemblerInfoForParsing(true); + int Res = 1; if (InputArgs.hasArg(OPT_as_lex)) { // -as-lex; Lex only, and output a stream of tokens -- GitLab From a9763deb2f3f20d789b947ec69360c258377db6a Mon Sep 17 00:00:00 2001 From: Shubham Sandeep Rastogi Date: Wed, 15 May 2024 18:15:40 -0700 Subject: [PATCH 052/403] Merge sourcelocation in CSEMIRBuilder::getDominatingInstrForID. (#90922) Make sure to merge the sourcelocation of the Dominating Instruction that is hoisted in a basic block in the CSEMIRBuilder in the legalizer pass. If this is not done, we can have a incorrect line table entry that makes the instruction pointer jump around. For example the line table without this patch looks like: ``` Address Line Column File ISA Discriminator OpIndex Flags ------------------ ------ ------ ------ --- ------------- ------- ------------- 0x0000000000000000 0 0 1 0 0 0 is_stmt 0x0000000000000010 11 14 1 0 0 0 is_stmt prologue_end 0x0000000000000028 12 1 1 0 0 0 is_stmt 0x000000000000002c 12 15 1 0 0 0 0x000000000000004c 12 13 1 0 0 0 0x000000000000005c 13 1 1 0 0 0 is_stmt 0x0000000000000064 12 13 1 0 0 0 is_stmt 0x000000000000007c 13 7 1 0 0 0 is_stmt 0x00000000000000c8 13 1 1 0 0 0 0x00000000000000e8 13 1 1 0 0 0 epilogue_begin 0x00000000000000f8 13 1 1 0 0 0 end_sequence ``` The line table entry for 0x000000000000005c should be 0 After this patch, the line table looks like: ``` Address Line Column File ISA Discriminator OpIndex Flags ------------------ ------ ------ ------ --- ------------- ------- ------------- 0x0000000000000000 0 0 1 0 0 0 is_stmt 0x0000000000000010 11 14 1 0 0 0 is_stmt prologue_end 0x0000000000000028 12 1 1 0 0 0 is_stmt 0x000000000000002c 12 15 1 0 0 0 0x000000000000004c 12 13 1 0 0 0 0x000000000000005c 0 0 1 0 0 0 0x0000000000000064 12 13 1 0 0 0 0x000000000000007c 13 7 1 0 0 0 is_stmt 0x00000000000000c8 13 1 1 0 0 0 0x00000000000000e8 13 1 1 0 0 0 epilogue_begin 0x00000000000000f8 13 1 1 0 0 0 end_sequence ``` --- .../GlobalISel/LegalizationArtifactCombiner.h | 6 ++++ llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp | 5 ++++ .../AArch64/merge-locations-legalizer.mir | 30 +++++++++++++++++++ 3 files changed, 41 insertions(+) create mode 100644 llvm/test/DebugInfo/AArch64/merge-locations-legalizer.mir diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h index 305bef7dd3ea..2efc48e3be4c 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizationArtifactCombiner.h @@ -26,6 +26,7 @@ #include "llvm/CodeGen/Register.h" #include "llvm/CodeGen/TargetOpcodes.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/Support/Debug.h" #define DEBUG_TYPE "legalizer" @@ -99,6 +100,11 @@ public: const LLT DstTy = MRI.getType(DstReg); if (isInstLegal({TargetOpcode::G_CONSTANT, {DstTy}})) { auto &CstVal = SrcMI->getOperand(1); + auto *MergedLocation = DILocation::getMergedLocation( + MI.getDebugLoc().get(), SrcMI->getDebugLoc().get()); + // Set the debug location to the merged location of the SrcMI and the MI + // if the aext fold is successful. + Builder.setDebugLoc(MergedLocation); Builder.buildConstant( DstReg, CstVal.getCImm()->getValue().sext(DstTy.getSizeInBits())); UpdatedDefs.push_back(DstReg); diff --git a/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp index 551ba1e6036c..547529bbe699 100644 --- a/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp +++ b/llvm/lib/CodeGen/GlobalISel/CSEMIRBuilder.cpp @@ -51,6 +51,11 @@ CSEMIRBuilder::getDominatingInstrForID(FoldingSetNodeID &ID, // this builder will have the def ready. setInsertPt(*CurMBB, std::next(MII)); } else if (!dominates(MI, CurrPos)) { + // Update the spliced machineinstr's debug location by merging it with the + // debug location of the instruction at the insertion point. + auto *Loc = DILocation::getMergedLocation(getDebugLoc().get(), + MI->getDebugLoc().get()); + MI->setDebugLoc(Loc); CurMBB->splice(CurrPos, CurMBB, MI); } return MachineInstrBuilder(getMF(), MI); diff --git a/llvm/test/DebugInfo/AArch64/merge-locations-legalizer.mir b/llvm/test/DebugInfo/AArch64/merge-locations-legalizer.mir new file mode 100644 index 000000000000..3bdf87cea0e5 --- /dev/null +++ b/llvm/test/DebugInfo/AArch64/merge-locations-legalizer.mir @@ -0,0 +1,30 @@ +# This test checks to make sure that when an instruction (%3 in the test) is +# moved due to matching a result of a fold of two other instructions +# (%1, and %2 in the test) in the legalizer, the DILocation of the +# instruction that is moved (%3) is updated appropriately. + +# RUN: llc %s -run-pass=legalizer -mtriple=aarch64 -o - | FileCheck %s +# CHECK-NOT: %2:_(s32) = G_CONSTANT i32 0, debug-location !DILocation(line: 13 +# CHECK: %2:_(s32) = G_CONSTANT i32 0, debug-location !DILocation(line: 0, +--- | + + define i32 @main(i32 %0, ptr %1) #0 !dbg !57 { + entry: + ret i32 0, !dbg !71 + } + !3 = !DIFile(filename: "main.swift", directory: "/Volumes/Data/swift") + !23 = distinct !DICompileUnit(language: DW_LANG_Swift, file: !3, sdk: "blah.sdk") + !57 = distinct !DISubprogram(name: "main", unit: !23) + !64 = distinct !DILexicalBlock(scope: !57, column: 1) + !66 = distinct !DILexicalBlock(scope: !64, column: 1) + !68 = !DILocation(line: 12, scope: !66) + !70 = distinct !DILexicalBlock(scope: !66, column: 1) + !71 = !DILocation(line: 13, scope: !70) +name: main +body: | + bb.0: + %1:_(s8) = G_CONSTANT i8 0, debug-location !68 + %2:_(s32) = G_ANYEXT %1(s8), debug-location !68 + $w2 = COPY %2(s32), debug-location !68 + %3:_(s32) = G_CONSTANT i32 0, debug-location !71 + $w0 = COPY %3(s32), debug-location !71 -- GitLab From 72200fcc346bee1830d9e640e42d717a55acd74c Mon Sep 17 00:00:00 2001 From: Ryosuke Niwa Date: Wed, 15 May 2024 18:16:39 -0700 Subject: [PATCH 053/403] [analyzer] Check C++ base or member initializer in WebKit checkers. (#92220) Co-authored-by: Ryosuke Niwa --- .../Checkers/WebKit/PtrTypesSemantics.cpp | 10 ++++++++- .../Checkers/WebKit/uncounted-obj-arg.cpp | 21 +++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp index 950d35a090a3..5c797d523308 100644 --- a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp @@ -525,11 +525,19 @@ bool TrivialFunctionAnalysis::isTrivialImpl( if (!IsNew) return It->second; + TrivialFunctionAnalysisVisitor V(Cache); + + if (auto *CtorDecl = dyn_cast(D)) { + for (auto *CtorInit : CtorDecl->inits()) { + if (!V.Visit(CtorInit->getInit())) + return false; + } + } + const Stmt *Body = D->getBody(); if (!Body) return false; - TrivialFunctionAnalysisVisitor V(Cache); bool Result = V.Visit(Body); if (Result) Cache[D] = true; diff --git a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp index ed37671df3d3..96986631726f 100644 --- a/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp +++ b/clang/test/Analysis/Checkers/WebKit/uncounted-obj-arg.cpp @@ -159,10 +159,13 @@ private: StorageType m_storage { 0 }; }; +int atoi(const char* str); + class Number { public: Number(int v) : v(v) { } Number(double); + Number(const char* str) : v(atoi(str)) { } Number operator+(const Number&); Number& operator++() { ++v; return *this; } Number operator++(int) { Number returnValue(v); ++v; return returnValue; } @@ -173,9 +176,16 @@ private: int v; }; +class DerivedNumber : public Number { +public: + DerivedNumber(char c) : Number(c - '0') { } + DerivedNumber(const char* str) : Number(atoi(str)) { } +}; + class ComplexNumber { public: ComplexNumber() : realPart(0), complexPart(0) { } + ComplexNumber(int real, const char* str) : realPart(real), complexPart(str) { } ComplexNumber(const ComplexNumber&); ComplexNumber& operator++() { realPart.someMethod(); return *this; } ComplexNumber operator++(int); @@ -311,6 +321,7 @@ public: return; } unsigned trivial60() { return ObjectWithNonTrivialDestructor { 5 }.value(); } + unsigned trivial61() { return DerivedNumber('7').value(); } static RefCounted& singleton() { static RefCounted s_RefCounted; @@ -391,6 +402,9 @@ public: ComplexNumber nonTrivial18() { return +complex; } ComplexNumber* nonTrivial19() { return new ComplexNumber(complex); } unsigned nonTrivial20() { return ObjectWithMutatingDestructor { 7 }.value(); } + unsigned nonTrivial21() { return Number("123").value(); } + unsigned nonTrivial22() { return ComplexNumber(123, "456").real().value(); } + unsigned nonTrivial23() { return DerivedNumber("123").value(); } static unsigned s_v; unsigned v { 0 }; @@ -479,6 +493,7 @@ public: getFieldTrivial().trivial58(); // no-warning getFieldTrivial().trivial59(); // no-warning getFieldTrivial().trivial60(); // no-warning + getFieldTrivial().trivial61(); // no-warning RefCounted::singleton().trivial18(); // no-warning RefCounted::singleton().someFunction(); // no-warning @@ -525,6 +540,12 @@ public: // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} getFieldTrivial().nonTrivial20(); // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} + getFieldTrivial().nonTrivial21(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} + getFieldTrivial().nonTrivial22(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} + getFieldTrivial().nonTrivial23(); + // expected-warning@-1{{Call argument for 'this' parameter is uncounted and unsafe}} } }; -- GitLab From f0b3654701bde1cf7821d60698b42383edaff9f3 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 16 May 2024 10:21:22 +0900 Subject: [PATCH 054/403] [LoopUnroll] Clamp PartialThreshold for large LoopMicroOpBufferSize (#67657) The znver3/znver4 scheduler models are outliers, specifying very large LoopMicroOpBufferSizes at 512, while typical values for other subtargets are on the order of ~50. Even if this information is micro-architecturally correct (*), this does not mean that we want to runtime unroll all loops to a size that completely fills the loop buffer. Unless this is the single hot loop in the entire application, the massive code size increase will bust the micro-op and instruction caches. Protect against this by clamping to the default PartialThreshold of 150, which is the same as the default full-unroll threshold and half the aggressive full-unroll threshold. Allowing more partial unrolling than full unrolling certainly does not make sense. (*) I strongly doubt that this is actually correct -- I believe this may derive from an incorrect reading of Agner Fog's micro-architecture guide. The number 4096 that was originally used here is the size of the general micro-op cache, not that of a loop buffer. A separate loop buffer is not listed for the Zen microarchitecture. Comparing this to the listing for Skylake, it has a 1536 micro-op buffer, but only a 64 micro-op loopback buffer, with a note that it's rarely fully utilized. Our scheduling model specifies LoopMicroOpBufferSize of 50 in that case. --- llvm/include/llvm/CodeGen/BasicTTIImpl.h | 8 +- llvm/test/Transforms/LoopUnroll/X86/znver3.ll | 764 +----------------- 2 files changed, 30 insertions(+), 742 deletions(-) diff --git a/llvm/include/llvm/CodeGen/BasicTTIImpl.h b/llvm/include/llvm/CodeGen/BasicTTIImpl.h index 2091432d4fe2..8dba6a641285 100644 --- a/llvm/include/llvm/CodeGen/BasicTTIImpl.h +++ b/llvm/include/llvm/CodeGen/BasicTTIImpl.h @@ -612,7 +612,13 @@ public: if (PartialUnrollingThreshold.getNumOccurrences() > 0) MaxOps = PartialUnrollingThreshold; else if (ST->getSchedModel().LoopMicroOpBufferSize > 0) - MaxOps = ST->getSchedModel().LoopMicroOpBufferSize; + // Upper bound by the default PartialThreshold, which is the same as + // the default full-unroll Threshold. Even if the loop micro-op buffer + // is very large, this does not mean that we want to unroll all loops + // to that length, as it would increase code size beyond the limits of + // what unrolling normally allows. + MaxOps = std::min(ST->getSchedModel().LoopMicroOpBufferSize, + UP.PartialThreshold); else return; diff --git a/llvm/test/Transforms/LoopUnroll/X86/znver3.ll b/llvm/test/Transforms/LoopUnroll/X86/znver3.ll index 30389062a096..467c57906d88 100644 --- a/llvm/test/Transforms/LoopUnroll/X86/znver3.ll +++ b/llvm/test/Transforms/LoopUnroll/X86/znver3.ll @@ -9,8 +9,8 @@ define i32 @test(ptr %ary) "target-cpu"="znver3" { ; CHECK-NEXT: entry: ; CHECK-NEXT: br label [[FOR_BODY:%.*]] ; CHECK: for.body: -; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT_127:%.*]], [[FOR_BODY]] ] -; CHECK-NEXT: [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[SUM_NEXT_127:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY:%.*]] ], [ [[INDVARS_IV_NEXT_31:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[SUM_NEXT_31:%.*]], [[FOR_BODY]] ] ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV]] ; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 ; CHECK-NEXT: [[SUM_NEXT:%.*]] = add nsw i32 [[VAL]], [[SUM]] @@ -137,396 +137,12 @@ define i32 @test(ptr %ary) "target-cpu"="znver3" { ; CHECK-NEXT: [[INDVARS_IV_NEXT_30:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 31 ; CHECK-NEXT: [[ARRAYIDX_31:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_30]] ; CHECK-NEXT: [[VAL_31:%.*]] = load i32, ptr [[ARRAYIDX_31]], align 4 -; CHECK-NEXT: [[SUM_NEXT_31:%.*]] = add nsw i32 [[VAL_31]], [[SUM_NEXT_30]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_31:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 32 -; CHECK-NEXT: [[ARRAYIDX_32:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_31]] -; CHECK-NEXT: [[VAL_32:%.*]] = load i32, ptr [[ARRAYIDX_32]], align 4 -; CHECK-NEXT: [[SUM_NEXT_32:%.*]] = add nsw i32 [[VAL_32]], [[SUM_NEXT_31]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_32:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 33 -; CHECK-NEXT: [[ARRAYIDX_33:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_32]] -; CHECK-NEXT: [[VAL_33:%.*]] = load i32, ptr [[ARRAYIDX_33]], align 4 -; CHECK-NEXT: [[SUM_NEXT_33:%.*]] = add nsw i32 [[VAL_33]], [[SUM_NEXT_32]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_33:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 34 -; CHECK-NEXT: [[ARRAYIDX_34:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_33]] -; CHECK-NEXT: [[VAL_34:%.*]] = load i32, ptr [[ARRAYIDX_34]], align 4 -; CHECK-NEXT: [[SUM_NEXT_34:%.*]] = add nsw i32 [[VAL_34]], [[SUM_NEXT_33]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_34:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 35 -; CHECK-NEXT: [[ARRAYIDX_35:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_34]] -; CHECK-NEXT: [[VAL_35:%.*]] = load i32, ptr [[ARRAYIDX_35]], align 4 -; CHECK-NEXT: [[SUM_NEXT_35:%.*]] = add nsw i32 [[VAL_35]], [[SUM_NEXT_34]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_35:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 36 -; CHECK-NEXT: [[ARRAYIDX_36:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_35]] -; CHECK-NEXT: [[VAL_36:%.*]] = load i32, ptr [[ARRAYIDX_36]], align 4 -; CHECK-NEXT: [[SUM_NEXT_36:%.*]] = add nsw i32 [[VAL_36]], [[SUM_NEXT_35]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_36:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 37 -; CHECK-NEXT: [[ARRAYIDX_37:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_36]] -; CHECK-NEXT: [[VAL_37:%.*]] = load i32, ptr [[ARRAYIDX_37]], align 4 -; CHECK-NEXT: [[SUM_NEXT_37:%.*]] = add nsw i32 [[VAL_37]], [[SUM_NEXT_36]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_37:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 38 -; CHECK-NEXT: [[ARRAYIDX_38:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_37]] -; CHECK-NEXT: [[VAL_38:%.*]] = load i32, ptr [[ARRAYIDX_38]], align 4 -; CHECK-NEXT: [[SUM_NEXT_38:%.*]] = add nsw i32 [[VAL_38]], [[SUM_NEXT_37]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_38:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 39 -; CHECK-NEXT: [[ARRAYIDX_39:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_38]] -; CHECK-NEXT: [[VAL_39:%.*]] = load i32, ptr [[ARRAYIDX_39]], align 4 -; CHECK-NEXT: [[SUM_NEXT_39:%.*]] = add nsw i32 [[VAL_39]], [[SUM_NEXT_38]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_39:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 40 -; CHECK-NEXT: [[ARRAYIDX_40:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_39]] -; CHECK-NEXT: [[VAL_40:%.*]] = load i32, ptr [[ARRAYIDX_40]], align 4 -; CHECK-NEXT: [[SUM_NEXT_40:%.*]] = add nsw i32 [[VAL_40]], [[SUM_NEXT_39]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_40:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 41 -; CHECK-NEXT: [[ARRAYIDX_41:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_40]] -; CHECK-NEXT: [[VAL_41:%.*]] = load i32, ptr [[ARRAYIDX_41]], align 4 -; CHECK-NEXT: [[SUM_NEXT_41:%.*]] = add nsw i32 [[VAL_41]], [[SUM_NEXT_40]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_41:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 42 -; CHECK-NEXT: [[ARRAYIDX_42:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_41]] -; CHECK-NEXT: [[VAL_42:%.*]] = load i32, ptr [[ARRAYIDX_42]], align 4 -; CHECK-NEXT: [[SUM_NEXT_42:%.*]] = add nsw i32 [[VAL_42]], [[SUM_NEXT_41]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_42:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 43 -; CHECK-NEXT: [[ARRAYIDX_43:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_42]] -; CHECK-NEXT: [[VAL_43:%.*]] = load i32, ptr [[ARRAYIDX_43]], align 4 -; CHECK-NEXT: [[SUM_NEXT_43:%.*]] = add nsw i32 [[VAL_43]], [[SUM_NEXT_42]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_43:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 44 -; CHECK-NEXT: [[ARRAYIDX_44:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_43]] -; CHECK-NEXT: [[VAL_44:%.*]] = load i32, ptr [[ARRAYIDX_44]], align 4 -; CHECK-NEXT: [[SUM_NEXT_44:%.*]] = add nsw i32 [[VAL_44]], [[SUM_NEXT_43]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_44:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 45 -; CHECK-NEXT: [[ARRAYIDX_45:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_44]] -; CHECK-NEXT: [[VAL_45:%.*]] = load i32, ptr [[ARRAYIDX_45]], align 4 -; CHECK-NEXT: [[SUM_NEXT_45:%.*]] = add nsw i32 [[VAL_45]], [[SUM_NEXT_44]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_45:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 46 -; CHECK-NEXT: [[ARRAYIDX_46:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_45]] -; CHECK-NEXT: [[VAL_46:%.*]] = load i32, ptr [[ARRAYIDX_46]], align 4 -; CHECK-NEXT: [[SUM_NEXT_46:%.*]] = add nsw i32 [[VAL_46]], [[SUM_NEXT_45]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_46:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 47 -; CHECK-NEXT: [[ARRAYIDX_47:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_46]] -; CHECK-NEXT: [[VAL_47:%.*]] = load i32, ptr [[ARRAYIDX_47]], align 4 -; CHECK-NEXT: [[SUM_NEXT_47:%.*]] = add nsw i32 [[VAL_47]], [[SUM_NEXT_46]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_47:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 48 -; CHECK-NEXT: [[ARRAYIDX_48:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_47]] -; CHECK-NEXT: [[VAL_48:%.*]] = load i32, ptr [[ARRAYIDX_48]], align 4 -; CHECK-NEXT: [[SUM_NEXT_48:%.*]] = add nsw i32 [[VAL_48]], [[SUM_NEXT_47]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_48:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 49 -; CHECK-NEXT: [[ARRAYIDX_49:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_48]] -; CHECK-NEXT: [[VAL_49:%.*]] = load i32, ptr [[ARRAYIDX_49]], align 4 -; CHECK-NEXT: [[SUM_NEXT_49:%.*]] = add nsw i32 [[VAL_49]], [[SUM_NEXT_48]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_49:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 50 -; CHECK-NEXT: [[ARRAYIDX_50:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_49]] -; CHECK-NEXT: [[VAL_50:%.*]] = load i32, ptr [[ARRAYIDX_50]], align 4 -; CHECK-NEXT: [[SUM_NEXT_50:%.*]] = add nsw i32 [[VAL_50]], [[SUM_NEXT_49]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_50:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 51 -; CHECK-NEXT: [[ARRAYIDX_51:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_50]] -; CHECK-NEXT: [[VAL_51:%.*]] = load i32, ptr [[ARRAYIDX_51]], align 4 -; CHECK-NEXT: [[SUM_NEXT_51:%.*]] = add nsw i32 [[VAL_51]], [[SUM_NEXT_50]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_51:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 52 -; CHECK-NEXT: [[ARRAYIDX_52:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_51]] -; CHECK-NEXT: [[VAL_52:%.*]] = load i32, ptr [[ARRAYIDX_52]], align 4 -; CHECK-NEXT: [[SUM_NEXT_52:%.*]] = add nsw i32 [[VAL_52]], [[SUM_NEXT_51]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_52:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 53 -; CHECK-NEXT: [[ARRAYIDX_53:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_52]] -; CHECK-NEXT: [[VAL_53:%.*]] = load i32, ptr [[ARRAYIDX_53]], align 4 -; CHECK-NEXT: [[SUM_NEXT_53:%.*]] = add nsw i32 [[VAL_53]], [[SUM_NEXT_52]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_53:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 54 -; CHECK-NEXT: [[ARRAYIDX_54:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_53]] -; CHECK-NEXT: [[VAL_54:%.*]] = load i32, ptr [[ARRAYIDX_54]], align 4 -; CHECK-NEXT: [[SUM_NEXT_54:%.*]] = add nsw i32 [[VAL_54]], [[SUM_NEXT_53]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_54:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 55 -; CHECK-NEXT: [[ARRAYIDX_55:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_54]] -; CHECK-NEXT: [[VAL_55:%.*]] = load i32, ptr [[ARRAYIDX_55]], align 4 -; CHECK-NEXT: [[SUM_NEXT_55:%.*]] = add nsw i32 [[VAL_55]], [[SUM_NEXT_54]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_55:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 56 -; CHECK-NEXT: [[ARRAYIDX_56:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_55]] -; CHECK-NEXT: [[VAL_56:%.*]] = load i32, ptr [[ARRAYIDX_56]], align 4 -; CHECK-NEXT: [[SUM_NEXT_56:%.*]] = add nsw i32 [[VAL_56]], [[SUM_NEXT_55]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_56:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 57 -; CHECK-NEXT: [[ARRAYIDX_57:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_56]] -; CHECK-NEXT: [[VAL_57:%.*]] = load i32, ptr [[ARRAYIDX_57]], align 4 -; CHECK-NEXT: [[SUM_NEXT_57:%.*]] = add nsw i32 [[VAL_57]], [[SUM_NEXT_56]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_57:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 58 -; CHECK-NEXT: [[ARRAYIDX_58:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_57]] -; CHECK-NEXT: [[VAL_58:%.*]] = load i32, ptr [[ARRAYIDX_58]], align 4 -; CHECK-NEXT: [[SUM_NEXT_58:%.*]] = add nsw i32 [[VAL_58]], [[SUM_NEXT_57]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_58:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 59 -; CHECK-NEXT: [[ARRAYIDX_59:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_58]] -; CHECK-NEXT: [[VAL_59:%.*]] = load i32, ptr [[ARRAYIDX_59]], align 4 -; CHECK-NEXT: [[SUM_NEXT_59:%.*]] = add nsw i32 [[VAL_59]], [[SUM_NEXT_58]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_59:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 60 -; CHECK-NEXT: [[ARRAYIDX_60:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_59]] -; CHECK-NEXT: [[VAL_60:%.*]] = load i32, ptr [[ARRAYIDX_60]], align 4 -; CHECK-NEXT: [[SUM_NEXT_60:%.*]] = add nsw i32 [[VAL_60]], [[SUM_NEXT_59]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_60:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 61 -; CHECK-NEXT: [[ARRAYIDX_61:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_60]] -; CHECK-NEXT: [[VAL_61:%.*]] = load i32, ptr [[ARRAYIDX_61]], align 4 -; CHECK-NEXT: [[SUM_NEXT_61:%.*]] = add nsw i32 [[VAL_61]], [[SUM_NEXT_60]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_61:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 62 -; CHECK-NEXT: [[ARRAYIDX_62:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_61]] -; CHECK-NEXT: [[VAL_62:%.*]] = load i32, ptr [[ARRAYIDX_62]], align 4 -; CHECK-NEXT: [[SUM_NEXT_62:%.*]] = add nsw i32 [[VAL_62]], [[SUM_NEXT_61]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_62:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 63 -; CHECK-NEXT: [[ARRAYIDX_63:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_62]] -; CHECK-NEXT: [[VAL_63:%.*]] = load i32, ptr [[ARRAYIDX_63]], align 4 -; CHECK-NEXT: [[SUM_NEXT_63:%.*]] = add nsw i32 [[VAL_63]], [[SUM_NEXT_62]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_63:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 64 -; CHECK-NEXT: [[ARRAYIDX_64:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_63]] -; CHECK-NEXT: [[VAL_64:%.*]] = load i32, ptr [[ARRAYIDX_64]], align 4 -; CHECK-NEXT: [[SUM_NEXT_64:%.*]] = add nsw i32 [[VAL_64]], [[SUM_NEXT_63]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_64:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 65 -; CHECK-NEXT: [[ARRAYIDX_65:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_64]] -; CHECK-NEXT: [[VAL_65:%.*]] = load i32, ptr [[ARRAYIDX_65]], align 4 -; CHECK-NEXT: [[SUM_NEXT_65:%.*]] = add nsw i32 [[VAL_65]], [[SUM_NEXT_64]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_65:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 66 -; CHECK-NEXT: [[ARRAYIDX_66:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_65]] -; CHECK-NEXT: [[VAL_66:%.*]] = load i32, ptr [[ARRAYIDX_66]], align 4 -; CHECK-NEXT: [[SUM_NEXT_66:%.*]] = add nsw i32 [[VAL_66]], [[SUM_NEXT_65]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_66:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 67 -; CHECK-NEXT: [[ARRAYIDX_67:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_66]] -; CHECK-NEXT: [[VAL_67:%.*]] = load i32, ptr [[ARRAYIDX_67]], align 4 -; CHECK-NEXT: [[SUM_NEXT_67:%.*]] = add nsw i32 [[VAL_67]], [[SUM_NEXT_66]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_67:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 68 -; CHECK-NEXT: [[ARRAYIDX_68:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_67]] -; CHECK-NEXT: [[VAL_68:%.*]] = load i32, ptr [[ARRAYIDX_68]], align 4 -; CHECK-NEXT: [[SUM_NEXT_68:%.*]] = add nsw i32 [[VAL_68]], [[SUM_NEXT_67]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_68:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 69 -; CHECK-NEXT: [[ARRAYIDX_69:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_68]] -; CHECK-NEXT: [[VAL_69:%.*]] = load i32, ptr [[ARRAYIDX_69]], align 4 -; CHECK-NEXT: [[SUM_NEXT_69:%.*]] = add nsw i32 [[VAL_69]], [[SUM_NEXT_68]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_69:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 70 -; CHECK-NEXT: [[ARRAYIDX_70:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_69]] -; CHECK-NEXT: [[VAL_70:%.*]] = load i32, ptr [[ARRAYIDX_70]], align 4 -; CHECK-NEXT: [[SUM_NEXT_70:%.*]] = add nsw i32 [[VAL_70]], [[SUM_NEXT_69]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_70:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 71 -; CHECK-NEXT: [[ARRAYIDX_71:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_70]] -; CHECK-NEXT: [[VAL_71:%.*]] = load i32, ptr [[ARRAYIDX_71]], align 4 -; CHECK-NEXT: [[SUM_NEXT_71:%.*]] = add nsw i32 [[VAL_71]], [[SUM_NEXT_70]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_71:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 72 -; CHECK-NEXT: [[ARRAYIDX_72:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_71]] -; CHECK-NEXT: [[VAL_72:%.*]] = load i32, ptr [[ARRAYIDX_72]], align 4 -; CHECK-NEXT: [[SUM_NEXT_72:%.*]] = add nsw i32 [[VAL_72]], [[SUM_NEXT_71]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_72:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 73 -; CHECK-NEXT: [[ARRAYIDX_73:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_72]] -; CHECK-NEXT: [[VAL_73:%.*]] = load i32, ptr [[ARRAYIDX_73]], align 4 -; CHECK-NEXT: [[SUM_NEXT_73:%.*]] = add nsw i32 [[VAL_73]], [[SUM_NEXT_72]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_73:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 74 -; CHECK-NEXT: [[ARRAYIDX_74:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_73]] -; CHECK-NEXT: [[VAL_74:%.*]] = load i32, ptr [[ARRAYIDX_74]], align 4 -; CHECK-NEXT: [[SUM_NEXT_74:%.*]] = add nsw i32 [[VAL_74]], [[SUM_NEXT_73]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_74:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 75 -; CHECK-NEXT: [[ARRAYIDX_75:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_74]] -; CHECK-NEXT: [[VAL_75:%.*]] = load i32, ptr [[ARRAYIDX_75]], align 4 -; CHECK-NEXT: [[SUM_NEXT_75:%.*]] = add nsw i32 [[VAL_75]], [[SUM_NEXT_74]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_75:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 76 -; CHECK-NEXT: [[ARRAYIDX_76:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_75]] -; CHECK-NEXT: [[VAL_76:%.*]] = load i32, ptr [[ARRAYIDX_76]], align 4 -; CHECK-NEXT: [[SUM_NEXT_76:%.*]] = add nsw i32 [[VAL_76]], [[SUM_NEXT_75]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_76:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 77 -; CHECK-NEXT: [[ARRAYIDX_77:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_76]] -; CHECK-NEXT: [[VAL_77:%.*]] = load i32, ptr [[ARRAYIDX_77]], align 4 -; CHECK-NEXT: [[SUM_NEXT_77:%.*]] = add nsw i32 [[VAL_77]], [[SUM_NEXT_76]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_77:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 78 -; CHECK-NEXT: [[ARRAYIDX_78:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_77]] -; CHECK-NEXT: [[VAL_78:%.*]] = load i32, ptr [[ARRAYIDX_78]], align 4 -; CHECK-NEXT: [[SUM_NEXT_78:%.*]] = add nsw i32 [[VAL_78]], [[SUM_NEXT_77]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_78:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 79 -; CHECK-NEXT: [[ARRAYIDX_79:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_78]] -; CHECK-NEXT: [[VAL_79:%.*]] = load i32, ptr [[ARRAYIDX_79]], align 4 -; CHECK-NEXT: [[SUM_NEXT_79:%.*]] = add nsw i32 [[VAL_79]], [[SUM_NEXT_78]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_79:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 80 -; CHECK-NEXT: [[ARRAYIDX_80:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_79]] -; CHECK-NEXT: [[VAL_80:%.*]] = load i32, ptr [[ARRAYIDX_80]], align 4 -; CHECK-NEXT: [[SUM_NEXT_80:%.*]] = add nsw i32 [[VAL_80]], [[SUM_NEXT_79]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_80:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 81 -; CHECK-NEXT: [[ARRAYIDX_81:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_80]] -; CHECK-NEXT: [[VAL_81:%.*]] = load i32, ptr [[ARRAYIDX_81]], align 4 -; CHECK-NEXT: [[SUM_NEXT_81:%.*]] = add nsw i32 [[VAL_81]], [[SUM_NEXT_80]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_81:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 82 -; CHECK-NEXT: [[ARRAYIDX_82:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_81]] -; CHECK-NEXT: [[VAL_82:%.*]] = load i32, ptr [[ARRAYIDX_82]], align 4 -; CHECK-NEXT: [[SUM_NEXT_82:%.*]] = add nsw i32 [[VAL_82]], [[SUM_NEXT_81]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_82:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 83 -; CHECK-NEXT: [[ARRAYIDX_83:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_82]] -; CHECK-NEXT: [[VAL_83:%.*]] = load i32, ptr [[ARRAYIDX_83]], align 4 -; CHECK-NEXT: [[SUM_NEXT_83:%.*]] = add nsw i32 [[VAL_83]], [[SUM_NEXT_82]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_83:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 84 -; CHECK-NEXT: [[ARRAYIDX_84:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_83]] -; CHECK-NEXT: [[VAL_84:%.*]] = load i32, ptr [[ARRAYIDX_84]], align 4 -; CHECK-NEXT: [[SUM_NEXT_84:%.*]] = add nsw i32 [[VAL_84]], [[SUM_NEXT_83]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_84:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 85 -; CHECK-NEXT: [[ARRAYIDX_85:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_84]] -; CHECK-NEXT: [[VAL_85:%.*]] = load i32, ptr [[ARRAYIDX_85]], align 4 -; CHECK-NEXT: [[SUM_NEXT_85:%.*]] = add nsw i32 [[VAL_85]], [[SUM_NEXT_84]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_85:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 86 -; CHECK-NEXT: [[ARRAYIDX_86:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_85]] -; CHECK-NEXT: [[VAL_86:%.*]] = load i32, ptr [[ARRAYIDX_86]], align 4 -; CHECK-NEXT: [[SUM_NEXT_86:%.*]] = add nsw i32 [[VAL_86]], [[SUM_NEXT_85]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_86:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 87 -; CHECK-NEXT: [[ARRAYIDX_87:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_86]] -; CHECK-NEXT: [[VAL_87:%.*]] = load i32, ptr [[ARRAYIDX_87]], align 4 -; CHECK-NEXT: [[SUM_NEXT_87:%.*]] = add nsw i32 [[VAL_87]], [[SUM_NEXT_86]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_87:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 88 -; CHECK-NEXT: [[ARRAYIDX_88:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_87]] -; CHECK-NEXT: [[VAL_88:%.*]] = load i32, ptr [[ARRAYIDX_88]], align 4 -; CHECK-NEXT: [[SUM_NEXT_88:%.*]] = add nsw i32 [[VAL_88]], [[SUM_NEXT_87]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_88:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 89 -; CHECK-NEXT: [[ARRAYIDX_89:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_88]] -; CHECK-NEXT: [[VAL_89:%.*]] = load i32, ptr [[ARRAYIDX_89]], align 4 -; CHECK-NEXT: [[SUM_NEXT_89:%.*]] = add nsw i32 [[VAL_89]], [[SUM_NEXT_88]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_89:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 90 -; CHECK-NEXT: [[ARRAYIDX_90:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_89]] -; CHECK-NEXT: [[VAL_90:%.*]] = load i32, ptr [[ARRAYIDX_90]], align 4 -; CHECK-NEXT: [[SUM_NEXT_90:%.*]] = add nsw i32 [[VAL_90]], [[SUM_NEXT_89]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_90:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 91 -; CHECK-NEXT: [[ARRAYIDX_91:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_90]] -; CHECK-NEXT: [[VAL_91:%.*]] = load i32, ptr [[ARRAYIDX_91]], align 4 -; CHECK-NEXT: [[SUM_NEXT_91:%.*]] = add nsw i32 [[VAL_91]], [[SUM_NEXT_90]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_91:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 92 -; CHECK-NEXT: [[ARRAYIDX_92:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_91]] -; CHECK-NEXT: [[VAL_92:%.*]] = load i32, ptr [[ARRAYIDX_92]], align 4 -; CHECK-NEXT: [[SUM_NEXT_92:%.*]] = add nsw i32 [[VAL_92]], [[SUM_NEXT_91]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_92:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 93 -; CHECK-NEXT: [[ARRAYIDX_93:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_92]] -; CHECK-NEXT: [[VAL_93:%.*]] = load i32, ptr [[ARRAYIDX_93]], align 4 -; CHECK-NEXT: [[SUM_NEXT_93:%.*]] = add nsw i32 [[VAL_93]], [[SUM_NEXT_92]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_93:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 94 -; CHECK-NEXT: [[ARRAYIDX_94:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_93]] -; CHECK-NEXT: [[VAL_94:%.*]] = load i32, ptr [[ARRAYIDX_94]], align 4 -; CHECK-NEXT: [[SUM_NEXT_94:%.*]] = add nsw i32 [[VAL_94]], [[SUM_NEXT_93]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_94:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 95 -; CHECK-NEXT: [[ARRAYIDX_95:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_94]] -; CHECK-NEXT: [[VAL_95:%.*]] = load i32, ptr [[ARRAYIDX_95]], align 4 -; CHECK-NEXT: [[SUM_NEXT_95:%.*]] = add nsw i32 [[VAL_95]], [[SUM_NEXT_94]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_95:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 96 -; CHECK-NEXT: [[ARRAYIDX_96:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_95]] -; CHECK-NEXT: [[VAL_96:%.*]] = load i32, ptr [[ARRAYIDX_96]], align 4 -; CHECK-NEXT: [[SUM_NEXT_96:%.*]] = add nsw i32 [[VAL_96]], [[SUM_NEXT_95]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_96:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 97 -; CHECK-NEXT: [[ARRAYIDX_97:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_96]] -; CHECK-NEXT: [[VAL_97:%.*]] = load i32, ptr [[ARRAYIDX_97]], align 4 -; CHECK-NEXT: [[SUM_NEXT_97:%.*]] = add nsw i32 [[VAL_97]], [[SUM_NEXT_96]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_97:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 98 -; CHECK-NEXT: [[ARRAYIDX_98:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_97]] -; CHECK-NEXT: [[VAL_98:%.*]] = load i32, ptr [[ARRAYIDX_98]], align 4 -; CHECK-NEXT: [[SUM_NEXT_98:%.*]] = add nsw i32 [[VAL_98]], [[SUM_NEXT_97]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_98:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 99 -; CHECK-NEXT: [[ARRAYIDX_99:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_98]] -; CHECK-NEXT: [[VAL_99:%.*]] = load i32, ptr [[ARRAYIDX_99]], align 4 -; CHECK-NEXT: [[SUM_NEXT_99:%.*]] = add nsw i32 [[VAL_99]], [[SUM_NEXT_98]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_99:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 100 -; CHECK-NEXT: [[ARRAYIDX_100:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_99]] -; CHECK-NEXT: [[VAL_100:%.*]] = load i32, ptr [[ARRAYIDX_100]], align 4 -; CHECK-NEXT: [[SUM_NEXT_100:%.*]] = add nsw i32 [[VAL_100]], [[SUM_NEXT_99]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_100:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 101 -; CHECK-NEXT: [[ARRAYIDX_101:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_100]] -; CHECK-NEXT: [[VAL_101:%.*]] = load i32, ptr [[ARRAYIDX_101]], align 4 -; CHECK-NEXT: [[SUM_NEXT_101:%.*]] = add nsw i32 [[VAL_101]], [[SUM_NEXT_100]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_101:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 102 -; CHECK-NEXT: [[ARRAYIDX_102:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_101]] -; CHECK-NEXT: [[VAL_102:%.*]] = load i32, ptr [[ARRAYIDX_102]], align 4 -; CHECK-NEXT: [[SUM_NEXT_102:%.*]] = add nsw i32 [[VAL_102]], [[SUM_NEXT_101]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_102:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 103 -; CHECK-NEXT: [[ARRAYIDX_103:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_102]] -; CHECK-NEXT: [[VAL_103:%.*]] = load i32, ptr [[ARRAYIDX_103]], align 4 -; CHECK-NEXT: [[SUM_NEXT_103:%.*]] = add nsw i32 [[VAL_103]], [[SUM_NEXT_102]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_103:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 104 -; CHECK-NEXT: [[ARRAYIDX_104:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_103]] -; CHECK-NEXT: [[VAL_104:%.*]] = load i32, ptr [[ARRAYIDX_104]], align 4 -; CHECK-NEXT: [[SUM_NEXT_104:%.*]] = add nsw i32 [[VAL_104]], [[SUM_NEXT_103]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_104:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 105 -; CHECK-NEXT: [[ARRAYIDX_105:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_104]] -; CHECK-NEXT: [[VAL_105:%.*]] = load i32, ptr [[ARRAYIDX_105]], align 4 -; CHECK-NEXT: [[SUM_NEXT_105:%.*]] = add nsw i32 [[VAL_105]], [[SUM_NEXT_104]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_105:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 106 -; CHECK-NEXT: [[ARRAYIDX_106:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_105]] -; CHECK-NEXT: [[VAL_106:%.*]] = load i32, ptr [[ARRAYIDX_106]], align 4 -; CHECK-NEXT: [[SUM_NEXT_106:%.*]] = add nsw i32 [[VAL_106]], [[SUM_NEXT_105]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_106:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 107 -; CHECK-NEXT: [[ARRAYIDX_107:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_106]] -; CHECK-NEXT: [[VAL_107:%.*]] = load i32, ptr [[ARRAYIDX_107]], align 4 -; CHECK-NEXT: [[SUM_NEXT_107:%.*]] = add nsw i32 [[VAL_107]], [[SUM_NEXT_106]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_107:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 108 -; CHECK-NEXT: [[ARRAYIDX_108:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_107]] -; CHECK-NEXT: [[VAL_108:%.*]] = load i32, ptr [[ARRAYIDX_108]], align 4 -; CHECK-NEXT: [[SUM_NEXT_108:%.*]] = add nsw i32 [[VAL_108]], [[SUM_NEXT_107]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_108:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 109 -; CHECK-NEXT: [[ARRAYIDX_109:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_108]] -; CHECK-NEXT: [[VAL_109:%.*]] = load i32, ptr [[ARRAYIDX_109]], align 4 -; CHECK-NEXT: [[SUM_NEXT_109:%.*]] = add nsw i32 [[VAL_109]], [[SUM_NEXT_108]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_109:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 110 -; CHECK-NEXT: [[ARRAYIDX_110:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_109]] -; CHECK-NEXT: [[VAL_110:%.*]] = load i32, ptr [[ARRAYIDX_110]], align 4 -; CHECK-NEXT: [[SUM_NEXT_110:%.*]] = add nsw i32 [[VAL_110]], [[SUM_NEXT_109]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_110:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 111 -; CHECK-NEXT: [[ARRAYIDX_111:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_110]] -; CHECK-NEXT: [[VAL_111:%.*]] = load i32, ptr [[ARRAYIDX_111]], align 4 -; CHECK-NEXT: [[SUM_NEXT_111:%.*]] = add nsw i32 [[VAL_111]], [[SUM_NEXT_110]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_111:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 112 -; CHECK-NEXT: [[ARRAYIDX_112:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_111]] -; CHECK-NEXT: [[VAL_112:%.*]] = load i32, ptr [[ARRAYIDX_112]], align 4 -; CHECK-NEXT: [[SUM_NEXT_112:%.*]] = add nsw i32 [[VAL_112]], [[SUM_NEXT_111]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_112:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 113 -; CHECK-NEXT: [[ARRAYIDX_113:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_112]] -; CHECK-NEXT: [[VAL_113:%.*]] = load i32, ptr [[ARRAYIDX_113]], align 4 -; CHECK-NEXT: [[SUM_NEXT_113:%.*]] = add nsw i32 [[VAL_113]], [[SUM_NEXT_112]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_113:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 114 -; CHECK-NEXT: [[ARRAYIDX_114:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_113]] -; CHECK-NEXT: [[VAL_114:%.*]] = load i32, ptr [[ARRAYIDX_114]], align 4 -; CHECK-NEXT: [[SUM_NEXT_114:%.*]] = add nsw i32 [[VAL_114]], [[SUM_NEXT_113]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_114:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 115 -; CHECK-NEXT: [[ARRAYIDX_115:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_114]] -; CHECK-NEXT: [[VAL_115:%.*]] = load i32, ptr [[ARRAYIDX_115]], align 4 -; CHECK-NEXT: [[SUM_NEXT_115:%.*]] = add nsw i32 [[VAL_115]], [[SUM_NEXT_114]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_115:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 116 -; CHECK-NEXT: [[ARRAYIDX_116:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_115]] -; CHECK-NEXT: [[VAL_116:%.*]] = load i32, ptr [[ARRAYIDX_116]], align 4 -; CHECK-NEXT: [[SUM_NEXT_116:%.*]] = add nsw i32 [[VAL_116]], [[SUM_NEXT_115]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_116:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 117 -; CHECK-NEXT: [[ARRAYIDX_117:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_116]] -; CHECK-NEXT: [[VAL_117:%.*]] = load i32, ptr [[ARRAYIDX_117]], align 4 -; CHECK-NEXT: [[SUM_NEXT_117:%.*]] = add nsw i32 [[VAL_117]], [[SUM_NEXT_116]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_117:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 118 -; CHECK-NEXT: [[ARRAYIDX_118:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_117]] -; CHECK-NEXT: [[VAL_118:%.*]] = load i32, ptr [[ARRAYIDX_118]], align 4 -; CHECK-NEXT: [[SUM_NEXT_118:%.*]] = add nsw i32 [[VAL_118]], [[SUM_NEXT_117]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_118:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 119 -; CHECK-NEXT: [[ARRAYIDX_119:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_118]] -; CHECK-NEXT: [[VAL_119:%.*]] = load i32, ptr [[ARRAYIDX_119]], align 4 -; CHECK-NEXT: [[SUM_NEXT_119:%.*]] = add nsw i32 [[VAL_119]], [[SUM_NEXT_118]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_119:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 120 -; CHECK-NEXT: [[ARRAYIDX_120:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_119]] -; CHECK-NEXT: [[VAL_120:%.*]] = load i32, ptr [[ARRAYIDX_120]], align 4 -; CHECK-NEXT: [[SUM_NEXT_120:%.*]] = add nsw i32 [[VAL_120]], [[SUM_NEXT_119]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_120:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 121 -; CHECK-NEXT: [[ARRAYIDX_121:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_120]] -; CHECK-NEXT: [[VAL_121:%.*]] = load i32, ptr [[ARRAYIDX_121]], align 4 -; CHECK-NEXT: [[SUM_NEXT_121:%.*]] = add nsw i32 [[VAL_121]], [[SUM_NEXT_120]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_121:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 122 -; CHECK-NEXT: [[ARRAYIDX_122:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_121]] -; CHECK-NEXT: [[VAL_122:%.*]] = load i32, ptr [[ARRAYIDX_122]], align 4 -; CHECK-NEXT: [[SUM_NEXT_122:%.*]] = add nsw i32 [[VAL_122]], [[SUM_NEXT_121]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_122:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 123 -; CHECK-NEXT: [[ARRAYIDX_123:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_122]] -; CHECK-NEXT: [[VAL_123:%.*]] = load i32, ptr [[ARRAYIDX_123]], align 4 -; CHECK-NEXT: [[SUM_NEXT_123:%.*]] = add nsw i32 [[VAL_123]], [[SUM_NEXT_122]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_123:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 124 -; CHECK-NEXT: [[ARRAYIDX_124:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_123]] -; CHECK-NEXT: [[VAL_124:%.*]] = load i32, ptr [[ARRAYIDX_124]], align 4 -; CHECK-NEXT: [[SUM_NEXT_124:%.*]] = add nsw i32 [[VAL_124]], [[SUM_NEXT_123]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_124:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 125 -; CHECK-NEXT: [[ARRAYIDX_125:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_124]] -; CHECK-NEXT: [[VAL_125:%.*]] = load i32, ptr [[ARRAYIDX_125]], align 4 -; CHECK-NEXT: [[SUM_NEXT_125:%.*]] = add nsw i32 [[VAL_125]], [[SUM_NEXT_124]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_125:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 126 -; CHECK-NEXT: [[ARRAYIDX_126:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_125]] -; CHECK-NEXT: [[VAL_126:%.*]] = load i32, ptr [[ARRAYIDX_126]], align 4 -; CHECK-NEXT: [[SUM_NEXT_126:%.*]] = add nsw i32 [[VAL_126]], [[SUM_NEXT_125]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_126:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 127 -; CHECK-NEXT: [[ARRAYIDX_127:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_126]] -; CHECK-NEXT: [[VAL_127:%.*]] = load i32, ptr [[ARRAYIDX_127]], align 4 -; CHECK-NEXT: [[SUM_NEXT_127]] = add nsw i32 [[VAL_127]], [[SUM_NEXT_126]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_127]] = add nuw nsw i64 [[INDVARS_IV]], 128 -; CHECK-NEXT: [[EXITCOND_NOT_127:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT_127]], 8192 -; CHECK-NEXT: br i1 [[EXITCOND_NOT_127]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]] +; CHECK-NEXT: [[SUM_NEXT_31]] = add nsw i32 [[VAL_31]], [[SUM_NEXT_30]] +; CHECK-NEXT: [[INDVARS_IV_NEXT_31]] = add nuw nsw i64 [[INDVARS_IV]], 32 +; CHECK-NEXT: [[EXITCOND_NOT_31:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT_31]], 8192 +; CHECK-NEXT: br i1 [[EXITCOND_NOT_31]], label [[FOR_COND_CLEANUP:%.*]], label [[FOR_BODY]] ; CHECK: for.cond.cleanup: -; CHECK-NEXT: [[SUM_NEXT_LCSSA:%.*]] = phi i32 [ [[SUM_NEXT_127]], [[FOR_BODY]] ] +; CHECK-NEXT: [[SUM_NEXT_LCSSA:%.*]] = phi i32 [ [[SUM_NEXT_31]], [[FOR_BODY]] ] ; CHECK-NEXT: ret i32 [[SUM_NEXT_LCSSA]] ; entry: @@ -551,16 +167,16 @@ define i32 @test2(ptr %ary, i64 %n) "target-cpu"="znver3" { ; CHECK-SAME: ptr [[ARY:%.*]], i64 [[N:%.*]]) #[[ATTR0]] { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[N]], -1 -; CHECK-NEXT: [[XTRAITER:%.*]] = and i64 [[N]], 7 -; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i64 [[TMP0]], 7 +; CHECK-NEXT: [[XTRAITER:%.*]] = and i64 [[N]], 1 +; CHECK-NEXT: [[TMP1:%.*]] = icmp ult i64 [[TMP0]], 1 ; CHECK-NEXT: br i1 [[TMP1]], label [[FOR_COND_CLEANUP_UNR_LCSSA:%.*]], label [[ENTRY_NEW:%.*]] ; CHECK: entry.new: ; CHECK-NEXT: [[UNROLL_ITER:%.*]] = sub i64 [[N]], [[XTRAITER]] ; CHECK-NEXT: br label [[FOR_BODY:%.*]] ; CHECK: for.body: -; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY_NEW]] ], [ [[INDVARS_IV_NEXT_7:%.*]], [[FOR_BODY]] ] -; CHECK-NEXT: [[SUM:%.*]] = phi i32 [ 0, [[ENTRY_NEW]] ], [ [[SUM_NEXT_7:%.*]], [[FOR_BODY]] ] -; CHECK-NEXT: [[NITER:%.*]] = phi i64 [ 0, [[ENTRY_NEW]] ], [ [[NITER_NEXT_7:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, [[ENTRY_NEW]] ], [ [[INDVARS_IV_NEXT_1:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[SUM:%.*]] = phi i32 [ 0, [[ENTRY_NEW]] ], [ [[SUM_NEXT_1:%.*]], [[FOR_BODY]] ] +; CHECK-NEXT: [[NITER:%.*]] = phi i64 [ 0, [[ENTRY_NEW]] ], [ [[NITER_NEXT_1:%.*]], [[FOR_BODY]] ] ; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV]] ; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAYIDX]], align 4 ; CHECK-NEXT: [[DUMMY1:%.*]] = mul i32 [[VAL]], [[VAL]] @@ -667,339 +283,15 @@ define i32 @test2(ptr %ary, i64 %n) "target-cpu"="znver3" { ; CHECK-NEXT: [[DUMMY48_1:%.*]] = mul i32 [[DUMMY47_1]], [[DUMMY47_1]] ; CHECK-NEXT: [[DUMMY49_1:%.*]] = mul i32 [[DUMMY48_1]], [[DUMMY48_1]] ; CHECK-NEXT: [[DUMMY50_1:%.*]] = mul i32 [[DUMMY49_1]], [[DUMMY49_1]] -; CHECK-NEXT: [[SUM_NEXT_1:%.*]] = add nsw i32 [[DUMMY50_1]], [[SUM_NEXT]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_1:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 2 -; CHECK-NEXT: [[ARRAYIDX_2:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_1]] -; CHECK-NEXT: [[VAL_2:%.*]] = load i32, ptr [[ARRAYIDX_2]], align 4 -; CHECK-NEXT: [[DUMMY1_2:%.*]] = mul i32 [[VAL_2]], [[VAL_2]] -; CHECK-NEXT: [[DUMMY2_2:%.*]] = mul i32 [[DUMMY1_2]], [[DUMMY1_2]] -; CHECK-NEXT: [[DUMMY3_2:%.*]] = mul i32 [[DUMMY2_2]], [[DUMMY2_2]] -; CHECK-NEXT: [[DUMMY4_2:%.*]] = mul i32 [[DUMMY3_2]], [[DUMMY3_2]] -; CHECK-NEXT: [[DUMMY5_2:%.*]] = mul i32 [[DUMMY4_2]], [[DUMMY4_2]] -; CHECK-NEXT: [[DUMMY6_2:%.*]] = mul i32 [[DUMMY5_2]], [[DUMMY5_2]] -; CHECK-NEXT: [[DUMMY7_2:%.*]] = mul i32 [[DUMMY6_2]], [[DUMMY6_2]] -; CHECK-NEXT: [[DUMMY8_2:%.*]] = mul i32 [[DUMMY7_2]], [[DUMMY7_2]] -; CHECK-NEXT: [[DUMMY9_2:%.*]] = mul i32 [[DUMMY8_2]], [[DUMMY8_2]] -; CHECK-NEXT: [[DUMMY10_2:%.*]] = mul i32 [[DUMMY9_2]], [[DUMMY9_2]] -; CHECK-NEXT: [[DUMMY11_2:%.*]] = mul i32 [[DUMMY10_2]], [[DUMMY10_2]] -; CHECK-NEXT: [[DUMMY12_2:%.*]] = mul i32 [[DUMMY11_2]], [[DUMMY11_2]] -; CHECK-NEXT: [[DUMMY13_2:%.*]] = mul i32 [[DUMMY12_2]], [[DUMMY12_2]] -; CHECK-NEXT: [[DUMMY14_2:%.*]] = mul i32 [[DUMMY13_2]], [[DUMMY13_2]] -; CHECK-NEXT: [[DUMMY15_2:%.*]] = mul i32 [[DUMMY14_2]], [[DUMMY14_2]] -; CHECK-NEXT: [[DUMMY16_2:%.*]] = mul i32 [[DUMMY15_2]], [[DUMMY15_2]] -; CHECK-NEXT: [[DUMMY17_2:%.*]] = mul i32 [[DUMMY16_2]], [[DUMMY16_2]] -; CHECK-NEXT: [[DUMMY18_2:%.*]] = mul i32 [[DUMMY17_2]], [[DUMMY17_2]] -; CHECK-NEXT: [[DUMMY19_2:%.*]] = mul i32 [[DUMMY18_2]], [[DUMMY18_2]] -; CHECK-NEXT: [[DUMMY20_2:%.*]] = mul i32 [[DUMMY19_2]], [[DUMMY19_2]] -; CHECK-NEXT: [[DUMMY21_2:%.*]] = mul i32 [[DUMMY20_2]], [[DUMMY20_2]] -; CHECK-NEXT: [[DUMMY22_2:%.*]] = mul i32 [[DUMMY21_2]], [[DUMMY21_2]] -; CHECK-NEXT: [[DUMMY23_2:%.*]] = mul i32 [[DUMMY22_2]], [[DUMMY22_2]] -; CHECK-NEXT: [[DUMMY24_2:%.*]] = mul i32 [[DUMMY23_2]], [[DUMMY23_2]] -; CHECK-NEXT: [[DUMMY25_2:%.*]] = mul i32 [[DUMMY24_2]], [[DUMMY24_2]] -; CHECK-NEXT: [[DUMMY26_2:%.*]] = mul i32 [[DUMMY25_2]], [[DUMMY25_2]] -; CHECK-NEXT: [[DUMMY27_2:%.*]] = mul i32 [[DUMMY26_2]], [[DUMMY26_2]] -; CHECK-NEXT: [[DUMMY28_2:%.*]] = mul i32 [[DUMMY27_2]], [[DUMMY27_2]] -; CHECK-NEXT: [[DUMMY29_2:%.*]] = mul i32 [[DUMMY28_2]], [[DUMMY28_2]] -; CHECK-NEXT: [[DUMMY30_2:%.*]] = mul i32 [[DUMMY29_2]], [[DUMMY29_2]] -; CHECK-NEXT: [[DUMMY31_2:%.*]] = mul i32 [[DUMMY30_2]], [[DUMMY30_2]] -; CHECK-NEXT: [[DUMMY32_2:%.*]] = mul i32 [[DUMMY31_2]], [[DUMMY31_2]] -; CHECK-NEXT: [[DUMMY33_2:%.*]] = mul i32 [[DUMMY32_2]], [[DUMMY32_2]] -; CHECK-NEXT: [[DUMMY34_2:%.*]] = mul i32 [[DUMMY33_2]], [[DUMMY33_2]] -; CHECK-NEXT: [[DUMMY35_2:%.*]] = mul i32 [[DUMMY34_2]], [[DUMMY34_2]] -; CHECK-NEXT: [[DUMMY36_2:%.*]] = mul i32 [[DUMMY35_2]], [[DUMMY35_2]] -; CHECK-NEXT: [[DUMMY37_2:%.*]] = mul i32 [[DUMMY36_2]], [[DUMMY36_2]] -; CHECK-NEXT: [[DUMMY38_2:%.*]] = mul i32 [[DUMMY37_2]], [[DUMMY37_2]] -; CHECK-NEXT: [[DUMMY39_2:%.*]] = mul i32 [[DUMMY38_2]], [[DUMMY38_2]] -; CHECK-NEXT: [[DUMMY40_2:%.*]] = mul i32 [[DUMMY39_2]], [[DUMMY39_2]] -; CHECK-NEXT: [[DUMMY41_2:%.*]] = mul i32 [[DUMMY40_2]], [[DUMMY40_2]] -; CHECK-NEXT: [[DUMMY42_2:%.*]] = mul i32 [[DUMMY41_2]], [[DUMMY41_2]] -; CHECK-NEXT: [[DUMMY43_2:%.*]] = mul i32 [[DUMMY42_2]], [[DUMMY42_2]] -; CHECK-NEXT: [[DUMMY44_2:%.*]] = mul i32 [[DUMMY43_2]], [[DUMMY43_2]] -; CHECK-NEXT: [[DUMMY45_2:%.*]] = mul i32 [[DUMMY44_2]], [[DUMMY44_2]] -; CHECK-NEXT: [[DUMMY46_2:%.*]] = mul i32 [[DUMMY45_2]], [[DUMMY45_2]] -; CHECK-NEXT: [[DUMMY47_2:%.*]] = mul i32 [[DUMMY46_2]], [[DUMMY46_2]] -; CHECK-NEXT: [[DUMMY48_2:%.*]] = mul i32 [[DUMMY47_2]], [[DUMMY47_2]] -; CHECK-NEXT: [[DUMMY49_2:%.*]] = mul i32 [[DUMMY48_2]], [[DUMMY48_2]] -; CHECK-NEXT: [[DUMMY50_2:%.*]] = mul i32 [[DUMMY49_2]], [[DUMMY49_2]] -; CHECK-NEXT: [[SUM_NEXT_2:%.*]] = add nsw i32 [[DUMMY50_2]], [[SUM_NEXT_1]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_2:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 3 -; CHECK-NEXT: [[ARRAYIDX_3:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_2]] -; CHECK-NEXT: [[VAL_3:%.*]] = load i32, ptr [[ARRAYIDX_3]], align 4 -; CHECK-NEXT: [[DUMMY1_3:%.*]] = mul i32 [[VAL_3]], [[VAL_3]] -; CHECK-NEXT: [[DUMMY2_3:%.*]] = mul i32 [[DUMMY1_3]], [[DUMMY1_3]] -; CHECK-NEXT: [[DUMMY3_3:%.*]] = mul i32 [[DUMMY2_3]], [[DUMMY2_3]] -; CHECK-NEXT: [[DUMMY4_3:%.*]] = mul i32 [[DUMMY3_3]], [[DUMMY3_3]] -; CHECK-NEXT: [[DUMMY5_3:%.*]] = mul i32 [[DUMMY4_3]], [[DUMMY4_3]] -; CHECK-NEXT: [[DUMMY6_3:%.*]] = mul i32 [[DUMMY5_3]], [[DUMMY5_3]] -; CHECK-NEXT: [[DUMMY7_3:%.*]] = mul i32 [[DUMMY6_3]], [[DUMMY6_3]] -; CHECK-NEXT: [[DUMMY8_3:%.*]] = mul i32 [[DUMMY7_3]], [[DUMMY7_3]] -; CHECK-NEXT: [[DUMMY9_3:%.*]] = mul i32 [[DUMMY8_3]], [[DUMMY8_3]] -; CHECK-NEXT: [[DUMMY10_3:%.*]] = mul i32 [[DUMMY9_3]], [[DUMMY9_3]] -; CHECK-NEXT: [[DUMMY11_3:%.*]] = mul i32 [[DUMMY10_3]], [[DUMMY10_3]] -; CHECK-NEXT: [[DUMMY12_3:%.*]] = mul i32 [[DUMMY11_3]], [[DUMMY11_3]] -; CHECK-NEXT: [[DUMMY13_3:%.*]] = mul i32 [[DUMMY12_3]], [[DUMMY12_3]] -; CHECK-NEXT: [[DUMMY14_3:%.*]] = mul i32 [[DUMMY13_3]], [[DUMMY13_3]] -; CHECK-NEXT: [[DUMMY15_3:%.*]] = mul i32 [[DUMMY14_3]], [[DUMMY14_3]] -; CHECK-NEXT: [[DUMMY16_3:%.*]] = mul i32 [[DUMMY15_3]], [[DUMMY15_3]] -; CHECK-NEXT: [[DUMMY17_3:%.*]] = mul i32 [[DUMMY16_3]], [[DUMMY16_3]] -; CHECK-NEXT: [[DUMMY18_3:%.*]] = mul i32 [[DUMMY17_3]], [[DUMMY17_3]] -; CHECK-NEXT: [[DUMMY19_3:%.*]] = mul i32 [[DUMMY18_3]], [[DUMMY18_3]] -; CHECK-NEXT: [[DUMMY20_3:%.*]] = mul i32 [[DUMMY19_3]], [[DUMMY19_3]] -; CHECK-NEXT: [[DUMMY21_3:%.*]] = mul i32 [[DUMMY20_3]], [[DUMMY20_3]] -; CHECK-NEXT: [[DUMMY22_3:%.*]] = mul i32 [[DUMMY21_3]], [[DUMMY21_3]] -; CHECK-NEXT: [[DUMMY23_3:%.*]] = mul i32 [[DUMMY22_3]], [[DUMMY22_3]] -; CHECK-NEXT: [[DUMMY24_3:%.*]] = mul i32 [[DUMMY23_3]], [[DUMMY23_3]] -; CHECK-NEXT: [[DUMMY25_3:%.*]] = mul i32 [[DUMMY24_3]], [[DUMMY24_3]] -; CHECK-NEXT: [[DUMMY26_3:%.*]] = mul i32 [[DUMMY25_3]], [[DUMMY25_3]] -; CHECK-NEXT: [[DUMMY27_3:%.*]] = mul i32 [[DUMMY26_3]], [[DUMMY26_3]] -; CHECK-NEXT: [[DUMMY28_3:%.*]] = mul i32 [[DUMMY27_3]], [[DUMMY27_3]] -; CHECK-NEXT: [[DUMMY29_3:%.*]] = mul i32 [[DUMMY28_3]], [[DUMMY28_3]] -; CHECK-NEXT: [[DUMMY30_3:%.*]] = mul i32 [[DUMMY29_3]], [[DUMMY29_3]] -; CHECK-NEXT: [[DUMMY31_3:%.*]] = mul i32 [[DUMMY30_3]], [[DUMMY30_3]] -; CHECK-NEXT: [[DUMMY32_3:%.*]] = mul i32 [[DUMMY31_3]], [[DUMMY31_3]] -; CHECK-NEXT: [[DUMMY33_3:%.*]] = mul i32 [[DUMMY32_3]], [[DUMMY32_3]] -; CHECK-NEXT: [[DUMMY34_3:%.*]] = mul i32 [[DUMMY33_3]], [[DUMMY33_3]] -; CHECK-NEXT: [[DUMMY35_3:%.*]] = mul i32 [[DUMMY34_3]], [[DUMMY34_3]] -; CHECK-NEXT: [[DUMMY36_3:%.*]] = mul i32 [[DUMMY35_3]], [[DUMMY35_3]] -; CHECK-NEXT: [[DUMMY37_3:%.*]] = mul i32 [[DUMMY36_3]], [[DUMMY36_3]] -; CHECK-NEXT: [[DUMMY38_3:%.*]] = mul i32 [[DUMMY37_3]], [[DUMMY37_3]] -; CHECK-NEXT: [[DUMMY39_3:%.*]] = mul i32 [[DUMMY38_3]], [[DUMMY38_3]] -; CHECK-NEXT: [[DUMMY40_3:%.*]] = mul i32 [[DUMMY39_3]], [[DUMMY39_3]] -; CHECK-NEXT: [[DUMMY41_3:%.*]] = mul i32 [[DUMMY40_3]], [[DUMMY40_3]] -; CHECK-NEXT: [[DUMMY42_3:%.*]] = mul i32 [[DUMMY41_3]], [[DUMMY41_3]] -; CHECK-NEXT: [[DUMMY43_3:%.*]] = mul i32 [[DUMMY42_3]], [[DUMMY42_3]] -; CHECK-NEXT: [[DUMMY44_3:%.*]] = mul i32 [[DUMMY43_3]], [[DUMMY43_3]] -; CHECK-NEXT: [[DUMMY45_3:%.*]] = mul i32 [[DUMMY44_3]], [[DUMMY44_3]] -; CHECK-NEXT: [[DUMMY46_3:%.*]] = mul i32 [[DUMMY45_3]], [[DUMMY45_3]] -; CHECK-NEXT: [[DUMMY47_3:%.*]] = mul i32 [[DUMMY46_3]], [[DUMMY46_3]] -; CHECK-NEXT: [[DUMMY48_3:%.*]] = mul i32 [[DUMMY47_3]], [[DUMMY47_3]] -; CHECK-NEXT: [[DUMMY49_3:%.*]] = mul i32 [[DUMMY48_3]], [[DUMMY48_3]] -; CHECK-NEXT: [[DUMMY50_3:%.*]] = mul i32 [[DUMMY49_3]], [[DUMMY49_3]] -; CHECK-NEXT: [[SUM_NEXT_3:%.*]] = add nsw i32 [[DUMMY50_3]], [[SUM_NEXT_2]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_3:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 4 -; CHECK-NEXT: [[ARRAYIDX_4:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_3]] -; CHECK-NEXT: [[VAL_4:%.*]] = load i32, ptr [[ARRAYIDX_4]], align 4 -; CHECK-NEXT: [[DUMMY1_4:%.*]] = mul i32 [[VAL_4]], [[VAL_4]] -; CHECK-NEXT: [[DUMMY2_4:%.*]] = mul i32 [[DUMMY1_4]], [[DUMMY1_4]] -; CHECK-NEXT: [[DUMMY3_4:%.*]] = mul i32 [[DUMMY2_4]], [[DUMMY2_4]] -; CHECK-NEXT: [[DUMMY4_4:%.*]] = mul i32 [[DUMMY3_4]], [[DUMMY3_4]] -; CHECK-NEXT: [[DUMMY5_4:%.*]] = mul i32 [[DUMMY4_4]], [[DUMMY4_4]] -; CHECK-NEXT: [[DUMMY6_4:%.*]] = mul i32 [[DUMMY5_4]], [[DUMMY5_4]] -; CHECK-NEXT: [[DUMMY7_4:%.*]] = mul i32 [[DUMMY6_4]], [[DUMMY6_4]] -; CHECK-NEXT: [[DUMMY8_4:%.*]] = mul i32 [[DUMMY7_4]], [[DUMMY7_4]] -; CHECK-NEXT: [[DUMMY9_4:%.*]] = mul i32 [[DUMMY8_4]], [[DUMMY8_4]] -; CHECK-NEXT: [[DUMMY10_4:%.*]] = mul i32 [[DUMMY9_4]], [[DUMMY9_4]] -; CHECK-NEXT: [[DUMMY11_4:%.*]] = mul i32 [[DUMMY10_4]], [[DUMMY10_4]] -; CHECK-NEXT: [[DUMMY12_4:%.*]] = mul i32 [[DUMMY11_4]], [[DUMMY11_4]] -; CHECK-NEXT: [[DUMMY13_4:%.*]] = mul i32 [[DUMMY12_4]], [[DUMMY12_4]] -; CHECK-NEXT: [[DUMMY14_4:%.*]] = mul i32 [[DUMMY13_4]], [[DUMMY13_4]] -; CHECK-NEXT: [[DUMMY15_4:%.*]] = mul i32 [[DUMMY14_4]], [[DUMMY14_4]] -; CHECK-NEXT: [[DUMMY16_4:%.*]] = mul i32 [[DUMMY15_4]], [[DUMMY15_4]] -; CHECK-NEXT: [[DUMMY17_4:%.*]] = mul i32 [[DUMMY16_4]], [[DUMMY16_4]] -; CHECK-NEXT: [[DUMMY18_4:%.*]] = mul i32 [[DUMMY17_4]], [[DUMMY17_4]] -; CHECK-NEXT: [[DUMMY19_4:%.*]] = mul i32 [[DUMMY18_4]], [[DUMMY18_4]] -; CHECK-NEXT: [[DUMMY20_4:%.*]] = mul i32 [[DUMMY19_4]], [[DUMMY19_4]] -; CHECK-NEXT: [[DUMMY21_4:%.*]] = mul i32 [[DUMMY20_4]], [[DUMMY20_4]] -; CHECK-NEXT: [[DUMMY22_4:%.*]] = mul i32 [[DUMMY21_4]], [[DUMMY21_4]] -; CHECK-NEXT: [[DUMMY23_4:%.*]] = mul i32 [[DUMMY22_4]], [[DUMMY22_4]] -; CHECK-NEXT: [[DUMMY24_4:%.*]] = mul i32 [[DUMMY23_4]], [[DUMMY23_4]] -; CHECK-NEXT: [[DUMMY25_4:%.*]] = mul i32 [[DUMMY24_4]], [[DUMMY24_4]] -; CHECK-NEXT: [[DUMMY26_4:%.*]] = mul i32 [[DUMMY25_4]], [[DUMMY25_4]] -; CHECK-NEXT: [[DUMMY27_4:%.*]] = mul i32 [[DUMMY26_4]], [[DUMMY26_4]] -; CHECK-NEXT: [[DUMMY28_4:%.*]] = mul i32 [[DUMMY27_4]], [[DUMMY27_4]] -; CHECK-NEXT: [[DUMMY29_4:%.*]] = mul i32 [[DUMMY28_4]], [[DUMMY28_4]] -; CHECK-NEXT: [[DUMMY30_4:%.*]] = mul i32 [[DUMMY29_4]], [[DUMMY29_4]] -; CHECK-NEXT: [[DUMMY31_4:%.*]] = mul i32 [[DUMMY30_4]], [[DUMMY30_4]] -; CHECK-NEXT: [[DUMMY32_4:%.*]] = mul i32 [[DUMMY31_4]], [[DUMMY31_4]] -; CHECK-NEXT: [[DUMMY33_4:%.*]] = mul i32 [[DUMMY32_4]], [[DUMMY32_4]] -; CHECK-NEXT: [[DUMMY34_4:%.*]] = mul i32 [[DUMMY33_4]], [[DUMMY33_4]] -; CHECK-NEXT: [[DUMMY35_4:%.*]] = mul i32 [[DUMMY34_4]], [[DUMMY34_4]] -; CHECK-NEXT: [[DUMMY36_4:%.*]] = mul i32 [[DUMMY35_4]], [[DUMMY35_4]] -; CHECK-NEXT: [[DUMMY37_4:%.*]] = mul i32 [[DUMMY36_4]], [[DUMMY36_4]] -; CHECK-NEXT: [[DUMMY38_4:%.*]] = mul i32 [[DUMMY37_4]], [[DUMMY37_4]] -; CHECK-NEXT: [[DUMMY39_4:%.*]] = mul i32 [[DUMMY38_4]], [[DUMMY38_4]] -; CHECK-NEXT: [[DUMMY40_4:%.*]] = mul i32 [[DUMMY39_4]], [[DUMMY39_4]] -; CHECK-NEXT: [[DUMMY41_4:%.*]] = mul i32 [[DUMMY40_4]], [[DUMMY40_4]] -; CHECK-NEXT: [[DUMMY42_4:%.*]] = mul i32 [[DUMMY41_4]], [[DUMMY41_4]] -; CHECK-NEXT: [[DUMMY43_4:%.*]] = mul i32 [[DUMMY42_4]], [[DUMMY42_4]] -; CHECK-NEXT: [[DUMMY44_4:%.*]] = mul i32 [[DUMMY43_4]], [[DUMMY43_4]] -; CHECK-NEXT: [[DUMMY45_4:%.*]] = mul i32 [[DUMMY44_4]], [[DUMMY44_4]] -; CHECK-NEXT: [[DUMMY46_4:%.*]] = mul i32 [[DUMMY45_4]], [[DUMMY45_4]] -; CHECK-NEXT: [[DUMMY47_4:%.*]] = mul i32 [[DUMMY46_4]], [[DUMMY46_4]] -; CHECK-NEXT: [[DUMMY48_4:%.*]] = mul i32 [[DUMMY47_4]], [[DUMMY47_4]] -; CHECK-NEXT: [[DUMMY49_4:%.*]] = mul i32 [[DUMMY48_4]], [[DUMMY48_4]] -; CHECK-NEXT: [[DUMMY50_4:%.*]] = mul i32 [[DUMMY49_4]], [[DUMMY49_4]] -; CHECK-NEXT: [[SUM_NEXT_4:%.*]] = add nsw i32 [[DUMMY50_4]], [[SUM_NEXT_3]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_4:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 5 -; CHECK-NEXT: [[ARRAYIDX_5:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_4]] -; CHECK-NEXT: [[VAL_5:%.*]] = load i32, ptr [[ARRAYIDX_5]], align 4 -; CHECK-NEXT: [[DUMMY1_5:%.*]] = mul i32 [[VAL_5]], [[VAL_5]] -; CHECK-NEXT: [[DUMMY2_5:%.*]] = mul i32 [[DUMMY1_5]], [[DUMMY1_5]] -; CHECK-NEXT: [[DUMMY3_5:%.*]] = mul i32 [[DUMMY2_5]], [[DUMMY2_5]] -; CHECK-NEXT: [[DUMMY4_5:%.*]] = mul i32 [[DUMMY3_5]], [[DUMMY3_5]] -; CHECK-NEXT: [[DUMMY5_5:%.*]] = mul i32 [[DUMMY4_5]], [[DUMMY4_5]] -; CHECK-NEXT: [[DUMMY6_5:%.*]] = mul i32 [[DUMMY5_5]], [[DUMMY5_5]] -; CHECK-NEXT: [[DUMMY7_5:%.*]] = mul i32 [[DUMMY6_5]], [[DUMMY6_5]] -; CHECK-NEXT: [[DUMMY8_5:%.*]] = mul i32 [[DUMMY7_5]], [[DUMMY7_5]] -; CHECK-NEXT: [[DUMMY9_5:%.*]] = mul i32 [[DUMMY8_5]], [[DUMMY8_5]] -; CHECK-NEXT: [[DUMMY10_5:%.*]] = mul i32 [[DUMMY9_5]], [[DUMMY9_5]] -; CHECK-NEXT: [[DUMMY11_5:%.*]] = mul i32 [[DUMMY10_5]], [[DUMMY10_5]] -; CHECK-NEXT: [[DUMMY12_5:%.*]] = mul i32 [[DUMMY11_5]], [[DUMMY11_5]] -; CHECK-NEXT: [[DUMMY13_5:%.*]] = mul i32 [[DUMMY12_5]], [[DUMMY12_5]] -; CHECK-NEXT: [[DUMMY14_5:%.*]] = mul i32 [[DUMMY13_5]], [[DUMMY13_5]] -; CHECK-NEXT: [[DUMMY15_5:%.*]] = mul i32 [[DUMMY14_5]], [[DUMMY14_5]] -; CHECK-NEXT: [[DUMMY16_5:%.*]] = mul i32 [[DUMMY15_5]], [[DUMMY15_5]] -; CHECK-NEXT: [[DUMMY17_5:%.*]] = mul i32 [[DUMMY16_5]], [[DUMMY16_5]] -; CHECK-NEXT: [[DUMMY18_5:%.*]] = mul i32 [[DUMMY17_5]], [[DUMMY17_5]] -; CHECK-NEXT: [[DUMMY19_5:%.*]] = mul i32 [[DUMMY18_5]], [[DUMMY18_5]] -; CHECK-NEXT: [[DUMMY20_5:%.*]] = mul i32 [[DUMMY19_5]], [[DUMMY19_5]] -; CHECK-NEXT: [[DUMMY21_5:%.*]] = mul i32 [[DUMMY20_5]], [[DUMMY20_5]] -; CHECK-NEXT: [[DUMMY22_5:%.*]] = mul i32 [[DUMMY21_5]], [[DUMMY21_5]] -; CHECK-NEXT: [[DUMMY23_5:%.*]] = mul i32 [[DUMMY22_5]], [[DUMMY22_5]] -; CHECK-NEXT: [[DUMMY24_5:%.*]] = mul i32 [[DUMMY23_5]], [[DUMMY23_5]] -; CHECK-NEXT: [[DUMMY25_5:%.*]] = mul i32 [[DUMMY24_5]], [[DUMMY24_5]] -; CHECK-NEXT: [[DUMMY26_5:%.*]] = mul i32 [[DUMMY25_5]], [[DUMMY25_5]] -; CHECK-NEXT: [[DUMMY27_5:%.*]] = mul i32 [[DUMMY26_5]], [[DUMMY26_5]] -; CHECK-NEXT: [[DUMMY28_5:%.*]] = mul i32 [[DUMMY27_5]], [[DUMMY27_5]] -; CHECK-NEXT: [[DUMMY29_5:%.*]] = mul i32 [[DUMMY28_5]], [[DUMMY28_5]] -; CHECK-NEXT: [[DUMMY30_5:%.*]] = mul i32 [[DUMMY29_5]], [[DUMMY29_5]] -; CHECK-NEXT: [[DUMMY31_5:%.*]] = mul i32 [[DUMMY30_5]], [[DUMMY30_5]] -; CHECK-NEXT: [[DUMMY32_5:%.*]] = mul i32 [[DUMMY31_5]], [[DUMMY31_5]] -; CHECK-NEXT: [[DUMMY33_5:%.*]] = mul i32 [[DUMMY32_5]], [[DUMMY32_5]] -; CHECK-NEXT: [[DUMMY34_5:%.*]] = mul i32 [[DUMMY33_5]], [[DUMMY33_5]] -; CHECK-NEXT: [[DUMMY35_5:%.*]] = mul i32 [[DUMMY34_5]], [[DUMMY34_5]] -; CHECK-NEXT: [[DUMMY36_5:%.*]] = mul i32 [[DUMMY35_5]], [[DUMMY35_5]] -; CHECK-NEXT: [[DUMMY37_5:%.*]] = mul i32 [[DUMMY36_5]], [[DUMMY36_5]] -; CHECK-NEXT: [[DUMMY38_5:%.*]] = mul i32 [[DUMMY37_5]], [[DUMMY37_5]] -; CHECK-NEXT: [[DUMMY39_5:%.*]] = mul i32 [[DUMMY38_5]], [[DUMMY38_5]] -; CHECK-NEXT: [[DUMMY40_5:%.*]] = mul i32 [[DUMMY39_5]], [[DUMMY39_5]] -; CHECK-NEXT: [[DUMMY41_5:%.*]] = mul i32 [[DUMMY40_5]], [[DUMMY40_5]] -; CHECK-NEXT: [[DUMMY42_5:%.*]] = mul i32 [[DUMMY41_5]], [[DUMMY41_5]] -; CHECK-NEXT: [[DUMMY43_5:%.*]] = mul i32 [[DUMMY42_5]], [[DUMMY42_5]] -; CHECK-NEXT: [[DUMMY44_5:%.*]] = mul i32 [[DUMMY43_5]], [[DUMMY43_5]] -; CHECK-NEXT: [[DUMMY45_5:%.*]] = mul i32 [[DUMMY44_5]], [[DUMMY44_5]] -; CHECK-NEXT: [[DUMMY46_5:%.*]] = mul i32 [[DUMMY45_5]], [[DUMMY45_5]] -; CHECK-NEXT: [[DUMMY47_5:%.*]] = mul i32 [[DUMMY46_5]], [[DUMMY46_5]] -; CHECK-NEXT: [[DUMMY48_5:%.*]] = mul i32 [[DUMMY47_5]], [[DUMMY47_5]] -; CHECK-NEXT: [[DUMMY49_5:%.*]] = mul i32 [[DUMMY48_5]], [[DUMMY48_5]] -; CHECK-NEXT: [[DUMMY50_5:%.*]] = mul i32 [[DUMMY49_5]], [[DUMMY49_5]] -; CHECK-NEXT: [[SUM_NEXT_5:%.*]] = add nsw i32 [[DUMMY50_5]], [[SUM_NEXT_4]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_5:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 6 -; CHECK-NEXT: [[ARRAYIDX_6:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_5]] -; CHECK-NEXT: [[VAL_6:%.*]] = load i32, ptr [[ARRAYIDX_6]], align 4 -; CHECK-NEXT: [[DUMMY1_6:%.*]] = mul i32 [[VAL_6]], [[VAL_6]] -; CHECK-NEXT: [[DUMMY2_6:%.*]] = mul i32 [[DUMMY1_6]], [[DUMMY1_6]] -; CHECK-NEXT: [[DUMMY3_6:%.*]] = mul i32 [[DUMMY2_6]], [[DUMMY2_6]] -; CHECK-NEXT: [[DUMMY4_6:%.*]] = mul i32 [[DUMMY3_6]], [[DUMMY3_6]] -; CHECK-NEXT: [[DUMMY5_6:%.*]] = mul i32 [[DUMMY4_6]], [[DUMMY4_6]] -; CHECK-NEXT: [[DUMMY6_6:%.*]] = mul i32 [[DUMMY5_6]], [[DUMMY5_6]] -; CHECK-NEXT: [[DUMMY7_6:%.*]] = mul i32 [[DUMMY6_6]], [[DUMMY6_6]] -; CHECK-NEXT: [[DUMMY8_6:%.*]] = mul i32 [[DUMMY7_6]], [[DUMMY7_6]] -; CHECK-NEXT: [[DUMMY9_6:%.*]] = mul i32 [[DUMMY8_6]], [[DUMMY8_6]] -; CHECK-NEXT: [[DUMMY10_6:%.*]] = mul i32 [[DUMMY9_6]], [[DUMMY9_6]] -; CHECK-NEXT: [[DUMMY11_6:%.*]] = mul i32 [[DUMMY10_6]], [[DUMMY10_6]] -; CHECK-NEXT: [[DUMMY12_6:%.*]] = mul i32 [[DUMMY11_6]], [[DUMMY11_6]] -; CHECK-NEXT: [[DUMMY13_6:%.*]] = mul i32 [[DUMMY12_6]], [[DUMMY12_6]] -; CHECK-NEXT: [[DUMMY14_6:%.*]] = mul i32 [[DUMMY13_6]], [[DUMMY13_6]] -; CHECK-NEXT: [[DUMMY15_6:%.*]] = mul i32 [[DUMMY14_6]], [[DUMMY14_6]] -; CHECK-NEXT: [[DUMMY16_6:%.*]] = mul i32 [[DUMMY15_6]], [[DUMMY15_6]] -; CHECK-NEXT: [[DUMMY17_6:%.*]] = mul i32 [[DUMMY16_6]], [[DUMMY16_6]] -; CHECK-NEXT: [[DUMMY18_6:%.*]] = mul i32 [[DUMMY17_6]], [[DUMMY17_6]] -; CHECK-NEXT: [[DUMMY19_6:%.*]] = mul i32 [[DUMMY18_6]], [[DUMMY18_6]] -; CHECK-NEXT: [[DUMMY20_6:%.*]] = mul i32 [[DUMMY19_6]], [[DUMMY19_6]] -; CHECK-NEXT: [[DUMMY21_6:%.*]] = mul i32 [[DUMMY20_6]], [[DUMMY20_6]] -; CHECK-NEXT: [[DUMMY22_6:%.*]] = mul i32 [[DUMMY21_6]], [[DUMMY21_6]] -; CHECK-NEXT: [[DUMMY23_6:%.*]] = mul i32 [[DUMMY22_6]], [[DUMMY22_6]] -; CHECK-NEXT: [[DUMMY24_6:%.*]] = mul i32 [[DUMMY23_6]], [[DUMMY23_6]] -; CHECK-NEXT: [[DUMMY25_6:%.*]] = mul i32 [[DUMMY24_6]], [[DUMMY24_6]] -; CHECK-NEXT: [[DUMMY26_6:%.*]] = mul i32 [[DUMMY25_6]], [[DUMMY25_6]] -; CHECK-NEXT: [[DUMMY27_6:%.*]] = mul i32 [[DUMMY26_6]], [[DUMMY26_6]] -; CHECK-NEXT: [[DUMMY28_6:%.*]] = mul i32 [[DUMMY27_6]], [[DUMMY27_6]] -; CHECK-NEXT: [[DUMMY29_6:%.*]] = mul i32 [[DUMMY28_6]], [[DUMMY28_6]] -; CHECK-NEXT: [[DUMMY30_6:%.*]] = mul i32 [[DUMMY29_6]], [[DUMMY29_6]] -; CHECK-NEXT: [[DUMMY31_6:%.*]] = mul i32 [[DUMMY30_6]], [[DUMMY30_6]] -; CHECK-NEXT: [[DUMMY32_6:%.*]] = mul i32 [[DUMMY31_6]], [[DUMMY31_6]] -; CHECK-NEXT: [[DUMMY33_6:%.*]] = mul i32 [[DUMMY32_6]], [[DUMMY32_6]] -; CHECK-NEXT: [[DUMMY34_6:%.*]] = mul i32 [[DUMMY33_6]], [[DUMMY33_6]] -; CHECK-NEXT: [[DUMMY35_6:%.*]] = mul i32 [[DUMMY34_6]], [[DUMMY34_6]] -; CHECK-NEXT: [[DUMMY36_6:%.*]] = mul i32 [[DUMMY35_6]], [[DUMMY35_6]] -; CHECK-NEXT: [[DUMMY37_6:%.*]] = mul i32 [[DUMMY36_6]], [[DUMMY36_6]] -; CHECK-NEXT: [[DUMMY38_6:%.*]] = mul i32 [[DUMMY37_6]], [[DUMMY37_6]] -; CHECK-NEXT: [[DUMMY39_6:%.*]] = mul i32 [[DUMMY38_6]], [[DUMMY38_6]] -; CHECK-NEXT: [[DUMMY40_6:%.*]] = mul i32 [[DUMMY39_6]], [[DUMMY39_6]] -; CHECK-NEXT: [[DUMMY41_6:%.*]] = mul i32 [[DUMMY40_6]], [[DUMMY40_6]] -; CHECK-NEXT: [[DUMMY42_6:%.*]] = mul i32 [[DUMMY41_6]], [[DUMMY41_6]] -; CHECK-NEXT: [[DUMMY43_6:%.*]] = mul i32 [[DUMMY42_6]], [[DUMMY42_6]] -; CHECK-NEXT: [[DUMMY44_6:%.*]] = mul i32 [[DUMMY43_6]], [[DUMMY43_6]] -; CHECK-NEXT: [[DUMMY45_6:%.*]] = mul i32 [[DUMMY44_6]], [[DUMMY44_6]] -; CHECK-NEXT: [[DUMMY46_6:%.*]] = mul i32 [[DUMMY45_6]], [[DUMMY45_6]] -; CHECK-NEXT: [[DUMMY47_6:%.*]] = mul i32 [[DUMMY46_6]], [[DUMMY46_6]] -; CHECK-NEXT: [[DUMMY48_6:%.*]] = mul i32 [[DUMMY47_6]], [[DUMMY47_6]] -; CHECK-NEXT: [[DUMMY49_6:%.*]] = mul i32 [[DUMMY48_6]], [[DUMMY48_6]] -; CHECK-NEXT: [[DUMMY50_6:%.*]] = mul i32 [[DUMMY49_6]], [[DUMMY49_6]] -; CHECK-NEXT: [[SUM_NEXT_6:%.*]] = add nsw i32 [[DUMMY50_6]], [[SUM_NEXT_5]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_6:%.*]] = add nuw nsw i64 [[INDVARS_IV]], 7 -; CHECK-NEXT: [[ARRAYIDX_7:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_NEXT_6]] -; CHECK-NEXT: [[VAL_7:%.*]] = load i32, ptr [[ARRAYIDX_7]], align 4 -; CHECK-NEXT: [[DUMMY1_7:%.*]] = mul i32 [[VAL_7]], [[VAL_7]] -; CHECK-NEXT: [[DUMMY2_7:%.*]] = mul i32 [[DUMMY1_7]], [[DUMMY1_7]] -; CHECK-NEXT: [[DUMMY3_7:%.*]] = mul i32 [[DUMMY2_7]], [[DUMMY2_7]] -; CHECK-NEXT: [[DUMMY4_7:%.*]] = mul i32 [[DUMMY3_7]], [[DUMMY3_7]] -; CHECK-NEXT: [[DUMMY5_7:%.*]] = mul i32 [[DUMMY4_7]], [[DUMMY4_7]] -; CHECK-NEXT: [[DUMMY6_7:%.*]] = mul i32 [[DUMMY5_7]], [[DUMMY5_7]] -; CHECK-NEXT: [[DUMMY7_7:%.*]] = mul i32 [[DUMMY6_7]], [[DUMMY6_7]] -; CHECK-NEXT: [[DUMMY8_7:%.*]] = mul i32 [[DUMMY7_7]], [[DUMMY7_7]] -; CHECK-NEXT: [[DUMMY9_7:%.*]] = mul i32 [[DUMMY8_7]], [[DUMMY8_7]] -; CHECK-NEXT: [[DUMMY10_7:%.*]] = mul i32 [[DUMMY9_7]], [[DUMMY9_7]] -; CHECK-NEXT: [[DUMMY11_7:%.*]] = mul i32 [[DUMMY10_7]], [[DUMMY10_7]] -; CHECK-NEXT: [[DUMMY12_7:%.*]] = mul i32 [[DUMMY11_7]], [[DUMMY11_7]] -; CHECK-NEXT: [[DUMMY13_7:%.*]] = mul i32 [[DUMMY12_7]], [[DUMMY12_7]] -; CHECK-NEXT: [[DUMMY14_7:%.*]] = mul i32 [[DUMMY13_7]], [[DUMMY13_7]] -; CHECK-NEXT: [[DUMMY15_7:%.*]] = mul i32 [[DUMMY14_7]], [[DUMMY14_7]] -; CHECK-NEXT: [[DUMMY16_7:%.*]] = mul i32 [[DUMMY15_7]], [[DUMMY15_7]] -; CHECK-NEXT: [[DUMMY17_7:%.*]] = mul i32 [[DUMMY16_7]], [[DUMMY16_7]] -; CHECK-NEXT: [[DUMMY18_7:%.*]] = mul i32 [[DUMMY17_7]], [[DUMMY17_7]] -; CHECK-NEXT: [[DUMMY19_7:%.*]] = mul i32 [[DUMMY18_7]], [[DUMMY18_7]] -; CHECK-NEXT: [[DUMMY20_7:%.*]] = mul i32 [[DUMMY19_7]], [[DUMMY19_7]] -; CHECK-NEXT: [[DUMMY21_7:%.*]] = mul i32 [[DUMMY20_7]], [[DUMMY20_7]] -; CHECK-NEXT: [[DUMMY22_7:%.*]] = mul i32 [[DUMMY21_7]], [[DUMMY21_7]] -; CHECK-NEXT: [[DUMMY23_7:%.*]] = mul i32 [[DUMMY22_7]], [[DUMMY22_7]] -; CHECK-NEXT: [[DUMMY24_7:%.*]] = mul i32 [[DUMMY23_7]], [[DUMMY23_7]] -; CHECK-NEXT: [[DUMMY25_7:%.*]] = mul i32 [[DUMMY24_7]], [[DUMMY24_7]] -; CHECK-NEXT: [[DUMMY26_7:%.*]] = mul i32 [[DUMMY25_7]], [[DUMMY25_7]] -; CHECK-NEXT: [[DUMMY27_7:%.*]] = mul i32 [[DUMMY26_7]], [[DUMMY26_7]] -; CHECK-NEXT: [[DUMMY28_7:%.*]] = mul i32 [[DUMMY27_7]], [[DUMMY27_7]] -; CHECK-NEXT: [[DUMMY29_7:%.*]] = mul i32 [[DUMMY28_7]], [[DUMMY28_7]] -; CHECK-NEXT: [[DUMMY30_7:%.*]] = mul i32 [[DUMMY29_7]], [[DUMMY29_7]] -; CHECK-NEXT: [[DUMMY31_7:%.*]] = mul i32 [[DUMMY30_7]], [[DUMMY30_7]] -; CHECK-NEXT: [[DUMMY32_7:%.*]] = mul i32 [[DUMMY31_7]], [[DUMMY31_7]] -; CHECK-NEXT: [[DUMMY33_7:%.*]] = mul i32 [[DUMMY32_7]], [[DUMMY32_7]] -; CHECK-NEXT: [[DUMMY34_7:%.*]] = mul i32 [[DUMMY33_7]], [[DUMMY33_7]] -; CHECK-NEXT: [[DUMMY35_7:%.*]] = mul i32 [[DUMMY34_7]], [[DUMMY34_7]] -; CHECK-NEXT: [[DUMMY36_7:%.*]] = mul i32 [[DUMMY35_7]], [[DUMMY35_7]] -; CHECK-NEXT: [[DUMMY37_7:%.*]] = mul i32 [[DUMMY36_7]], [[DUMMY36_7]] -; CHECK-NEXT: [[DUMMY38_7:%.*]] = mul i32 [[DUMMY37_7]], [[DUMMY37_7]] -; CHECK-NEXT: [[DUMMY39_7:%.*]] = mul i32 [[DUMMY38_7]], [[DUMMY38_7]] -; CHECK-NEXT: [[DUMMY40_7:%.*]] = mul i32 [[DUMMY39_7]], [[DUMMY39_7]] -; CHECK-NEXT: [[DUMMY41_7:%.*]] = mul i32 [[DUMMY40_7]], [[DUMMY40_7]] -; CHECK-NEXT: [[DUMMY42_7:%.*]] = mul i32 [[DUMMY41_7]], [[DUMMY41_7]] -; CHECK-NEXT: [[DUMMY43_7:%.*]] = mul i32 [[DUMMY42_7]], [[DUMMY42_7]] -; CHECK-NEXT: [[DUMMY44_7:%.*]] = mul i32 [[DUMMY43_7]], [[DUMMY43_7]] -; CHECK-NEXT: [[DUMMY45_7:%.*]] = mul i32 [[DUMMY44_7]], [[DUMMY44_7]] -; CHECK-NEXT: [[DUMMY46_7:%.*]] = mul i32 [[DUMMY45_7]], [[DUMMY45_7]] -; CHECK-NEXT: [[DUMMY47_7:%.*]] = mul i32 [[DUMMY46_7]], [[DUMMY46_7]] -; CHECK-NEXT: [[DUMMY48_7:%.*]] = mul i32 [[DUMMY47_7]], [[DUMMY47_7]] -; CHECK-NEXT: [[DUMMY49_7:%.*]] = mul i32 [[DUMMY48_7]], [[DUMMY48_7]] -; CHECK-NEXT: [[DUMMY50_7:%.*]] = mul i32 [[DUMMY49_7]], [[DUMMY49_7]] -; CHECK-NEXT: [[SUM_NEXT_7]] = add nsw i32 [[DUMMY50_7]], [[SUM_NEXT_6]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_7]] = add nuw nsw i64 [[INDVARS_IV]], 8 -; CHECK-NEXT: [[NITER_NEXT_7]] = add i64 [[NITER]], 8 -; CHECK-NEXT: [[NITER_NCMP_7:%.*]] = icmp eq i64 [[NITER_NEXT_7]], [[UNROLL_ITER]] -; CHECK-NEXT: br i1 [[NITER_NCMP_7]], label [[FOR_COND_CLEANUP_UNR_LCSSA_LOOPEXIT:%.*]], label [[FOR_BODY]] +; CHECK-NEXT: [[SUM_NEXT_1]] = add nsw i32 [[DUMMY50_1]], [[SUM_NEXT]] +; CHECK-NEXT: [[INDVARS_IV_NEXT_1]] = add nuw nsw i64 [[INDVARS_IV]], 2 +; CHECK-NEXT: [[NITER_NEXT_1]] = add i64 [[NITER]], 2 +; CHECK-NEXT: [[NITER_NCMP_1:%.*]] = icmp eq i64 [[NITER_NEXT_1]], [[UNROLL_ITER]] +; CHECK-NEXT: br i1 [[NITER_NCMP_1]], label [[FOR_COND_CLEANUP_UNR_LCSSA_LOOPEXIT:%.*]], label [[FOR_BODY]] ; CHECK: for.cond.cleanup.unr-lcssa.loopexit: -; CHECK-NEXT: [[SUM_NEXT_LCSSA_PH_PH:%.*]] = phi i32 [ [[SUM_NEXT_7]], [[FOR_BODY]] ] -; CHECK-NEXT: [[INDVARS_IV_UNR_PH:%.*]] = phi i64 [ [[INDVARS_IV_NEXT_7]], [[FOR_BODY]] ] -; CHECK-NEXT: [[SUM_UNR_PH:%.*]] = phi i32 [ [[SUM_NEXT_7]], [[FOR_BODY]] ] +; CHECK-NEXT: [[SUM_NEXT_LCSSA_PH_PH:%.*]] = phi i32 [ [[SUM_NEXT_1]], [[FOR_BODY]] ] +; CHECK-NEXT: [[INDVARS_IV_UNR_PH:%.*]] = phi i64 [ [[INDVARS_IV_NEXT_1]], [[FOR_BODY]] ] +; CHECK-NEXT: [[SUM_UNR_PH:%.*]] = phi i32 [ [[SUM_NEXT_1]], [[FOR_BODY]] ] ; CHECK-NEXT: br label [[FOR_COND_CLEANUP_UNR_LCSSA]] ; CHECK: for.cond.cleanup.unr-lcssa: ; CHECK-NEXT: [[SUM_NEXT_LCSSA_PH:%.*]] = phi i32 [ undef, [[ENTRY:%.*]] ], [ [[SUM_NEXT_LCSSA_PH_PH]], [[FOR_COND_CLEANUP_UNR_LCSSA_LOOPEXIT]] ] @@ -1010,10 +302,7 @@ define i32 @test2(ptr %ary, i64 %n) "target-cpu"="znver3" { ; CHECK: for.body.epil.preheader: ; CHECK-NEXT: br label [[FOR_BODY_EPIL:%.*]] ; CHECK: for.body.epil: -; CHECK-NEXT: [[INDVARS_IV_EPIL:%.*]] = phi i64 [ [[INDVARS_IV_UNR]], [[FOR_BODY_EPIL_PREHEADER]] ], [ [[INDVARS_IV_NEXT_EPIL:%.*]], [[FOR_BODY_EPIL]] ] -; CHECK-NEXT: [[SUM_EPIL:%.*]] = phi i32 [ [[SUM_UNR]], [[FOR_BODY_EPIL_PREHEADER]] ], [ [[SUM_NEXT_EPIL:%.*]], [[FOR_BODY_EPIL]] ] -; CHECK-NEXT: [[EPIL_ITER:%.*]] = phi i64 [ 0, [[FOR_BODY_EPIL_PREHEADER]] ], [ [[EPIL_ITER_NEXT:%.*]], [[FOR_BODY_EPIL]] ] -; CHECK-NEXT: [[ARRAYIDX_EPIL:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_EPIL]] +; CHECK-NEXT: [[ARRAYIDX_EPIL:%.*]] = getelementptr inbounds i32, ptr [[ARY]], i64 [[INDVARS_IV_UNR]] ; CHECK-NEXT: [[VAL_EPIL:%.*]] = load i32, ptr [[ARRAYIDX_EPIL]], align 4 ; CHECK-NEXT: [[DUMMY1_EPIL:%.*]] = mul i32 [[VAL_EPIL]], [[VAL_EPIL]] ; CHECK-NEXT: [[DUMMY2_EPIL:%.*]] = mul i32 [[DUMMY1_EPIL]], [[DUMMY1_EPIL]] @@ -1065,17 +354,10 @@ define i32 @test2(ptr %ary, i64 %n) "target-cpu"="znver3" { ; CHECK-NEXT: [[DUMMY48_EPIL:%.*]] = mul i32 [[DUMMY47_EPIL]], [[DUMMY47_EPIL]] ; CHECK-NEXT: [[DUMMY49_EPIL:%.*]] = mul i32 [[DUMMY48_EPIL]], [[DUMMY48_EPIL]] ; CHECK-NEXT: [[DUMMY50_EPIL:%.*]] = mul i32 [[DUMMY49_EPIL]], [[DUMMY49_EPIL]] -; CHECK-NEXT: [[SUM_NEXT_EPIL]] = add nsw i32 [[DUMMY50_EPIL]], [[SUM_EPIL]] -; CHECK-NEXT: [[INDVARS_IV_NEXT_EPIL]] = add nuw nsw i64 [[INDVARS_IV_EPIL]], 1 -; CHECK-NEXT: [[EXITCOND_NOT_EPIL:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT_EPIL]], [[N]] -; CHECK-NEXT: [[EPIL_ITER_NEXT]] = add i64 [[EPIL_ITER]], 1 -; CHECK-NEXT: [[EPIL_ITER_CMP:%.*]] = icmp ne i64 [[EPIL_ITER_NEXT]], [[XTRAITER]] -; CHECK-NEXT: br i1 [[EPIL_ITER_CMP]], label [[FOR_BODY_EPIL]], label [[FOR_COND_CLEANUP_EPILOG_LCSSA:%.*]], !llvm.loop [[LOOP0:![0-9]+]] -; CHECK: for.cond.cleanup.epilog-lcssa: -; CHECK-NEXT: [[SUM_NEXT_LCSSA_PH1:%.*]] = phi i32 [ [[SUM_NEXT_EPIL]], [[FOR_BODY_EPIL]] ] +; CHECK-NEXT: [[SUM_NEXT_EPIL:%.*]] = add nsw i32 [[DUMMY50_EPIL]], [[SUM_UNR]] ; CHECK-NEXT: br label [[FOR_COND_CLEANUP]] ; CHECK: for.cond.cleanup: -; CHECK-NEXT: [[SUM_NEXT_LCSSA:%.*]] = phi i32 [ [[SUM_NEXT_LCSSA_PH]], [[FOR_COND_CLEANUP_UNR_LCSSA]] ], [ [[SUM_NEXT_LCSSA_PH1]], [[FOR_COND_CLEANUP_EPILOG_LCSSA]] ] +; CHECK-NEXT: [[SUM_NEXT_LCSSA:%.*]] = phi i32 [ [[SUM_NEXT_LCSSA_PH]], [[FOR_COND_CLEANUP_UNR_LCSSA]] ], [ [[SUM_NEXT_EPIL]], [[FOR_BODY_EPIL]] ] ; CHECK-NEXT: ret i32 [[SUM_NEXT_LCSSA]] ; entry: -- GitLab From f60c699d37c41c46dd0be4ec98e5b4d74e73b2b7 Mon Sep 17 00:00:00 2001 From: Joseph Huber Date: Wed, 15 May 2024 20:44:02 -0500 Subject: [PATCH 055/403] [OpenMP] Fix intermediate header locations for OpenMP Summary: A previous patch moved the code here and accidentally overrwrote the include path that the LSP interface used. This caused incorrect errors when using clangd with the offload project. This patch removes the unnecessary header and makes sure we include the correct folder. --- openmp/runtime/src/CMakeLists.txt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/openmp/runtime/src/CMakeLists.txt b/openmp/runtime/src/CMakeLists.txt index a2468d04e60a..94eeea63b804 100644 --- a/openmp/runtime/src/CMakeLists.txt +++ b/openmp/runtime/src/CMakeLists.txt @@ -20,7 +20,7 @@ endif() # Configure omp.h, kmp_config.h and omp-tools.h if necessary configure_file(${LIBOMP_INC_DIR}/omp.h.var ${LIBOMP_HEADERS_INTDIR}/omp.h @ONLY) configure_file(${LIBOMP_INC_DIR}/ompx.h.var ${LIBOMP_HEADERS_INTDIR}/ompx.h @ONLY) -configure_file(kmp_config.h.cmake ${LIBOMP_HEADERS_INTDIR}/kmp_config.h @ONLY) +configure_file(kmp_config.h.cmake kmp_config.h @ONLY) if(${LIBOMP_OMPT_SUPPORT}) configure_file(${LIBOMP_INC_DIR}/omp-tools.h.var ${LIBOMP_HEADERS_INTDIR}/omp-tools.h @ONLY) endif() @@ -55,7 +55,6 @@ include_directories( ${LIBOMP_SRC_DIR}/i18n ${LIBOMP_INC_DIR} ${LIBOMP_SRC_DIR}/thirdparty/ittnotify - ${LIBOMP_HEADERS_INTDIR} ) # Building with time profiling support requires LLVM directory includes. @@ -441,7 +440,7 @@ if(${LIBOMP_OMPT_SUPPORT}) install(FILES ${LIBOMP_HEADERS_INTDIR}/omp-tools.h DESTINATION ${LIBOMP_HEADERS_INSTALL_PATH}) # install under legacy name ompt.h install(FILES ${LIBOMP_HEADERS_INTDIR}/omp-tools.h DESTINATION ${LIBOMP_HEADERS_INSTALL_PATH} RENAME ompt.h) - set(LIBOMP_OMP_TOOLS_INCLUDE_DIR ${LIBOMP_HEADERS_INTDIR} PARENT_SCOPE) + set(LIBOMP_OMP_TOOLS_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) endif() if(${BUILD_FORTRAN_MODULES}) set (destination ${LIBOMP_HEADERS_INSTALL_PATH}) -- GitLab From 1595988ee6f9732e7ea79928af8a470ad5ef7dbe Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 15 May 2024 21:52:59 -0400 Subject: [PATCH 056/403] Reapply "[Clang][Sema] Earlier type checking for builtin unary operators (#90500)" (#92283) This patch reapplies #90500, addressing a bug which caused binary operators with dependent operands to be incorrectly rebuilt by `TreeTransform`. --- clang/docs/ReleaseNotes.rst | 3 + clang/include/clang/AST/Type.h | 5 +- clang/lib/Sema/SemaExpr.cpp | 363 +++++++++--------- clang/lib/Sema/TreeTransform.h | 17 +- clang/test/AST/ast-dump-expr-json.cpp | 4 +- clang/test/AST/ast-dump-expr.cpp | 2 +- clang/test/AST/ast-dump-lambda.cpp | 2 +- .../expr/expr.unary/expr.unary.general/p1.cpp | 65 ++++ clang/test/CXX/over/over.built/ast.cpp | 158 ++++++-- clang/test/CXX/over/over.built/p10.cpp | 2 +- clang/test/CXX/over/over.built/p11.cpp | 2 +- .../over/over.oper/over.oper.general/p1.cpp | 173 +++++++++ .../temp.res/temp.dep/temp.dep.type/p4.cpp | 25 +- clang/test/Frontend/noderef_templates.cpp | 4 +- clang/test/SemaCXX/cxx2b-deducing-this.cpp | 6 +- .../test/SemaTemplate/class-template-spec.cpp | 12 +- .../ASTMatchers/ASTMatchersNarrowingTest.cpp | 6 +- 17 files changed, 586 insertions(+), 263 deletions(-) create mode 100644 clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp create mode 100644 clang/test/CXX/over/over.oper/over.oper.general/p1.cpp diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 089a85c8cb36..11812c355f8d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -56,6 +56,9 @@ C++ Specific Potentially Breaking Changes - Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906). +- Clang now performs semantic analysis for unary operators with dependent operands + that are known to be of non-class non-enumeration type prior to instantiation. + ABI Changes in This Version --------------------------- - Fixed Microsoft name mangling of implicitly defined variables used for thread diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index e6643469e0b3..da3834f19ca0 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -8044,7 +8044,10 @@ inline bool Type::isUndeducedType() const { /// Determines whether this is a type for which one can define /// an overloaded operator. inline bool Type::isOverloadableType() const { - return isDependentType() || isRecordType() || isEnumeralType(); + if (!CanonicalType->isDependentType()) + return isRecordType() || isEnumeralType(); + return !isArrayType() && !isFunctionType() && !isAnyPointerType() && + !isMemberPointerType(); } /// Determines whether this type is written as a typedef-name. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ec84798e4ce6..50569c1cd536 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -672,12 +672,12 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) { // We don't want to throw lvalue-to-rvalue casts on top of // expressions of certain types in C++. - if (getLangOpts().CPlusPlus && - (E->getType() == Context.OverloadTy || - // FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied - // to pointer types even if the pointee type is dependent. - (T->isDependentType() && !T->isPointerType()) || T->isRecordType())) - return E; + if (getLangOpts().CPlusPlus) { + if (T == Context.OverloadTy || T->isRecordType() || + (T->isDependentType() && !T->isAnyPointerType() && + !T->isMemberPointerType())) + return E; + } // The C standard is actually really unclear on this point, and // DR106 tells us what the result should be but not why. It's @@ -10827,7 +10827,7 @@ static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc, if (const AtomicType *ResAtomicType = ResType->getAs()) ResType = ResAtomicType->getValueType(); - assert(ResType->isAnyPointerType() && !ResType->isDependentType()); + assert(ResType->isAnyPointerType()); QualType PointeeTy = ResType->getPointeeType(); return S.RequireCompleteSizedType( Loc, PointeeTy, @@ -13957,9 +13957,6 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, ExprObjectKind &OK, SourceLocation OpLoc, bool IsInc, bool IsPrefix) { - if (Op->isTypeDependent()) - return S.Context.DependentTy; - QualType ResType = Op->getType(); // Atomic types can be used for increment / decrement where the non-atomic // versions can, so ignore the _Atomic() specifier for the purpose of @@ -14410,9 +14407,6 @@ static void RecordModifiableNonNullParam(Sema &S, const Expr *Exp) { static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, SourceLocation OpLoc, bool IsAfterAmp = false) { - if (Op->isTypeDependent()) - return S.Context.DependentTy; - ExprResult ConvResult = S.UsualUnaryConversions(Op); if (ConvResult.isInvalid()) return QualType(); @@ -15368,14 +15362,10 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, } if (getLangOpts().CPlusPlus) { - // If either expression is type-dependent, always build an - // overloaded op. - if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent()) - return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); - - // Otherwise, build an overloaded op if either expression has an - // overloadable type. - if (LHSExpr->getType()->isOverloadableType() || + // Otherwise, build an overloaded op if either expression is type-dependent + // or has an overloadable type. + if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() || + LHSExpr->getType()->isOverloadableType() || RHSExpr->getType()->isOverloadableType()) return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr); } @@ -15466,190 +15456,191 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 1); } - switch (Opc) { - case UO_PreInc: - case UO_PreDec: - case UO_PostInc: - case UO_PostDec: - resultType = - CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc, - Opc == UO_PreInc || Opc == UO_PostInc, - Opc == UO_PreInc || Opc == UO_PreDec); - CanOverflow = isOverflowingIntegerType(Context, resultType); - break; - case UO_AddrOf: - resultType = CheckAddressOfOperand(Input, OpLoc); - CheckAddressOfNoDeref(InputExpr); - RecordModifiableNonNullParam(*this, InputExpr); - break; - case UO_Deref: { - Input = DefaultFunctionArrayLvalueConversion(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = - CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp); - break; - } - case UO_Plus: - case UO_Minus: - CanOverflow = Opc == UO_Minus && - isOverflowingIntegerType(Context, Input.get()->getType()); - Input = UsualUnaryConversions(Input.get()); - if (Input.isInvalid()) - return ExprError(); - // Unary plus and minus require promoting an operand of half vector to a - // float vector and truncating the result back to a half vector. For now, we - // do this only when HalfArgsAndReturns is set (that is, when the target is - // arm or arm64). - ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get()); - - // If the operand is a half vector, promote it to a float vector. - if (ConvertHalfVec) - Input = convertVector(Input.get(), Context.FloatTy, *this); - resultType = Input.get()->getType(); - if (resultType->isDependentType()) - break; - if (resultType->isArithmeticType()) // C99 6.5.3.3p1 - break; - else if (resultType->isVectorType() && - // The z vector extensions don't allow + or - with bool vectors. - (!Context.getLangOpts().ZVector || - resultType->castAs()->getVectorKind() != - VectorKind::AltiVecBool)) - break; - else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and - - break; - else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6 - Opc == UO_Plus && resultType->isPointerType()) + if (InputExpr->isTypeDependent() && + InputExpr->getType()->isSpecificBuiltinType(BuiltinType::Dependent)) { + resultType = Context.DependentTy; + } else { + switch (Opc) { + case UO_PreInc: + case UO_PreDec: + case UO_PostInc: + case UO_PostDec: + resultType = + CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc, + Opc == UO_PreInc || Opc == UO_PostInc, + Opc == UO_PreInc || Opc == UO_PreDec); + CanOverflow = isOverflowingIntegerType(Context, resultType); break; - - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - - case UO_Not: // bitwise complement - Input = UsualUnaryConversions(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = Input.get()->getType(); - if (resultType->isDependentType()) + case UO_AddrOf: + resultType = CheckAddressOfOperand(Input, OpLoc); + CheckAddressOfNoDeref(InputExpr); + RecordModifiableNonNullParam(*this, InputExpr); break; - // C99 6.5.3.3p1. We allow complex int and float as a GCC extension. - if (resultType->isComplexType() || resultType->isComplexIntegerType()) - // C99 does not support '~' for complex conjugation. - Diag(OpLoc, diag::ext_integer_complement_complex) - << resultType << Input.get()->getSourceRange(); - else if (resultType->hasIntegerRepresentation()) + case UO_Deref: { + Input = DefaultFunctionArrayLvalueConversion(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = + CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp); break; - else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) { - // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate - // on vector float types. - QualType T = resultType->castAs()->getElementType(); - if (!T->isIntegerType()) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } else { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - break; - - case UO_LNot: // logical negation - // Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5). - Input = DefaultFunctionArrayLvalueConversion(Input.get()); - if (Input.isInvalid()) - return ExprError(); - resultType = Input.get()->getType(); - - // Though we still have to promote half FP to float... - if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) { - Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast) - .get(); - resultType = Context.FloatTy; } + case UO_Plus: + case UO_Minus: + CanOverflow = Opc == UO_Minus && + isOverflowingIntegerType(Context, Input.get()->getType()); + Input = UsualUnaryConversions(Input.get()); + if (Input.isInvalid()) + return ExprError(); + // Unary plus and minus require promoting an operand of half vector to a + // float vector and truncating the result back to a half vector. For now, + // we do this only when HalfArgsAndReturns is set (that is, when the + // target is arm or arm64). + ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get()); + + // If the operand is a half vector, promote it to a float vector. + if (ConvertHalfVec) + Input = convertVector(Input.get(), Context.FloatTy, *this); + resultType = Input.get()->getType(); + if (resultType->isArithmeticType()) // C99 6.5.3.3p1 + break; + else if (resultType->isVectorType() && + // The z vector extensions don't allow + or - with bool vectors. + (!Context.getLangOpts().ZVector || + resultType->castAs()->getVectorKind() != + VectorKind::AltiVecBool)) + break; + else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and - + break; + else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6 + Opc == UO_Plus && resultType->isPointerType()) + break; - // WebAsembly tables can't be used in unary expressions. - if (resultType->isPointerType() && - resultType->getPointeeType().isWebAssemblyReferenceType()) { return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); - } - if (resultType->isDependentType()) - break; - if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { - // C99 6.5.3.3p1: ok, fallthrough; - if (Context.getLangOpts().CPlusPlus) { - // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9: - // operand contextually converted to bool. - Input = ImpCastExprToType(Input.get(), Context.BoolTy, - ScalarTypeToBooleanCastKind(resultType)); - } else if (Context.getLangOpts().OpenCL && - Context.getLangOpts().OpenCLVersion < 120) { - // OpenCL v1.1 6.3.h: The logical operator not (!) does not - // operate on scalar float types. - if (!resultType->isIntegerType() && !resultType->isPointerType()) - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } - } else if (resultType->isExtVectorType()) { - if (Context.getLangOpts().OpenCL && - Context.getLangOpts().getOpenCLCompatibleVersion() < 120) { - // OpenCL v1.1 6.3.h: The logical operator not (!) does not - // operate on vector float types. + case UO_Not: // bitwise complement + Input = UsualUnaryConversions(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = Input.get()->getType(); + // C99 6.5.3.3p1. We allow complex int and float as a GCC extension. + if (resultType->isComplexType() || resultType->isComplexIntegerType()) + // C99 does not support '~' for complex conjugation. + Diag(OpLoc, diag::ext_integer_complement_complex) + << resultType << Input.get()->getSourceRange(); + else if (resultType->hasIntegerRepresentation()) + break; + else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) { + // OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate + // on vector float types. QualType T = resultType->castAs()->getElementType(); if (!T->isIntegerType()) return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); + } else { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); } - // Vector logical not returns the signed variant of the operand type. - resultType = GetSignedVectorType(resultType); break; - } else if (Context.getLangOpts().CPlusPlus && resultType->isVectorType()) { - const VectorType *VTy = resultType->castAs(); - if (VTy->getVectorKind() != VectorKind::Generic) + + case UO_LNot: // logical negation + // Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5). + Input = DefaultFunctionArrayLvalueConversion(Input.get()); + if (Input.isInvalid()) + return ExprError(); + resultType = Input.get()->getType(); + + // Though we still have to promote half FP to float... + if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) { + Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast) + .get(); + resultType = Context.FloatTy; + } + + // WebAsembly tables can't be used in unary expressions. + if (resultType->isPointerType() && + resultType->getPointeeType().isWebAssemblyReferenceType()) { return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) << resultType << Input.get()->getSourceRange()); + } - // Vector logical not returns the signed variant of the operand type. - resultType = GetSignedVectorType(resultType); - break; - } else { - return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) - << resultType << Input.get()->getSourceRange()); - } + if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { + // C99 6.5.3.3p1: ok, fallthrough; + if (Context.getLangOpts().CPlusPlus) { + // C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9: + // operand contextually converted to bool. + Input = ImpCastExprToType(Input.get(), Context.BoolTy, + ScalarTypeToBooleanCastKind(resultType)); + } else if (Context.getLangOpts().OpenCL && + Context.getLangOpts().OpenCLVersion < 120) { + // OpenCL v1.1 6.3.h: The logical operator not (!) does not + // operate on scalar float types. + if (!resultType->isIntegerType() && !resultType->isPointerType()) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + } else if (resultType->isExtVectorType()) { + if (Context.getLangOpts().OpenCL && + Context.getLangOpts().getOpenCLCompatibleVersion() < 120) { + // OpenCL v1.1 6.3.h: The logical operator not (!) does not + // operate on vector float types. + QualType T = resultType->castAs()->getElementType(); + if (!T->isIntegerType()) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + // Vector logical not returns the signed variant of the operand type. + resultType = GetSignedVectorType(resultType); + break; + } else if (Context.getLangOpts().CPlusPlus && + resultType->isVectorType()) { + const VectorType *VTy = resultType->castAs(); + if (VTy->getVectorKind() != VectorKind::Generic) + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); - // LNot always has type int. C99 6.5.3.3p5. - // In C++, it's bool. C++ 5.3.1p8 - resultType = Context.getLogicalOperationType(); - break; - case UO_Real: - case UO_Imag: - resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real); - // _Real maps ordinary l-values into ordinary l-values. _Imag maps ordinary - // complex l-values to ordinary l-values and all other values to r-values. - if (Input.isInvalid()) - return ExprError(); - if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) { - if (Input.get()->isGLValue() && - Input.get()->getObjectKind() == OK_Ordinary) - VK = Input.get()->getValueKind(); - } else if (!getLangOpts().CPlusPlus) { - // In C, a volatile scalar is read by __imag. In C++, it is not. - Input = DefaultLvalueConversion(Input.get()); + // Vector logical not returns the signed variant of the operand type. + resultType = GetSignedVectorType(resultType); + break; + } else { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + + // LNot always has type int. C99 6.5.3.3p5. + // In C++, it's bool. C++ 5.3.1p8 + resultType = Context.getLogicalOperationType(); + break; + case UO_Real: + case UO_Imag: + resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real); + // _Real maps ordinary l-values into ordinary l-values. _Imag maps + // ordinary complex l-values to ordinary l-values and all other values to + // r-values. + if (Input.isInvalid()) + return ExprError(); + if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) { + if (Input.get()->isGLValue() && + Input.get()->getObjectKind() == OK_Ordinary) + VK = Input.get()->getValueKind(); + } else if (!getLangOpts().CPlusPlus) { + // In C, a volatile scalar is read by __imag. In C++, it is not. + Input = DefaultLvalueConversion(Input.get()); + } + break; + case UO_Extension: + resultType = Input.get()->getType(); + VK = Input.get()->getValueKind(); + OK = Input.get()->getObjectKind(); + break; + case UO_Coawait: + // It's unnecessary to represent the pass-through operator co_await in the + // AST; just return the input expression instead. + assert(!Input.get()->getType()->isDependentType() && + "the co_await expression must be non-dependant before " + "building operator co_await"); + return Input; } - break; - case UO_Extension: - resultType = Input.get()->getType(); - VK = Input.get()->getValueKind(); - OK = Input.get()->getObjectKind(); - break; - case UO_Coawait: - // It's unnecessary to represent the pass-through operator co_await in the - // AST; just return the input expression instead. - assert(!Input.get()->getType()->isDependentType() && - "the co_await expression must be non-dependant before " - "building operator co_await"); - return Input; } if (resultType.isNull() || Input.isInvalid()) return ExprError(); diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index c039b95293af..b10e5ba65eb1 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -16236,10 +16236,11 @@ ExprResult TreeTransform::RebuildCXXOperatorCallExpr( return getSema().CreateBuiltinUnaryOp(OpLoc, Opc, First); } } else { - if (!First->getType()->isOverloadableType() && + if (!First->isTypeDependent() && !Second->isTypeDependent() && + !First->getType()->isOverloadableType() && !Second->getType()->isOverloadableType()) { - // Neither of the arguments is an overloadable type, so try to - // create a built-in binary operation. + // Neither of the arguments is type-dependent or has an overloadable + // type, so try to create a built-in binary operation. BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op); ExprResult Result = SemaRef.CreateBuiltinBinOp(OpLoc, Opc, First, Second); @@ -16250,12 +16251,8 @@ ExprResult TreeTransform::RebuildCXXOperatorCallExpr( } } - // Add any functions found via argument-dependent lookup. - Expr *Args[2] = { First, Second }; - unsigned NumArgs = 1 + (Second != nullptr); - // Create the overloaded operator invocation for unary operators. - if (NumArgs == 1 || isPostIncDec) { + if (!Second || isPostIncDec) { UnaryOperatorKind Opc = UnaryOperator::getOverloadedOpcode(Op, isPostIncDec); return SemaRef.CreateOverloadedUnaryOp(OpLoc, Opc, Functions, First, @@ -16264,8 +16261,8 @@ ExprResult TreeTransform::RebuildCXXOperatorCallExpr( // Create the overloaded operator invocation for binary operators. BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op); - ExprResult Result = SemaRef.CreateOverloadedBinOp( - OpLoc, Opc, Functions, Args[0], Args[1], RequiresADL); + ExprResult Result = SemaRef.CreateOverloadedBinOp(OpLoc, Opc, Functions, + First, Second, RequiresADL); if (Result.isInvalid()) return ExprError(); diff --git a/clang/test/AST/ast-dump-expr-json.cpp b/clang/test/AST/ast-dump-expr-json.cpp index 0fb07b0b434c..4b7365e554cb 100644 --- a/clang/test/AST/ast-dump-expr-json.cpp +++ b/clang/test/AST/ast-dump-expr-json.cpp @@ -4261,9 +4261,9 @@ void TestNonADLCall3() { // CHECK-NEXT: } // CHECK-NEXT: }, // CHECK-NEXT: "type": { -// CHECK-NEXT: "qualType": "" +// CHECK-NEXT: "qualType": "V" // CHECK-NEXT: }, -// CHECK-NEXT: "valueCategory": "prvalue", +// CHECK-NEXT: "valueCategory": "lvalue", // CHECK-NEXT: "isPostfix": false, // CHECK-NEXT: "opcode": "*", // CHECK-NEXT: "canOverflow": false, diff --git a/clang/test/AST/ast-dump-expr.cpp b/clang/test/AST/ast-dump-expr.cpp index 69e65e22d61d..4df5ba4276ab 100644 --- a/clang/test/AST/ast-dump-expr.cpp +++ b/clang/test/AST/ast-dump-expr.cpp @@ -282,7 +282,7 @@ void PrimaryExpressions(Ts... a) { // CHECK-NEXT: CompoundStmt // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} col:8 implicit 'V' // CHECK-NEXT: ParenListExpr 0x{{[^ ]*}} 'NULL TYPE' - // CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} '' prefix '*' cannot overflow + // CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} 'V' lvalue prefix '*' cannot overflow // CHECK-NEXT: CXXThisExpr 0x{{[^ ]*}} 'V *' this } }; diff --git a/clang/test/AST/ast-dump-lambda.cpp b/clang/test/AST/ast-dump-lambda.cpp index ef8789cd97d3..a4d3fe4fbda5 100644 --- a/clang/test/AST/ast-dump-lambda.cpp +++ b/clang/test/AST/ast-dump-lambda.cpp @@ -81,7 +81,7 @@ template void test(Ts... a) { // CHECK-NEXT: | | | `-CompoundStmt {{.*}} // CHECK-NEXT: | | `-FieldDecl {{.*}} col:8{{( imported)?}} implicit 'V' // CHECK-NEXT: | |-ParenListExpr {{.*}} 'NULL TYPE' -// CHECK-NEXT: | | `-UnaryOperator {{.*}} '' prefix '*' cannot overflow +// CHECK-NEXT: | | `-UnaryOperator {{.*}} 'V' lvalue prefix '*' cannot overflow // CHECK-NEXT: | | `-CXXThisExpr {{.*}} 'V *' this // CHECK-NEXT: | `-CompoundStmt {{.*}} // CHECK-NEXT: |-DeclStmt {{.*}} diff --git a/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp b/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp new file mode 100644 index 000000000000..6744ce1cad17 --- /dev/null +++ b/clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -Wno-unused -fsyntax-only %s -verify + +struct A { + void operator*(); + void operator+(); + void operator-(); + void operator!(); + void operator~(); + void operator&(); + void operator++(); + void operator--(); +}; + +struct B { }; + +template +void dependent(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) { + *t; + +t; + -t; + !t; + ~t; + &t; + ++t; + --t; + + *pt; + +pt; + -pt; // expected-error {{invalid argument type 'T *' to unary expression}} + !pt; + ~pt; // expected-error {{invalid argument type 'T *' to unary expression}} + &pt; + ++pt; + --pt; + + *mpt; // expected-error {{indirection requires pointer operand ('T U::*' invalid)}} + +mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}} + -mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}} + !mpt; + ~mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}} + &mpt; + ++mpt; // expected-error {{cannot increment value of type 'T U::*'}} + --mpt; // expected-error {{cannot decrement value of type 'T U::*'}} + + *ft; + +ft; + -ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}} + !ft; + ~ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}} + &ft; + ++ft; // expected-error {{cannot increment value of type 'T ()'}} + --ft; // expected-error {{cannot decrement value of type 'T ()'}} + + *at; + +at; + -at; // expected-error {{invalid argument type 'T *' to unary expression}} + !at; + ~at; // expected-error {{invalid argument type 'T *' to unary expression}} + &at; + ++at; // expected-error {{cannot increment value of type 'T[4]'}} + --at; // expected-error {{cannot decrement value of type 'T[4]'}} +} + +// Make sure we only emit diagnostics once. +template void dependent(A t, A* pt, A B::* mpt, A(&ft)(), A(&at)[4]); diff --git a/clang/test/CXX/over/over.built/ast.cpp b/clang/test/CXX/over/over.built/ast.cpp index 56a63431269f..78f86edb1e96 100644 --- a/clang/test/CXX/over/over.built/ast.cpp +++ b/clang/test/CXX/over/over.built/ast.cpp @@ -1,41 +1,139 @@ -// RUN: %clang_cc1 -std=c++17 -ast-dump %s -ast-dump-filter Test | FileCheck %s +// RUN: %clang_cc1 -std=c++17 -Wno-unused -ast-dump %s -ast-dump-filter Test | FileCheck %s -struct A{}; +namespace Test { + template + void Unary(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) { + // CHECK: UnaryOperator {{.*}} '' lvalue prefix '*' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + *t; -template -auto Test(T* pt, U* pu) { - // CHECK: UnaryOperator {{.*}} '' lvalue prefix '*' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - (void)*pt; + // CHECK: UnaryOperator {{.*}} '' prefix '+' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + +t; - // CHECK: UnaryOperator {{.*}} '' lvalue prefix '++' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - (void)(++pt); + // CHECK: UnaryOperator {{.*}} '' prefix '-' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + -t; - // CHECK: UnaryOperator {{.*}} '' prefix '+' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - (void)(+pt); + // CHECK: UnaryOperator {{.*}} '' prefix '!' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + !t; - // CHECK: BinaryOperator {{.*}} '' '+' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 - (void)(pt + 3); + // CHECK: UnaryOperator {{.*}} '' prefix '~' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + ~t; - // CHECK: BinaryOperator {{.*}} '' '-' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - (void)(pt - pt); + // CHECK: UnaryOperator {{.*}} '' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + &t; - // CHECK: BinaryOperator {{.*}} '' '-' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - // CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *' - (void)(pt - pu); + // CHECK: UnaryOperator {{.*}} '' lvalue prefix '++' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + ++t; - // CHECK: BinaryOperator {{.*}} '' '==' - // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' - // CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *' - (void)(pt == pu); + // CHECK: UnaryOperator {{.*}} '' lvalue prefix '--' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T' + --t; -} + // CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + *pt; + // CHECK: UnaryOperator {{.*}} 'T *' prefix '+' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + +pt; + // CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + !pt; + + // CHECK: UnaryOperator {{.*}} '' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + &pt; + + // CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '++' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + ++pt; + + // CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '--' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + --pt; + + // CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T U::*' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T U::*' lvalue ParmVar {{.*}} 'mpt' 'T U::*' + !mpt; + + // CHECK: UnaryOperator {{.*}} '' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T U::*' lvalue ParmVar {{.*}} 'mpt' 'T U::*' + &mpt; + + // CHECK: UnaryOperator {{.*}} 'T ()' lvalue prefix '*' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()' + *ft; + + // CHECK: UnaryOperator {{.*}} 'T (*)()' prefix '+' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()' + +ft; + + // CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()' + !ft; + + // CHECK: UnaryOperator {{.*}} '' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()' + &ft; + + // CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]' + *at; + + // CHECK: UnaryOperator {{.*}} 'T *' prefix '+' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]' + +at; + + // CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]' + !at; + + // CHECK: UnaryOperator {{.*}} '' prefix '&' cannot overflow + // CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]' + &at; + } + + template + void Binary(T* pt, U* pu) { + // CHECK: BinaryOperator {{.*}} '' '+' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + // CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 + pt + 3; + + // CHECK: BinaryOperator {{.*}} '' '-' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + pt - pt; + + // CHECK: BinaryOperator {{.*}} '' '-' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *' + pt - pu; + + // CHECK: BinaryOperator {{.*}} '' '==' + // CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *' + // CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *' + pt == pu; + } +} // namespace Test diff --git a/clang/test/CXX/over/over.built/p10.cpp b/clang/test/CXX/over/over.built/p10.cpp index 678056da5820..8ff2396d0b6f 100644 --- a/clang/test/CXX/over/over.built/p10.cpp +++ b/clang/test/CXX/over/over.built/p10.cpp @@ -15,6 +15,6 @@ void f(int i, float f, bool b, char c, int* pi, A* pa, T* pt) { (void)-pi; // expected-error {{invalid argument type}} (void)-pa; // expected-error {{invalid argument type}} - (void)-pt; // FIXME: we should be able to give an error here. + (void)-pt; // expected-error {{invalid argument type}} } diff --git a/clang/test/CXX/over/over.built/p11.cpp b/clang/test/CXX/over/over.built/p11.cpp index 7ebf16b95439..f7a741db726d 100644 --- a/clang/test/CXX/over/over.built/p11.cpp +++ b/clang/test/CXX/over/over.built/p11.cpp @@ -7,6 +7,6 @@ void f(int i, float f, bool b, char c, int* pi, T* pt) { (void)~b; (void)~c; (void)~pi; // expected-error {{invalid argument type}} - (void)~pt; // FIXME: we should be able to give an error here. + (void)~pt; // expected-error {{invalid argument type}} } diff --git a/clang/test/CXX/over/over.oper/over.oper.general/p1.cpp b/clang/test/CXX/over/over.oper/over.oper.general/p1.cpp new file mode 100644 index 000000000000..d49fb0645751 --- /dev/null +++ b/clang/test/CXX/over/over.oper/over.oper.general/p1.cpp @@ -0,0 +1,173 @@ +// RUN: %clang_cc1 -std=c++20 -verify -Wno-unused %s + +template +void operator->*(T, U); + +template +void operator+(T, U); + +template +void operator-(T, U); + +template +void operator*(T, U); + +template +void operator/(T, U); + +template +void operator%(T, U); + +template +void operator^(T, U); + +template +void operator&(T, U); + +template +void operator|(T, U); + +template +void operator+=(T, U); + +template +void operator-=(T, U); + +template +void operator*=(T, U); + +template +void operator/=(T, U); + +template +void operator%=(T, U); + +template +void operator^=(T, U); + +template +void operator&=(T, U); + +template +void operator|=(T, U); + +template +void operator==(T, U); + +template +void operator!=(T, U); + +template +void operator<(T, U); + +template +void operator>(T, U); + +template +void operator<=(T, U); + +template +void operator>=(T, U); + +template +void operator<=>(T, U); + +template +void operator&&(T, U); + +template +void operator||(T, U); + +template +void operator<<(T, U); + +template +void operator>>(T, U); + +template +void operator<<=(T, U); + +template +void operator>>=(T, U); + +template +void operator,(T, U); + +template +void operator*(T); + +template +void operator&(T); + +template +void operator+(T); + +template +void operator-(T); + +template +void operator!(T); + +template +void operator~(T); + +template +void operator++(T); + +template +void operator--(T); + +template +void operator++(T, int); + +template +void operator--(T, int); + +template +void f(int *x) { + [&](auto *y) { + *y; + &y; + +y; + -y; // expected-error {{invalid argument type 'auto *' to unary expression}} + !y; + ~y; // expected-error {{invalid argument type 'auto *' to unary expression}} + ++y; + --y; + y++; + y--; + y->*x; + y + x; + y - x; + y * x; + y / x; + y % x; + y ^ x; + y & x; + y | x; + y += x; + y -= x; + y *= x; + y /= x; + y %= x; + y ^= x; + y &= x; + y |= x; + y == x; + y != x; + y < x; + y > x; + y <= x; + y >= x; + y <=> x; + y && x; + y || x; + y << x; + y >> x; + y <<= x; + y >>= x; + y, x; + }; +} + +template void f(int*); diff --git a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp index 3ca7c6c7eb8e..982e5372f5b0 100644 --- a/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp +++ b/clang/test/CXX/temp/temp.res/temp.dep/temp.dep.type/p4.cpp @@ -357,17 +357,14 @@ namespace N0 { a->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} a->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} - // FIXME: An overloaded unary 'operator*' is built for these - // even though the operand is a pointer (to a dependent type). - // Type::isOverloadableType should return false for such cases. - (*this).x4; - (*this).B::x4; - (*this).A::x4; - (*this).B::A::x4; - (*this).f4(); - (*this).B::f4(); - (*this).A::f4(); - (*this).B::A::f4(); + (*this).x4; // expected-error{{no member named 'x4' in 'B'}} + (*this).B::x4; // expected-error{{no member named 'x4' in 'B'}} + (*this).A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + (*this).B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}} + (*this).f4(); // expected-error{{no member named 'f4' in 'B'}} + (*this).B::f4(); // expected-error{{no member named 'f4' in 'B'}} + (*this).A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} + (*this).B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}} b.x4; // expected-error{{no member named 'x4' in 'B'}} b.B::x4; // expected-error{{no member named 'x4' in 'B'}} @@ -399,15 +396,13 @@ namespace N1 { f<0>(); this->f<0>(); a->f<0>(); - // FIXME: This should not require 'template'! - (*this).f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + (*this).f<0>(); b.f<0>(); x.f<0>(); this->x.f<0>(); a->x.f<0>(); - // FIXME: This should not require 'template'! - (*this).x.f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}} + (*this).x.f<0>(); b.x.f<0>(); // FIXME: None of these should require 'template'! diff --git a/clang/test/Frontend/noderef_templates.cpp b/clang/test/Frontend/noderef_templates.cpp index 5fde6efd87c7..9e54cd5d7889 100644 --- a/clang/test/Frontend/noderef_templates.cpp +++ b/clang/test/Frontend/noderef_templates.cpp @@ -3,8 +3,8 @@ #define NODEREF __attribute__((noderef)) template -int func(T NODEREF *a) { // expected-note 2 {{a declared here}} - return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}} +int func(T NODEREF *a) { // expected-note 3 {{a declared here}} + return *a + 1; // expected-warning 3 {{dereferencing a; was declared with a 'noderef' type}} } void func() { diff --git a/clang/test/SemaCXX/cxx2b-deducing-this.cpp b/clang/test/SemaCXX/cxx2b-deducing-this.cpp index 5f29a955e053..aa64530bd5be 100644 --- a/clang/test/SemaCXX/cxx2b-deducing-this.cpp +++ b/clang/test/SemaCXX/cxx2b-deducing-this.cpp @@ -19,7 +19,7 @@ struct S { // new and delete are implicitly static void *operator new(this unsigned long); // expected-error{{an explicit object parameter cannot appear in a static function}} void operator delete(this void*); // expected-error{{an explicit object parameter cannot appear in a static function}} - + void g(this auto) const; // expected-error{{explicit object member function cannot have 'const' qualifier}} void h(this auto) &; // expected-error{{explicit object member function cannot have '&' qualifier}} void i(this auto) &&; // expected-error{{explicit object member function cannot have '&&' qualifier}} @@ -198,9 +198,7 @@ void func(int i) { void TestMutationInLambda() { [i = 0](this auto &&){ i++; }(); [i = 0](this auto){ i++; }(); - [i = 0](this const auto&){ i++; }(); - // expected-error@-1 {{cannot assign to a variable captured by copy in a non-mutable lambda}} - // expected-note@-2 {{in instantiation of}} + [i = 0](this const auto&){ i++; }(); // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} int x; const auto l1 = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}} diff --git a/clang/test/SemaTemplate/class-template-spec.cpp b/clang/test/SemaTemplate/class-template-spec.cpp index 56b8207bd9a4..faa54c367538 100644 --- a/clang/test/SemaTemplate/class-template-spec.cpp +++ b/clang/test/SemaTemplate/class-template-spec.cpp @@ -18,7 +18,7 @@ int test_specs(A *a1, A *a2) { return a1->x + a2->y; } -int test_incomplete_specs(A *a1, +int test_incomplete_specs(A *a1, A *a2) { (void)a1->x; // expected-error{{member access into incomplete type}} @@ -39,7 +39,7 @@ template <> struct X { int foo(); }; // #1 template <> struct X { int bar(); }; // #2 typedef int int_type; -void testme(X *x1, X *x2) { +void testme(X *x1, X *x2) { (void)x1->foo(); // okay: refers to #1 (void)x2->bar(); // okay: refers to #2 } @@ -53,7 +53,7 @@ struct A { A::A() { } // Make sure we can see specializations defined before the primary template. -namespace N{ +namespace N{ template struct A0; } @@ -97,7 +97,7 @@ namespace M { template<> struct ::A; // expected-error{{must occur at global scope}} } -template<> struct N::B { +template<> struct N::B { int testf(int x) { return f(x); } }; @@ -138,9 +138,9 @@ namespace PR18009 { template struct C { template struct S; - template struct S {}; // expected-error {{depends on a template parameter of the partial specialization}} + template struct S {}; // ok }; - C c; // expected-note {{in instantiation of}} + C c; template struct outer { template struct inner {}; diff --git a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp index c08deb903f12..f26140675fd4 100644 --- a/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp +++ b/clang/unittests/ASTMatchers/ASTMatchersNarrowingTest.cpp @@ -1572,9 +1572,9 @@ TEST_P(ASTMatchersTest, IsArrow_MatchesMemberVariablesViaArrow) { EXPECT_TRUE( matches("template class Y { void x() { this->m; } int m; };", memberExpr(isArrow()))); - EXPECT_TRUE( - notMatches("template class Y { void x() { (*this).m; } };", - cxxDependentScopeMemberExpr(isArrow()))); + EXPECT_TRUE(notMatches( + "template class Y { void x() { (*this).m; } int m; };", + memberExpr(isArrow()))); } TEST_P(ASTMatchersTest, IsArrow_MatchesStaticMemberVariablesViaArrow) { -- GitLab From 3a4c1b9b4428b08d4475decf74c11e0d328c5842 Mon Sep 17 00:00:00 2001 From: Chuanqi Xu Date: Thu, 16 May 2024 09:55:36 +0800 Subject: [PATCH 057/403] [Serialization] Read the initializer for interesting static variables before consuming it (#92218) Close https://github.com/llvm/llvm-project/issues/91418 Since we load the variable's initializers lazily, it'd be problematic if the initializers dependent on each other. For example, ``` SomeType a = ...; SomeType b = a; ``` Previously, when we load variable `b`, we need to load the initializer, then we need to load `a`. We can only mark the variable `b` as loaded after we load `a`. Then `a` is always initialized before `b`. However, it is not true after we implement lazy loading for initializers. So here we try to load the initializers of static variables to make sure they are passed to code generator by order. If we read any thing interesting, we would consume that before emitting the current declaration. --- clang/lib/Serialization/ASTReaderDecl.cpp | 29 ++- clang/test/Modules/pr91418.cppm | 67 +++++ clang/test/OpenMP/nvptx_lambda_capturing.cpp | 246 +++++++++---------- 3 files changed, 216 insertions(+), 126 deletions(-) create mode 100644 clang/test/Modules/pr91418.cppm diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 0c647086e304..a6254b70560c 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -4186,12 +4186,35 @@ void ASTReader::PassInterestingDeclsToConsumer() { GetDecl(ID); EagerlyDeserializedDecls.clear(); - while (!PotentiallyInterestingDecls.empty()) { - Decl *D = PotentiallyInterestingDecls.front(); - PotentiallyInterestingDecls.pop_front(); + auto ConsumingPotentialInterestingDecls = [this]() { + while (!PotentiallyInterestingDecls.empty()) { + Decl *D = PotentiallyInterestingDecls.front(); + PotentiallyInterestingDecls.pop_front(); + if (isConsumerInterestedIn(D)) + PassInterestingDeclToConsumer(D); + } + }; + std::deque MaybeInterestingDecls = + std::move(PotentiallyInterestingDecls); + assert(PotentiallyInterestingDecls.empty()); + while (!MaybeInterestingDecls.empty()) { + Decl *D = MaybeInterestingDecls.front(); + MaybeInterestingDecls.pop_front(); + // Since we load the variable's initializers lazily, it'd be problematic + // if the initializers dependent on each other. So here we try to load the + // initializers of static variables to make sure they are passed to code + // generator by order. If we read anything interesting, we would consume + // that before emitting the current declaration. + if (auto *VD = dyn_cast(D); + VD && VD->isFileVarDecl() && !VD->isExternallyVisible()) + VD->getInit(); + ConsumingPotentialInterestingDecls(); if (isConsumerInterestedIn(D)) PassInterestingDeclToConsumer(D); } + + // If we add any new potential interesting decl in the last call, consume it. + ConsumingPotentialInterestingDecls(); } void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) { diff --git a/clang/test/Modules/pr91418.cppm b/clang/test/Modules/pr91418.cppm new file mode 100644 index 000000000000..33fec992439d --- /dev/null +++ b/clang/test/Modules/pr91418.cppm @@ -0,0 +1,67 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 -x c++-header %t/foo.h \ +// RUN: -emit-pch -o %t/foo.pch +// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++20 %t/use.cpp -include-pch \ +// RUN: %t/foo.pch -emit-llvm -o - | FileCheck %t/use.cpp + +//--- foo.h +#ifndef FOO_H +#define FOO_H +typedef float __m128 __attribute__((__vector_size__(16), __aligned__(16))); + +static __inline__ __m128 __attribute__((__always_inline__, __min_vector_width__(128))) +_mm_setr_ps(float __z, float __y, float __x, float __w) +{ + return __extension__ (__m128){ __z, __y, __x, __w }; +} + +typedef __m128 VR; + +inline VR MakeVR( float X, float Y, float Z, float W ) +{ + return _mm_setr_ps( X, Y, Z, W ); +} + +extern "C" float sqrtf(float); + +namespace VectorSinConstantsSSE +{ + float a = (16 * sqrtf(0.225f)); + VR A = MakeVR(a, a, a, a); + static const float b = (16 * sqrtf(0.225f)); + static const VR B = MakeVR(b, b, b, b); +} + +#endif // FOO_H + +//--- use.cpp +#include "foo.h" +float use() { + return VectorSinConstantsSSE::A[0] + VectorSinConstantsSSE::A[1] + + VectorSinConstantsSSE::A[2] + VectorSinConstantsSSE::A[3] + + VectorSinConstantsSSE::B[0] + VectorSinConstantsSSE::B[1] + + VectorSinConstantsSSE::B[2] + VectorSinConstantsSSE::B[3]; +} + +// CHECK: define{{.*}}@__cxx_global_var_init( +// CHECK: store{{.*}}[[a_RESULT:%[a-zA-Z0-9]+]], ptr @_ZN21VectorSinConstantsSSE1aE + +// CHECK: define{{.*}}@__cxx_global_var_init.1( +// CHECK: [[A_CALL:%[a-zA-Z0-9]+]] = call{{.*}}@_Z6MakeVRffff( +// CHECK: store{{.*}}[[A_CALL]], ptr @_ZN21VectorSinConstantsSSE1AE + +// CHECK: define{{.*}}@__cxx_global_var_init.2( +// CHECK: [[B_CALL:%[a-zA-Z0-9]+]] = call{{.*}}@_Z6MakeVRffff( +// CHECK: store{{.*}}[[B_CALL]], ptr @_ZN21VectorSinConstantsSSEL1BE + +// CHECK: define{{.*}}@__cxx_global_var_init.3( +// CHECK: store{{.*}}[[b_RESULT:%[a-zA-Z0-9]+]], ptr @_ZN21VectorSinConstantsSSEL1bE + +// CHECK: @_GLOBAL__sub_I_use.cpp +// CHECK: call{{.*}}@__cxx_global_var_init( +// CHECK: call{{.*}}@__cxx_global_var_init.1( +// CHECK: call{{.*}}@__cxx_global_var_init.3( +// CHECK: call{{.*}}@__cxx_global_var_init.2( diff --git a/clang/test/OpenMP/nvptx_lambda_capturing.cpp b/clang/test/OpenMP/nvptx_lambda_capturing.cpp index 641fbc38dd6b..efea8d4a0561 100644 --- a/clang/test/OpenMP/nvptx_lambda_capturing.cpp +++ b/clang/test/OpenMP/nvptx_lambda_capturing.cpp @@ -1165,8 +1165,113 @@ int main(int argc, char **argv) { // CHECK2-NEXT: ret void // // +// CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l27 +// CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], ptr noundef [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[L:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK3-NEXT: entry: +// CHECK3-NEXT: [[DYN_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[L_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[L1:%.*]] = alloca [[CLASS_ANON:%.*]], align 8 +// CHECK3-NEXT: [[_TMP2:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR]], align 8 +// CHECK3-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 +// CHECK3-NEXT: store ptr [[L]], ptr [[L_ADDR]], align 8 +// CHECK3-NEXT: [[TMP0:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 +// CHECK3-NEXT: [[TMP1:%.*]] = load ptr, ptr [[L_ADDR]], align 8 +// CHECK3-NEXT: store ptr [[TMP1]], ptr [[TMP]], align 8 +// CHECK3-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_target_init(ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l27_kernel_environment, ptr [[DYN_PTR]]) +// CHECK3-NEXT: [[EXEC_USER_CODE:%.*]] = icmp eq i32 [[TMP2]], -1 +// CHECK3-NEXT: br i1 [[EXEC_USER_CODE]], label [[USER_CODE_ENTRY:%.*]], label [[WORKER_EXIT:%.*]] +// CHECK3: user_code.entry: +// CHECK3-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TMP]], align 8 +// CHECK3-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[L1]], ptr align 8 [[TMP3]], i64 8, i1 false) +// CHECK3-NEXT: store ptr [[L1]], ptr [[_TMP2]], align 8 +// CHECK3-NEXT: [[TMP4:%.*]] = load ptr, ptr [[_TMP2]], align 8 +// CHECK3-NEXT: [[TMP5:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP4]], i32 0, i32 0 +// CHECK3-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK3-NEXT: [[TMP6:%.*]] = load ptr, ptr [[_TMP2]], align 8 +// CHECK3-NEXT: [[CALL:%.*]] = call noundef i32 @_ZZN1S3fooEvENKUlvE_clEv(ptr noundef nonnull align 8 dereferenceable(8) [[TMP6]]) #[[ATTR7:[0-9]+]] +// CHECK3-NEXT: call void @__kmpc_target_deinit() +// CHECK3-NEXT: ret void +// CHECK3: worker.exit: +// CHECK3-NEXT: ret void +// +// +// CHECK3-LABEL: define {{[^@]+}}@_ZZN1S3fooEvENKUlvE_clEv +// CHECK3-SAME: (ptr noundef nonnull align 8 dereferenceable(8) [[THIS:%.*]]) #[[ATTR2:[0-9]+]] comdat align 2 { +// CHECK3-NEXT: entry: +// CHECK3-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 +// CHECK3-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 +// CHECK3-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[CLASS_ANON:%.*]], ptr [[THIS1]], i32 0, i32 0 +// CHECK3-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 +// CHECK3-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], ptr [[TMP1]], i32 0, i32 0 +// CHECK3-NEXT: [[TMP2:%.*]] = load i32, ptr [[A]], align 4 +// CHECK3-NEXT: ret i32 [[TMP2]] +// +// +// CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l29 +// CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], ptr noundef [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[L:%.*]]) #[[ATTR3:[0-9]+]] { +// CHECK3-NEXT: entry: +// CHECK3-NEXT: [[DYN_PTR_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[L_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[CAPTURED_VARS_ADDRS:%.*]] = alloca [2 x ptr], align 8 +// CHECK3-NEXT: store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR]], align 8 +// CHECK3-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 +// CHECK3-NEXT: store ptr [[L]], ptr [[L_ADDR]], align 8 +// CHECK3-NEXT: [[TMP0:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 +// CHECK3-NEXT: [[TMP1:%.*]] = load ptr, ptr [[L_ADDR]], align 8 +// CHECK3-NEXT: store ptr [[TMP1]], ptr [[TMP]], align 8 +// CHECK3-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_target_init(ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l29_kernel_environment, ptr [[DYN_PTR]]) +// CHECK3-NEXT: [[EXEC_USER_CODE:%.*]] = icmp eq i32 [[TMP2]], -1 +// CHECK3-NEXT: br i1 [[EXEC_USER_CODE]], label [[USER_CODE_ENTRY:%.*]], label [[WORKER_EXIT:%.*]] +// CHECK3: user_code.entry: +// CHECK3-NEXT: [[TMP3:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1:[0-9]+]]) +// CHECK3-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP]], align 8 +// CHECK3-NEXT: [[TMP5:%.*]] = getelementptr inbounds [2 x ptr], ptr [[CAPTURED_VARS_ADDRS]], i64 0, i64 0 +// CHECK3-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 +// CHECK3-NEXT: [[TMP6:%.*]] = getelementptr inbounds [2 x ptr], ptr [[CAPTURED_VARS_ADDRS]], i64 0, i64 1 +// CHECK3-NEXT: store ptr [[TMP4]], ptr [[TMP6]], align 8 +// CHECK3-NEXT: call void @__kmpc_parallel_51(ptr @[[GLOB1]], i32 [[TMP3]], i32 1, i32 -1, i32 -1, ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l29_omp_outlined, ptr null, ptr [[CAPTURED_VARS_ADDRS]], i64 2) +// CHECK3-NEXT: call void @__kmpc_target_deinit() +// CHECK3-NEXT: ret void +// CHECK3: worker.exit: +// CHECK3-NEXT: ret void +// +// +// CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l29_omp_outlined +// CHECK3-SAME: (ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[L:%.*]]) #[[ATTR4:[0-9]+]] { +// CHECK3-NEXT: entry: +// CHECK3-NEXT: [[DOTGLOBAL_TID__ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[DOTBOUND_TID__ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[L_ADDR:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: [[L1:%.*]] = alloca [[CLASS_ANON:%.*]], align 8 +// CHECK3-NEXT: [[_TMP2:%.*]] = alloca ptr, align 8 +// CHECK3-NEXT: store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR]], align 8 +// CHECK3-NEXT: store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR]], align 8 +// CHECK3-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 +// CHECK3-NEXT: store ptr [[L]], ptr [[L_ADDR]], align 8 +// CHECK3-NEXT: [[TMP0:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 +// CHECK3-NEXT: [[TMP1:%.*]] = load ptr, ptr [[L_ADDR]], align 8 +// CHECK3-NEXT: store ptr [[TMP1]], ptr [[TMP]], align 8 +// CHECK3-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP]], align 8 +// CHECK3-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[L1]], ptr align 8 [[TMP2]], i64 8, i1 false) +// CHECK3-NEXT: store ptr [[L1]], ptr [[_TMP2]], align 8 +// CHECK3-NEXT: [[TMP3:%.*]] = load ptr, ptr [[_TMP2]], align 8 +// CHECK3-NEXT: [[TMP4:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP3]], i32 0, i32 0 +// CHECK3-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 +// CHECK3-NEXT: [[TMP5:%.*]] = load ptr, ptr [[_TMP2]], align 8 +// CHECK3-NEXT: [[CALL:%.*]] = call noundef i32 @_ZZN1S3fooEvENKUlvE_clEv(ptr noundef nonnull align 8 dereferenceable(8) [[TMP5]]) #[[ATTR7]] +// CHECK3-NEXT: ret void +// +// // CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}_main_l41 -// CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], i64 noundef [[ARGC:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[C:%.*]], ptr noundef [[D:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[A:%.*]], ptr noundef nonnull align 8 dereferenceable(40) [[L:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], i64 noundef [[ARGC:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[C:%.*]], ptr noundef [[D:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[A:%.*]], ptr noundef nonnull align 8 dereferenceable(40) [[L:%.*]]) #[[ATTR0]] { // CHECK3-NEXT: entry: // CHECK3-NEXT: [[DYN_PTR_ADDR:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[ARGC_ADDR:%.*]] = alloca i64, align 8 @@ -1178,7 +1283,7 @@ int main(int argc, char **argv) { // CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[_TMP1:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[_TMP2:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[L3:%.*]] = alloca [[CLASS_ANON:%.*]], align 8 +// CHECK3-NEXT: [[L3:%.*]] = alloca [[CLASS_ANON_1:%.*]], align 8 // CHECK3-NEXT: [[_TMP4:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[B5:%.*]] = alloca i32, align 4 // CHECK3-NEXT: [[_TMP6:%.*]] = alloca ptr, align 8 @@ -1214,20 +1319,20 @@ int main(int argc, char **argv) { // CHECK3-NEXT: store i32 [[TMP9]], ptr [[C7]], align 4 // CHECK3-NEXT: store ptr [[C7]], ptr [[_TMP8]], align 8 // CHECK3-NEXT: [[TMP10:%.*]] = load ptr, ptr [[_TMP4]], align 8 -// CHECK3-NEXT: [[TMP11:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP10]], i32 0, i32 0 +// CHECK3-NEXT: [[TMP11:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP10]], i32 0, i32 0 // CHECK3-NEXT: store ptr [[ARGC_ADDR]], ptr [[TMP11]], align 8 -// CHECK3-NEXT: [[TMP12:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP10]], i32 0, i32 1 +// CHECK3-NEXT: [[TMP12:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP10]], i32 0, i32 1 // CHECK3-NEXT: [[TMP13:%.*]] = load ptr, ptr [[_TMP6]], align 8 // CHECK3-NEXT: store ptr [[TMP13]], ptr [[TMP12]], align 8 -// CHECK3-NEXT: [[TMP14:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP10]], i32 0, i32 2 +// CHECK3-NEXT: [[TMP14:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP10]], i32 0, i32 2 // CHECK3-NEXT: [[TMP15:%.*]] = load ptr, ptr [[_TMP8]], align 8 // CHECK3-NEXT: store ptr [[TMP15]], ptr [[TMP14]], align 8 -// CHECK3-NEXT: [[TMP16:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP10]], i32 0, i32 3 +// CHECK3-NEXT: [[TMP16:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP10]], i32 0, i32 3 // CHECK3-NEXT: store ptr [[D_ADDR]], ptr [[TMP16]], align 8 -// CHECK3-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP10]], i32 0, i32 4 +// CHECK3-NEXT: [[TMP17:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP10]], i32 0, i32 4 // CHECK3-NEXT: store ptr [[TMP2]], ptr [[TMP17]], align 8 // CHECK3-NEXT: [[TMP18:%.*]] = load ptr, ptr [[_TMP4]], align 8 -// CHECK3-NEXT: [[CALL:%.*]] = call noundef i64 @"_ZZ4mainENK3$_0clEv"(ptr noundef nonnull align 8 dereferenceable(40) [[TMP18]]) #[[ATTR7:[0-9]+]] +// CHECK3-NEXT: [[CALL:%.*]] = call noundef i64 @"_ZZ4mainENK3$_0clEv"(ptr noundef nonnull align 8 dereferenceable(40) [[TMP18]]) #[[ATTR7]] // CHECK3-NEXT: call void @__kmpc_target_deinit() // CHECK3-NEXT: ret void // CHECK3: worker.exit: @@ -1235,7 +1340,7 @@ int main(int argc, char **argv) { // // // CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}_main_l43 -// CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[ARGC:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[C:%.*]], ptr noundef [[D:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[A:%.*]], ptr noundef nonnull align 8 dereferenceable(40) [[L:%.*]]) #[[ATTR3:[0-9]+]] { +// CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[ARGC:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[C:%.*]], ptr noundef [[D:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[A:%.*]], ptr noundef nonnull align 8 dereferenceable(40) [[L:%.*]]) #[[ATTR3]] { // CHECK3-NEXT: entry: // CHECK3-NEXT: [[DYN_PTR_ADDR:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[ARGC_ADDR:%.*]] = alloca ptr, align 8 @@ -1267,7 +1372,7 @@ int main(int argc, char **argv) { // CHECK3-NEXT: [[EXEC_USER_CODE:%.*]] = icmp eq i32 [[TMP5]], -1 // CHECK3-NEXT: br i1 [[EXEC_USER_CODE]], label [[USER_CODE_ENTRY:%.*]], label [[WORKER_EXIT:%.*]] // CHECK3: user_code.entry: -// CHECK3-NEXT: [[TMP6:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1:[0-9]+]]) +// CHECK3-NEXT: [[TMP6:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]]) // CHECK3-NEXT: [[TMP7:%.*]] = load ptr, ptr [[TMP]], align 8 // CHECK3-NEXT: [[TMP8:%.*]] = load ptr, ptr [[_TMP1]], align 8 // CHECK3-NEXT: [[TMP9:%.*]] = load ptr, ptr [[D_ADDR]], align 8 @@ -1292,7 +1397,7 @@ int main(int argc, char **argv) { // // // CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}_main_l43_omp_outlined -// CHECK3-SAME: (ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[ARGC:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[C:%.*]], ptr noundef [[D:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[A:%.*]], ptr noundef nonnull align 8 dereferenceable(40) [[L:%.*]]) #[[ATTR4:[0-9]+]] { +// CHECK3-SAME: (ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[ARGC:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[B:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[C:%.*]], ptr noundef [[D:%.*]], ptr noundef nonnull align 4 dereferenceable(4) [[A:%.*]], ptr noundef nonnull align 8 dereferenceable(40) [[L:%.*]]) #[[ATTR4]] { // CHECK3-NEXT: entry: // CHECK3-NEXT: [[DOTGLOBAL_TID__ADDR:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[DOTBOUND_TID__ADDR:%.*]] = alloca ptr, align 8 @@ -1305,7 +1410,7 @@ int main(int argc, char **argv) { // CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[_TMP1:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[_TMP2:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[L3:%.*]] = alloca [[CLASS_ANON:%.*]], align 8 +// CHECK3-NEXT: [[L3:%.*]] = alloca [[CLASS_ANON_1:%.*]], align 8 // CHECK3-NEXT: [[_TMP4:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[ARGC5:%.*]] = alloca i32, align 4 // CHECK3-NEXT: [[B6:%.*]] = alloca i32, align 4 @@ -1345,128 +1450,23 @@ int main(int argc, char **argv) { // CHECK3-NEXT: [[TMP11:%.*]] = load i32, ptr [[TMP3]], align 4 // CHECK3-NEXT: store i32 [[TMP11]], ptr [[A10]], align 4 // CHECK3-NEXT: [[TMP12:%.*]] = load ptr, ptr [[_TMP4]], align 8 -// CHECK3-NEXT: [[TMP13:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP12]], i32 0, i32 0 +// CHECK3-NEXT: [[TMP13:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP12]], i32 0, i32 0 // CHECK3-NEXT: store ptr [[ARGC5]], ptr [[TMP13]], align 8 -// CHECK3-NEXT: [[TMP14:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP12]], i32 0, i32 1 +// CHECK3-NEXT: [[TMP14:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP12]], i32 0, i32 1 // CHECK3-NEXT: [[TMP15:%.*]] = load ptr, ptr [[_TMP7]], align 8 // CHECK3-NEXT: store ptr [[TMP15]], ptr [[TMP14]], align 8 -// CHECK3-NEXT: [[TMP16:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP12]], i32 0, i32 2 +// CHECK3-NEXT: [[TMP16:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP12]], i32 0, i32 2 // CHECK3-NEXT: [[TMP17:%.*]] = load ptr, ptr [[_TMP9]], align 8 // CHECK3-NEXT: store ptr [[TMP17]], ptr [[TMP16]], align 8 -// CHECK3-NEXT: [[TMP18:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP12]], i32 0, i32 3 +// CHECK3-NEXT: [[TMP18:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP12]], i32 0, i32 3 // CHECK3-NEXT: store ptr [[D_ADDR]], ptr [[TMP18]], align 8 -// CHECK3-NEXT: [[TMP19:%.*]] = getelementptr inbounds [[CLASS_ANON]], ptr [[TMP12]], i32 0, i32 4 +// CHECK3-NEXT: [[TMP19:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP12]], i32 0, i32 4 // CHECK3-NEXT: store ptr [[A10]], ptr [[TMP19]], align 8 // CHECK3-NEXT: [[TMP20:%.*]] = load ptr, ptr [[_TMP4]], align 8 // CHECK3-NEXT: [[CALL:%.*]] = call noundef i64 @"_ZZ4mainENK3$_0clEv"(ptr noundef nonnull align 8 dereferenceable(40) [[TMP20]]) #[[ATTR7]] // CHECK3-NEXT: ret void // // -// CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l27 -// CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], ptr noundef [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[L:%.*]]) #[[ATTR0]] { -// CHECK3-NEXT: entry: -// CHECK3-NEXT: [[DYN_PTR_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[L_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[L1:%.*]] = alloca [[CLASS_ANON_1:%.*]], align 8 -// CHECK3-NEXT: [[_TMP2:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR]], align 8 -// CHECK3-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 -// CHECK3-NEXT: store ptr [[L]], ptr [[L_ADDR]], align 8 -// CHECK3-NEXT: [[TMP0:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 -// CHECK3-NEXT: [[TMP1:%.*]] = load ptr, ptr [[L_ADDR]], align 8 -// CHECK3-NEXT: store ptr [[TMP1]], ptr [[TMP]], align 8 -// CHECK3-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_target_init(ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l27_kernel_environment, ptr [[DYN_PTR]]) -// CHECK3-NEXT: [[EXEC_USER_CODE:%.*]] = icmp eq i32 [[TMP2]], -1 -// CHECK3-NEXT: br i1 [[EXEC_USER_CODE]], label [[USER_CODE_ENTRY:%.*]], label [[WORKER_EXIT:%.*]] -// CHECK3: user_code.entry: -// CHECK3-NEXT: [[TMP3:%.*]] = load ptr, ptr [[TMP]], align 8 -// CHECK3-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[L1]], ptr align 8 [[TMP3]], i64 8, i1 false) -// CHECK3-NEXT: store ptr [[L1]], ptr [[_TMP2]], align 8 -// CHECK3-NEXT: [[TMP4:%.*]] = load ptr, ptr [[_TMP2]], align 8 -// CHECK3-NEXT: [[TMP5:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP4]], i32 0, i32 0 -// CHECK3-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 -// CHECK3-NEXT: [[TMP6:%.*]] = load ptr, ptr [[_TMP2]], align 8 -// CHECK3-NEXT: [[CALL:%.*]] = call noundef i32 @_ZZN1S3fooEvENKUlvE_clEv(ptr noundef nonnull align 8 dereferenceable(8) [[TMP6]]) #[[ATTR7]] -// CHECK3-NEXT: call void @__kmpc_target_deinit() -// CHECK3-NEXT: ret void -// CHECK3: worker.exit: -// CHECK3-NEXT: ret void -// -// -// CHECK3-LABEL: define {{[^@]+}}@_ZZN1S3fooEvENKUlvE_clEv -// CHECK3-SAME: (ptr noundef nonnull align 8 dereferenceable(8) [[THIS:%.*]]) #[[ATTR2:[0-9]+]] comdat align 2 { -// CHECK3-NEXT: entry: -// CHECK3-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 -// CHECK3-NEXT: [[THIS1:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 -// CHECK3-NEXT: [[TMP0:%.*]] = getelementptr inbounds [[CLASS_ANON_1:%.*]], ptr [[THIS1]], i32 0, i32 0 -// CHECK3-NEXT: [[TMP1:%.*]] = load ptr, ptr [[TMP0]], align 8 -// CHECK3-NEXT: [[A:%.*]] = getelementptr inbounds [[STRUCT_S:%.*]], ptr [[TMP1]], i32 0, i32 0 -// CHECK3-NEXT: [[TMP2:%.*]] = load i32, ptr [[A]], align 4 -// CHECK3-NEXT: ret i32 [[TMP2]] -// -// -// CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l29 -// CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], ptr noundef [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[L:%.*]]) #[[ATTR3]] { -// CHECK3-NEXT: entry: -// CHECK3-NEXT: [[DYN_PTR_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[L_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[CAPTURED_VARS_ADDRS:%.*]] = alloca [2 x ptr], align 8 -// CHECK3-NEXT: store ptr [[DYN_PTR]], ptr [[DYN_PTR_ADDR]], align 8 -// CHECK3-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 -// CHECK3-NEXT: store ptr [[L]], ptr [[L_ADDR]], align 8 -// CHECK3-NEXT: [[TMP0:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 -// CHECK3-NEXT: [[TMP1:%.*]] = load ptr, ptr [[L_ADDR]], align 8 -// CHECK3-NEXT: store ptr [[TMP1]], ptr [[TMP]], align 8 -// CHECK3-NEXT: [[TMP2:%.*]] = call i32 @__kmpc_target_init(ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l29_kernel_environment, ptr [[DYN_PTR]]) -// CHECK3-NEXT: [[EXEC_USER_CODE:%.*]] = icmp eq i32 [[TMP2]], -1 -// CHECK3-NEXT: br i1 [[EXEC_USER_CODE]], label [[USER_CODE_ENTRY:%.*]], label [[WORKER_EXIT:%.*]] -// CHECK3: user_code.entry: -// CHECK3-NEXT: [[TMP3:%.*]] = call i32 @__kmpc_global_thread_num(ptr @[[GLOB1]]) -// CHECK3-NEXT: [[TMP4:%.*]] = load ptr, ptr [[TMP]], align 8 -// CHECK3-NEXT: [[TMP5:%.*]] = getelementptr inbounds [2 x ptr], ptr [[CAPTURED_VARS_ADDRS]], i64 0, i64 0 -// CHECK3-NEXT: store ptr [[TMP0]], ptr [[TMP5]], align 8 -// CHECK3-NEXT: [[TMP6:%.*]] = getelementptr inbounds [2 x ptr], ptr [[CAPTURED_VARS_ADDRS]], i64 0, i64 1 -// CHECK3-NEXT: store ptr [[TMP4]], ptr [[TMP6]], align 8 -// CHECK3-NEXT: call void @__kmpc_parallel_51(ptr @[[GLOB1]], i32 [[TMP3]], i32 1, i32 -1, i32 -1, ptr @{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l29_omp_outlined, ptr null, ptr [[CAPTURED_VARS_ADDRS]], i64 2) -// CHECK3-NEXT: call void @__kmpc_target_deinit() -// CHECK3-NEXT: ret void -// CHECK3: worker.exit: -// CHECK3-NEXT: ret void -// -// -// CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__ZN1S3fooEv_l29_omp_outlined -// CHECK3-SAME: (ptr noalias noundef [[DOTGLOBAL_TID_:%.*]], ptr noalias noundef [[DOTBOUND_TID_:%.*]], ptr noundef [[THIS:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[L:%.*]]) #[[ATTR4]] { -// CHECK3-NEXT: entry: -// CHECK3-NEXT: [[DOTGLOBAL_TID__ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[DOTBOUND_TID__ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[THIS_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[L_ADDR:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[L1:%.*]] = alloca [[CLASS_ANON_1:%.*]], align 8 -// CHECK3-NEXT: [[_TMP2:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR]], align 8 -// CHECK3-NEXT: store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR]], align 8 -// CHECK3-NEXT: store ptr [[THIS]], ptr [[THIS_ADDR]], align 8 -// CHECK3-NEXT: store ptr [[L]], ptr [[L_ADDR]], align 8 -// CHECK3-NEXT: [[TMP0:%.*]] = load ptr, ptr [[THIS_ADDR]], align 8 -// CHECK3-NEXT: [[TMP1:%.*]] = load ptr, ptr [[L_ADDR]], align 8 -// CHECK3-NEXT: store ptr [[TMP1]], ptr [[TMP]], align 8 -// CHECK3-NEXT: [[TMP2:%.*]] = load ptr, ptr [[TMP]], align 8 -// CHECK3-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 8 [[L1]], ptr align 8 [[TMP2]], i64 8, i1 false) -// CHECK3-NEXT: store ptr [[L1]], ptr [[_TMP2]], align 8 -// CHECK3-NEXT: [[TMP3:%.*]] = load ptr, ptr [[_TMP2]], align 8 -// CHECK3-NEXT: [[TMP4:%.*]] = getelementptr inbounds [[CLASS_ANON_1]], ptr [[TMP3]], i32 0, i32 0 -// CHECK3-NEXT: store ptr [[TMP0]], ptr [[TMP4]], align 8 -// CHECK3-NEXT: [[TMP5:%.*]] = load ptr, ptr [[_TMP2]], align 8 -// CHECK3-NEXT: [[CALL:%.*]] = call noundef i32 @_ZZN1S3fooEvENKUlvE_clEv(ptr noundef nonnull align 8 dereferenceable(8) [[TMP5]]) #[[ATTR7]] -// CHECK3-NEXT: ret void -// -// // CHECK3-LABEL: define {{[^@]+}}@{{__omp_offloading_[0-9a-z]+_[0-9a-z]+}}__Z3fooIZN1S3fooEvEUlvE_EiRKT__l18 // CHECK3-SAME: (ptr noalias noundef [[DYN_PTR:%.*]], ptr noundef nonnull align 8 dereferenceable(8) [[T:%.*]]) #[[ATTR3]] { // CHECK3-NEXT: entry: @@ -1500,7 +1500,7 @@ int main(int argc, char **argv) { // CHECK3-NEXT: [[DOTBOUND_TID__ADDR:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[T_ADDR:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: [[TMP:%.*]] = alloca ptr, align 8 -// CHECK3-NEXT: [[T1:%.*]] = alloca [[CLASS_ANON_1:%.*]], align 8 +// CHECK3-NEXT: [[T1:%.*]] = alloca [[CLASS_ANON:%.*]], align 8 // CHECK3-NEXT: [[_TMP2:%.*]] = alloca ptr, align 8 // CHECK3-NEXT: store ptr [[DOTGLOBAL_TID_]], ptr [[DOTGLOBAL_TID__ADDR]], align 8 // CHECK3-NEXT: store ptr [[DOTBOUND_TID_]], ptr [[DOTBOUND_TID__ADDR]], align 8 -- GitLab From 1dd0d3cf40f21b842dbee107b3d203db9fbaa4ae Mon Sep 17 00:00:00 2001 From: Dhruv Chawla Date: Thu, 16 May 2024 08:08:06 +0530 Subject: [PATCH 058/403] [AArch64][GISel] Fold COPY(y:gpr, DUP(x:fpr, i)) -> UMOV(y:gpr, x:fpr, i) (#89017) This patch adds a peephole to AArch64PostSelectOptimize for codegen that is caused by RegBankSelect limiting G_EXTRACT_VECTOR_ELT only to FPR registers in both the input and output registers. This can cause a generation of COPY from FPR to GPR when, for example, the output register of the G_EXTRACT_VECTOR_ELT is used in a branch condition. This was noticed when looking at codegen differences between SDAG and GI for the s1279 kernel in the TSVC benchmark. --- .../GISel/AArch64PostSelectOptimize.cpp | 68 ++++++++- llvm/test/CodeGen/AArch64/aarch64-mulv.ll | 117 +++++---------- llvm/test/CodeGen/AArch64/aarch64-smull.ll | 133 ++++++++---------- llvm/test/CodeGen/AArch64/arm64-neon-copy.ll | 12 +- llvm/test/CodeGen/AArch64/bitcast.ll | 24 ++-- llvm/test/CodeGen/AArch64/insertextract.ll | 13 +- llvm/test/CodeGen/AArch64/ptradd.ll | 7 +- llvm/test/CodeGen/AArch64/reduce-and.ll | 42 +++--- llvm/test/CodeGen/AArch64/reduce-or.ll | 42 +++--- llvm/test/CodeGen/AArch64/reduce-xor.ll | 42 +++--- 10 files changed, 238 insertions(+), 262 deletions(-) diff --git a/llvm/lib/Target/AArch64/GISel/AArch64PostSelectOptimize.cpp b/llvm/lib/Target/AArch64/GISel/AArch64PostSelectOptimize.cpp index 11866f2dd186..e9aed60595e6 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64PostSelectOptimize.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64PostSelectOptimize.cpp @@ -48,6 +48,7 @@ private: bool doPeepholeOpts(MachineBasicBlock &MBB); /// Look for cross regclass copies that can be trivially eliminated. bool foldSimpleCrossClassCopies(MachineInstr &MI); + bool foldCopyDup(MachineInstr &MI); }; } // end anonymous namespace @@ -105,7 +106,10 @@ unsigned getNonFlagSettingVariant(unsigned Opc) { bool AArch64PostSelectOptimize::doPeepholeOpts(MachineBasicBlock &MBB) { bool Changed = false; for (auto &MI : make_early_inc_range(make_range(MBB.begin(), MBB.end()))) { - Changed |= foldSimpleCrossClassCopies(MI); + bool CurrentIterChanged = foldSimpleCrossClassCopies(MI); + if (!CurrentIterChanged) + CurrentIterChanged |= foldCopyDup(MI); + Changed |= CurrentIterChanged; } return Changed; } @@ -158,6 +162,68 @@ bool AArch64PostSelectOptimize::foldSimpleCrossClassCopies(MachineInstr &MI) { return true; } +bool AArch64PostSelectOptimize::foldCopyDup(MachineInstr &MI) { + if (!MI.isCopy()) + return false; + + auto *MF = MI.getMF(); + auto &MRI = MF->getRegInfo(); + auto *TII = MF->getSubtarget().getInstrInfo(); + + // Optimize COPY(y:GPR, DUP(x:FPR, i)) -> UMOV(y:GPR, x:FPR, i). + // Here Dst is y and Src is the result of DUP. + Register Dst = MI.getOperand(0).getReg(); + Register Src = MI.getOperand(1).getReg(); + + if (!Dst.isVirtual() || !Src.isVirtual()) + return false; + + auto TryMatchDUP = [&](const TargetRegisterClass *GPRRegClass, + const TargetRegisterClass *FPRRegClass, unsigned DUP, + unsigned UMOV) { + if (MRI.getRegClassOrNull(Dst) != GPRRegClass || + MRI.getRegClassOrNull(Src) != FPRRegClass) + return false; + + // There is a special case when one of the uses is COPY(z:FPR, y:GPR). + // In this case, we get COPY(z:FPR, COPY(y:GPR, DUP(x:FPR, i))), which can + // be folded by peephole-opt into just DUP(z:FPR, i), so this transform is + // not worthwhile in that case. + for (auto &Use : MRI.use_nodbg_instructions(Dst)) { + if (!Use.isCopy()) + continue; + + Register UseOp0 = Use.getOperand(0).getReg(); + Register UseOp1 = Use.getOperand(1).getReg(); + if (UseOp0.isPhysical() || UseOp1.isPhysical()) + return false; + + if (MRI.getRegClassOrNull(UseOp0) == FPRRegClass && + MRI.getRegClassOrNull(UseOp1) == GPRRegClass) + return false; + } + + MachineInstr *SrcMI = MRI.getUniqueVRegDef(Src); + if (!SrcMI || SrcMI->getOpcode() != DUP || !MRI.hasOneNonDBGUse(Src)) + return false; + + Register DupSrc = SrcMI->getOperand(1).getReg(); + int64_t DupImm = SrcMI->getOperand(2).getImm(); + + BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), TII->get(UMOV), Dst) + .addReg(DupSrc) + .addImm(DupImm); + SrcMI->eraseFromParent(); + MI.eraseFromParent(); + return true; + }; + + return TryMatchDUP(&AArch64::GPR32RegClass, &AArch64::FPR32RegClass, + AArch64::DUPi32, AArch64::UMOVvi32) || + TryMatchDUP(&AArch64::GPR64RegClass, &AArch64::FPR64RegClass, + AArch64::DUPi64, AArch64::UMOVvi64); +} + bool AArch64PostSelectOptimize::optimizeNZCVDefs(MachineBasicBlock &MBB) { // If we find a dead NZCV implicit-def, we // - try to convert the operation to a non-flag-setting equivalent diff --git a/llvm/test/CodeGen/AArch64/aarch64-mulv.ll b/llvm/test/CodeGen/AArch64/aarch64-mulv.ll index 7b7ca9d8ffc2..e11ae9a25159 100644 --- a/llvm/test/CodeGen/AArch64/aarch64-mulv.ll +++ b/llvm/test/CodeGen/AArch64/aarch64-mulv.ll @@ -25,22 +25,13 @@ declare i64 @llvm.vector.reduce.mul.v4i64(<4 x i64>) declare i128 @llvm.vector.reduce.mul.v2i128(<2 x i128>) define i8 @mulv_v2i8(<2 x i8> %a) { -; CHECK-SD-LABEL: mulv_v2i8: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: // kill: def $d0 killed $d0 def $q0 -; CHECK-SD-NEXT: mov w8, v0.s[1] -; CHECK-SD-NEXT: fmov w9, s0 -; CHECK-SD-NEXT: mul w0, w9, w8 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: mulv_v2i8: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: // kill: def $d0 killed $d0 def $q0 -; CHECK-GI-NEXT: mov s1, v0.s[1] -; CHECK-GI-NEXT: fmov w8, s0 -; CHECK-GI-NEXT: fmov w9, s1 -; CHECK-GI-NEXT: mul w0, w8, w9 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: mulv_v2i8: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 +; CHECK-NEXT: mov w8, v0.s[1] +; CHECK-NEXT: fmov w9, s0 +; CHECK-NEXT: mul w0, w9, w8 +; CHECK-NEXT: ret entry: %arg1 = call i8 @llvm.vector.reduce.mul.v2i8(<2 x i8> %a) ret i8 %arg1 @@ -230,22 +221,13 @@ entry: } define i16 @mulv_v2i16(<2 x i16> %a) { -; CHECK-SD-LABEL: mulv_v2i16: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: // kill: def $d0 killed $d0 def $q0 -; CHECK-SD-NEXT: mov w8, v0.s[1] -; CHECK-SD-NEXT: fmov w9, s0 -; CHECK-SD-NEXT: mul w0, w9, w8 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: mulv_v2i16: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: // kill: def $d0 killed $d0 def $q0 -; CHECK-GI-NEXT: mov s1, v0.s[1] -; CHECK-GI-NEXT: fmov w8, s0 -; CHECK-GI-NEXT: fmov w9, s1 -; CHECK-GI-NEXT: mul w0, w8, w9 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: mulv_v2i16: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 +; CHECK-NEXT: mov w8, v0.s[1] +; CHECK-NEXT: fmov w9, s0 +; CHECK-NEXT: mul w0, w9, w8 +; CHECK-NEXT: ret entry: %arg1 = call i16 @llvm.vector.reduce.mul.v2i16(<2 x i16> %a) ret i16 %arg1 @@ -372,22 +354,13 @@ entry: } define i32 @mulv_v2i32(<2 x i32> %a) { -; CHECK-SD-LABEL: mulv_v2i32: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: // kill: def $d0 killed $d0 def $q0 -; CHECK-SD-NEXT: mov w8, v0.s[1] -; CHECK-SD-NEXT: fmov w9, s0 -; CHECK-SD-NEXT: mul w0, w9, w8 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: mulv_v2i32: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: // kill: def $d0 killed $d0 def $q0 -; CHECK-GI-NEXT: mov s1, v0.s[1] -; CHECK-GI-NEXT: fmov w8, s0 -; CHECK-GI-NEXT: fmov w9, s1 -; CHECK-GI-NEXT: mul w0, w8, w9 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: mulv_v2i32: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: // kill: def $d0 killed $d0 def $q0 +; CHECK-NEXT: mov w8, v0.s[1] +; CHECK-NEXT: fmov w9, s0 +; CHECK-NEXT: mul w0, w9, w8 +; CHECK-NEXT: ret entry: %arg1 = call i32 @llvm.vector.reduce.mul.v2i32(<2 x i32> %a) ret i32 %arg1 @@ -424,10 +397,9 @@ define i32 @mulv_v4i32(<4 x i32> %a) { ; CHECK-GI: // %bb.0: // %entry ; CHECK-GI-NEXT: mov d1, v0.d[1] ; CHECK-GI-NEXT: mul v0.2s, v0.2s, v1.2s -; CHECK-GI-NEXT: mov s1, v0.s[1] -; CHECK-GI-NEXT: fmov w8, s0 -; CHECK-GI-NEXT: fmov w9, s1 -; CHECK-GI-NEXT: mul w0, w8, w9 +; CHECK-GI-NEXT: mov w8, v0.s[1] +; CHECK-GI-NEXT: fmov w9, s0 +; CHECK-GI-NEXT: mul w0, w9, w8 ; CHECK-GI-NEXT: ret entry: %arg1 = call i32 @llvm.vector.reduce.mul.v4i32(<4 x i32> %a) @@ -452,10 +424,9 @@ define i32 @mulv_v8i32(<8 x i32> %a) { ; CHECK-GI-NEXT: mul v0.2s, v0.2s, v2.2s ; CHECK-GI-NEXT: mul v1.2s, v1.2s, v3.2s ; CHECK-GI-NEXT: mul v0.2s, v0.2s, v1.2s -; CHECK-GI-NEXT: mov s1, v0.s[1] -; CHECK-GI-NEXT: fmov w8, s0 -; CHECK-GI-NEXT: fmov w9, s1 -; CHECK-GI-NEXT: mul w0, w8, w9 +; CHECK-GI-NEXT: mov w8, v0.s[1] +; CHECK-GI-NEXT: fmov w9, s0 +; CHECK-GI-NEXT: mul w0, w9, w8 ; CHECK-GI-NEXT: ret entry: %arg1 = call i32 @llvm.vector.reduce.mul.v8i32(<8 x i32> %a) @@ -463,20 +434,12 @@ entry: } define i64 @mulv_v2i64(<2 x i64> %a) { -; CHECK-SD-LABEL: mulv_v2i64: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: mov x8, v0.d[1] -; CHECK-SD-NEXT: fmov x9, d0 -; CHECK-SD-NEXT: mul x0, x9, x8 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: mulv_v2i64: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: mov d1, v0.d[1] -; CHECK-GI-NEXT: fmov x8, d0 -; CHECK-GI-NEXT: fmov x9, d1 -; CHECK-GI-NEXT: mul x0, x8, x9 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: mulv_v2i64: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: mov x8, v0.d[1] +; CHECK-NEXT: fmov x9, d0 +; CHECK-NEXT: mul x0, x9, x8 +; CHECK-NEXT: ret entry: %arg1 = call i64 @llvm.vector.reduce.mul.v2i64(<2 x i64> %a) ret i64 %arg1 @@ -522,14 +485,12 @@ define i64 @mulv_v4i64(<4 x i64> %a) { ; ; CHECK-GI-LABEL: mulv_v4i64: ; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: mov d2, v0.d[1] -; CHECK-GI-NEXT: mov d3, v1.d[1] -; CHECK-GI-NEXT: fmov x8, d0 -; CHECK-GI-NEXT: fmov x9, d2 -; CHECK-GI-NEXT: fmov x10, d3 -; CHECK-GI-NEXT: mul x8, x8, x9 -; CHECK-GI-NEXT: fmov x9, d1 -; CHECK-GI-NEXT: mul x9, x9, x10 +; CHECK-GI-NEXT: mov x8, v0.d[1] +; CHECK-GI-NEXT: fmov x10, d0 +; CHECK-GI-NEXT: mov x9, v1.d[1] +; CHECK-GI-NEXT: mul x8, x10, x8 +; CHECK-GI-NEXT: fmov x10, d1 +; CHECK-GI-NEXT: mul x9, x10, x9 ; CHECK-GI-NEXT: mul x0, x8, x9 ; CHECK-GI-NEXT: ret entry: diff --git a/llvm/test/CodeGen/AArch64/aarch64-smull.ll b/llvm/test/CodeGen/AArch64/aarch64-smull.ll index 540471a05901..307aa397eabb 100644 --- a/llvm/test/CodeGen/AArch64/aarch64-smull.ll +++ b/llvm/test/CodeGen/AArch64/aarch64-smull.ll @@ -279,17 +279,15 @@ define <2 x i64> @smull_zext_v2i32_v2i64(ptr %A, ptr %B) nounwind { ; CHECK-GI-NEXT: ldr d0, [x1] ; CHECK-GI-NEXT: sshll v0.2d, v0.2s, #0 ; CHECK-GI-NEXT: fmov d1, x8 -; CHECK-GI-NEXT: mov d3, v0.d[1] +; CHECK-GI-NEXT: fmov x11, d0 ; CHECK-GI-NEXT: mov v1.d[1], x9 -; CHECK-GI-NEXT: fmov x9, d0 -; CHECK-GI-NEXT: fmov x10, d3 -; CHECK-GI-NEXT: mov d2, v1.d[1] -; CHECK-GI-NEXT: fmov x8, d1 +; CHECK-GI-NEXT: mov x9, v0.d[1] +; CHECK-GI-NEXT: fmov x10, d1 +; CHECK-GI-NEXT: mov x8, v1.d[1] +; CHECK-GI-NEXT: mul x10, x10, x11 ; CHECK-GI-NEXT: mul x8, x8, x9 -; CHECK-GI-NEXT: fmov x9, d2 -; CHECK-GI-NEXT: mul x9, x9, x10 -; CHECK-GI-NEXT: fmov d0, x8 -; CHECK-GI-NEXT: mov v0.d[1], x9 +; CHECK-GI-NEXT: fmov d0, x10 +; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: ret %load.A = load <2 x i16>, ptr %A %load.B = load <2 x i32>, ptr %B @@ -324,16 +322,14 @@ define <2 x i64> @smull_zext_and_v2i32_v2i64(ptr %A, ptr %B) nounwind { ; CHECK-GI-NEXT: ldr d1, [x1] ; CHECK-GI-NEXT: sshll v1.2d, v1.2s, #0 ; CHECK-GI-NEXT: ushll v0.2d, v0.2s, #0 -; CHECK-GI-NEXT: mov d3, v1.d[1] -; CHECK-GI-NEXT: fmov x9, d1 -; CHECK-GI-NEXT: mov d2, v0.d[1] -; CHECK-GI-NEXT: fmov x8, d0 +; CHECK-GI-NEXT: fmov x11, d1 +; CHECK-GI-NEXT: mov x9, v1.d[1] +; CHECK-GI-NEXT: fmov x10, d0 +; CHECK-GI-NEXT: mov x8, v0.d[1] +; CHECK-GI-NEXT: mul x10, x10, x11 ; CHECK-GI-NEXT: mul x8, x8, x9 -; CHECK-GI-NEXT: fmov x10, d3 -; CHECK-GI-NEXT: fmov x9, d2 -; CHECK-GI-NEXT: mul x9, x9, x10 -; CHECK-GI-NEXT: fmov d0, x8 -; CHECK-GI-NEXT: mov v0.d[1], x9 +; CHECK-GI-NEXT: fmov d0, x10 +; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: ret %load.A = load <2 x i32>, ptr %A %and.A = and <2 x i32> %load.A, @@ -1052,16 +1048,14 @@ define <2 x i64> @smull_extvec_v2i32_v2i64(<2 x i32> %arg) nounwind { ; CHECK-GI-NEXT: adrp x8, .LCPI36_0 ; CHECK-GI-NEXT: sshll v0.2d, v0.2s, #0 ; CHECK-GI-NEXT: ldr q1, [x8, :lo12:.LCPI36_0] -; CHECK-GI-NEXT: mov d2, v0.d[1] -; CHECK-GI-NEXT: mov d3, v1.d[1] -; CHECK-GI-NEXT: fmov x8, d0 -; CHECK-GI-NEXT: fmov x9, d1 +; CHECK-GI-NEXT: fmov x10, d0 +; CHECK-GI-NEXT: fmov x11, d1 +; CHECK-GI-NEXT: mov x8, v0.d[1] +; CHECK-GI-NEXT: mov x9, v1.d[1] +; CHECK-GI-NEXT: mul x10, x10, x11 ; CHECK-GI-NEXT: mul x8, x8, x9 -; CHECK-GI-NEXT: fmov x9, d2 -; CHECK-GI-NEXT: fmov x10, d3 -; CHECK-GI-NEXT: mul x9, x9, x10 -; CHECK-GI-NEXT: fmov d0, x8 -; CHECK-GI-NEXT: mov v0.d[1], x9 +; CHECK-GI-NEXT: fmov d0, x10 +; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: ret %tmp3 = sext <2 x i32> %arg to <2 x i64> %tmp4 = mul <2 x i64> %tmp3, @@ -1169,16 +1163,14 @@ define <2 x i64> @umull_extvec_v2i32_v2i64(<2 x i32> %arg) nounwind { ; CHECK-GI-NEXT: adrp x8, .LCPI40_0 ; CHECK-GI-NEXT: ushll v0.2d, v0.2s, #0 ; CHECK-GI-NEXT: ldr q1, [x8, :lo12:.LCPI40_0] -; CHECK-GI-NEXT: mov d2, v0.d[1] -; CHECK-GI-NEXT: mov d3, v1.d[1] -; CHECK-GI-NEXT: fmov x8, d0 -; CHECK-GI-NEXT: fmov x9, d1 +; CHECK-GI-NEXT: fmov x10, d0 +; CHECK-GI-NEXT: fmov x11, d1 +; CHECK-GI-NEXT: mov x8, v0.d[1] +; CHECK-GI-NEXT: mov x9, v1.d[1] +; CHECK-GI-NEXT: mul x10, x10, x11 ; CHECK-GI-NEXT: mul x8, x8, x9 -; CHECK-GI-NEXT: fmov x9, d2 -; CHECK-GI-NEXT: fmov x10, d3 -; CHECK-GI-NEXT: mul x9, x9, x10 -; CHECK-GI-NEXT: fmov d0, x8 -; CHECK-GI-NEXT: mov v0.d[1], x9 +; CHECK-GI-NEXT: fmov d0, x10 +; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: ret %tmp3 = zext <2 x i32> %arg to <2 x i64> %tmp4 = mul <2 x i64> %tmp3, @@ -1272,17 +1264,15 @@ define <2 x i64> @amull_extvec_v2i32_v2i64(<2 x i32> %arg) nounwind { ; CHECK-GI-NEXT: adrp x8, .LCPI43_0 ; CHECK-GI-NEXT: ushll v0.2d, v0.2s, #0 ; CHECK-GI-NEXT: ldr q1, [x8, :lo12:.LCPI43_0] -; CHECK-GI-NEXT: mov d2, v0.d[1] -; CHECK-GI-NEXT: mov d3, v1.d[1] -; CHECK-GI-NEXT: fmov x8, d0 -; CHECK-GI-NEXT: fmov x9, d1 +; CHECK-GI-NEXT: fmov x10, d0 +; CHECK-GI-NEXT: fmov x11, d1 +; CHECK-GI-NEXT: mov x8, v0.d[1] +; CHECK-GI-NEXT: mov x9, v1.d[1] ; CHECK-GI-NEXT: movi v1.2d, #0x000000ffffffff +; CHECK-GI-NEXT: mul x10, x10, x11 ; CHECK-GI-NEXT: mul x8, x8, x9 -; CHECK-GI-NEXT: fmov x9, d2 -; CHECK-GI-NEXT: fmov x10, d3 -; CHECK-GI-NEXT: mul x9, x9, x10 -; CHECK-GI-NEXT: fmov d0, x8 -; CHECK-GI-NEXT: mov v0.d[1], x9 +; CHECK-GI-NEXT: fmov d0, x10 +; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: and v0.16b, v0.16b, v1.16b ; CHECK-GI-NEXT: ret %tmp3 = zext <2 x i32> %arg to <2 x i64> @@ -1901,17 +1891,15 @@ define <2 x i64> @umull_and_v2i64(<2 x i32> %src1, <2 x i64> %src2) { ; CHECK-GI: // %bb.0: // %entry ; CHECK-GI-NEXT: movi v2.2d, #0x000000000000ff ; CHECK-GI-NEXT: ushll v0.2d, v0.2s, #0 -; CHECK-GI-NEXT: fmov x8, d0 +; CHECK-GI-NEXT: fmov x10, d0 +; CHECK-GI-NEXT: mov x8, v0.d[1] ; CHECK-GI-NEXT: and v1.16b, v1.16b, v2.16b -; CHECK-GI-NEXT: mov d2, v0.d[1] -; CHECK-GI-NEXT: mov d3, v1.d[1] -; CHECK-GI-NEXT: fmov x9, d1 +; CHECK-GI-NEXT: fmov x11, d1 +; CHECK-GI-NEXT: mov x9, v1.d[1] +; CHECK-GI-NEXT: mul x10, x10, x11 ; CHECK-GI-NEXT: mul x8, x8, x9 -; CHECK-GI-NEXT: fmov x9, d2 -; CHECK-GI-NEXT: fmov x10, d3 -; CHECK-GI-NEXT: mul x9, x9, x10 -; CHECK-GI-NEXT: fmov d0, x8 -; CHECK-GI-NEXT: mov v0.d[1], x9 +; CHECK-GI-NEXT: fmov d0, x10 +; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: ret entry: %in1 = zext <2 x i32> %src1 to <2 x i64> @@ -1947,26 +1935,22 @@ define <4 x i64> @umull_and_v4i64(<4 x i32> %src1, <4 x i64> %src2) { ; CHECK-GI-NEXT: ushll v4.2d, v0.2s, #0 ; CHECK-GI-NEXT: ushll2 v0.2d, v0.4s, #0 ; CHECK-GI-NEXT: fmov x8, d4 +; CHECK-GI-NEXT: mov x10, v4.d[1] +; CHECK-GI-NEXT: mov x13, v0.d[1] ; CHECK-GI-NEXT: and v1.16b, v1.16b, v3.16b ; CHECK-GI-NEXT: and v2.16b, v2.16b, v3.16b -; CHECK-GI-NEXT: mov d3, v4.d[1] ; CHECK-GI-NEXT: fmov x9, d1 -; CHECK-GI-NEXT: mov d4, v1.d[1] -; CHECK-GI-NEXT: fmov x10, d2 -; CHECK-GI-NEXT: mov d1, v0.d[1] +; CHECK-GI-NEXT: fmov x12, d2 +; CHECK-GI-NEXT: mov x11, v1.d[1] +; CHECK-GI-NEXT: mov x14, v2.d[1] ; CHECK-GI-NEXT: mul x8, x8, x9 ; CHECK-GI-NEXT: fmov x9, d0 -; CHECK-GI-NEXT: mov d0, v2.d[1] -; CHECK-GI-NEXT: fmov x11, d4 -; CHECK-GI-NEXT: mul x9, x9, x10 -; CHECK-GI-NEXT: fmov x10, d3 -; CHECK-GI-NEXT: fmov x12, d0 -; CHECK-GI-NEXT: fmov d0, x8 ; CHECK-GI-NEXT: mul x10, x10, x11 -; CHECK-GI-NEXT: fmov x11, d1 -; CHECK-GI-NEXT: fmov d1, x9 -; CHECK-GI-NEXT: mul x11, x11, x12 +; CHECK-GI-NEXT: mul x9, x9, x12 +; CHECK-GI-NEXT: fmov d0, x8 +; CHECK-GI-NEXT: mul x11, x13, x14 ; CHECK-GI-NEXT: mov v0.d[1], x10 +; CHECK-GI-NEXT: fmov d1, x9 ; CHECK-GI-NEXT: mov v1.d[1], x11 ; CHECK-GI-NEXT: ret entry: @@ -1999,20 +1983,17 @@ define <4 x i64> @umull_and_v4i64_dup(<4 x i32> %src1, i64 %src2) { ; CHECK-GI-NEXT: ushll v1.2d, v0.2s, #0 ; CHECK-GI-NEXT: ushll2 v0.2d, v0.4s, #0 ; CHECK-GI-NEXT: dup v2.2d, x8 -; CHECK-GI-NEXT: mov d3, v1.d[1] ; CHECK-GI-NEXT: fmov x8, d1 -; CHECK-GI-NEXT: fmov x10, d0 -; CHECK-GI-NEXT: mov d1, v2.d[1] +; CHECK-GI-NEXT: fmov x12, d0 +; CHECK-GI-NEXT: mov x10, v1.d[1] ; CHECK-GI-NEXT: fmov x9, d2 -; CHECK-GI-NEXT: mov d2, v0.d[1] +; CHECK-GI-NEXT: mov x11, v2.d[1] +; CHECK-GI-NEXT: mov x13, v0.d[1] ; CHECK-GI-NEXT: mul x8, x8, x9 -; CHECK-GI-NEXT: fmov x11, d1 -; CHECK-GI-NEXT: fmov x12, d2 -; CHECK-GI-NEXT: mul x9, x10, x9 -; CHECK-GI-NEXT: fmov x10, d3 +; CHECK-GI-NEXT: mul x9, x12, x9 ; CHECK-GI-NEXT: mul x10, x10, x11 ; CHECK-GI-NEXT: fmov d0, x8 -; CHECK-GI-NEXT: mul x11, x12, x11 +; CHECK-GI-NEXT: mul x11, x13, x11 ; CHECK-GI-NEXT: fmov d1, x9 ; CHECK-GI-NEXT: mov v0.d[1], x10 ; CHECK-GI-NEXT: mov v1.d[1], x11 diff --git a/llvm/test/CodeGen/AArch64/arm64-neon-copy.ll b/llvm/test/CodeGen/AArch64/arm64-neon-copy.ll index 749d6071c98d..43d5ab5ab54e 100644 --- a/llvm/test/CodeGen/AArch64/arm64-neon-copy.ll +++ b/llvm/test/CodeGen/AArch64/arm64-neon-copy.ll @@ -1488,8 +1488,7 @@ define <4 x i16> @test_dup_v2i32_v4i16(<2 x i32> %a) { ; CHECK-GI-LABEL: test_dup_v2i32_v4i16: ; CHECK-GI: // %bb.0: // %entry ; CHECK-GI-NEXT: // kill: def $d0 killed $d0 def $q0 -; CHECK-GI-NEXT: mov s0, v0.s[1] -; CHECK-GI-NEXT: fmov w8, s0 +; CHECK-GI-NEXT: mov w8, v0.s[1] ; CHECK-GI-NEXT: dup v0.4h, w8 ; CHECK-GI-NEXT: ret entry: @@ -1510,8 +1509,7 @@ define <8 x i16> @test_dup_v4i32_v8i16(<4 x i32> %a) { ; ; CHECK-GI-LABEL: test_dup_v4i32_v8i16: ; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: mov s0, v0.s[3] -; CHECK-GI-NEXT: fmov w8, s0 +; CHECK-GI-NEXT: mov w8, v0.s[3] ; CHECK-GI-NEXT: dup v0.8h, w8 ; CHECK-GI-NEXT: ret entry: @@ -1578,8 +1576,7 @@ define <8 x i16> @test_dup_v2i64_v8i16(<2 x i64> %a) { ; ; CHECK-GI-LABEL: test_dup_v2i64_v8i16: ; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: mov d0, v0.d[1] -; CHECK-GI-NEXT: fmov x8, d0 +; CHECK-GI-NEXT: mov x8, v0.d[1] ; CHECK-GI-NEXT: dup v0.8h, w8 ; CHECK-GI-NEXT: ret entry: @@ -1626,8 +1623,7 @@ define <4 x i16> @test_dup_v4i32_v4i16(<4 x i32> %a) { ; ; CHECK-GI-LABEL: test_dup_v4i32_v4i16: ; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: mov s0, v0.s[1] -; CHECK-GI-NEXT: fmov w8, s0 +; CHECK-GI-NEXT: mov w8, v0.s[1] ; CHECK-GI-NEXT: dup v0.4h, w8 ; CHECK-GI-NEXT: ret entry: diff --git a/llvm/test/CodeGen/AArch64/bitcast.ll b/llvm/test/CodeGen/AArch64/bitcast.ll index e0851fd8739e..5de99586f7fc 100644 --- a/llvm/test/CodeGen/AArch64/bitcast.ll +++ b/llvm/test/CodeGen/AArch64/bitcast.ll @@ -517,10 +517,8 @@ define <4 x i64> @bitcast_v8i32_v4i64(<8 x i32> %a, <8 x i32> %b){ ; CHECK-GI: // %bb.0: ; CHECK-GI-NEXT: add v0.4s, v0.4s, v2.4s ; CHECK-GI-NEXT: add v1.4s, v1.4s, v3.4s -; CHECK-GI-NEXT: mov d2, v0.d[1] -; CHECK-GI-NEXT: mov d3, v1.d[1] -; CHECK-GI-NEXT: fmov x8, d2 -; CHECK-GI-NEXT: fmov x9, d3 +; CHECK-GI-NEXT: mov x8, v0.d[1] +; CHECK-GI-NEXT: mov x9, v1.d[1] ; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: mov v1.d[1], x9 ; CHECK-GI-NEXT: ret @@ -578,10 +576,8 @@ define <4 x i64> @bitcast_v16i16_v4i64(<16 x i16> %a, <16 x i16> %b){ ; CHECK-GI: // %bb.0: ; CHECK-GI-NEXT: add v0.8h, v0.8h, v2.8h ; CHECK-GI-NEXT: add v1.8h, v1.8h, v3.8h -; CHECK-GI-NEXT: mov d2, v0.d[1] -; CHECK-GI-NEXT: mov d3, v1.d[1] -; CHECK-GI-NEXT: fmov x8, d2 -; CHECK-GI-NEXT: fmov x9, d3 +; CHECK-GI-NEXT: mov x8, v0.d[1] +; CHECK-GI-NEXT: mov x9, v1.d[1] ; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: mov v1.d[1], x9 ; CHECK-GI-NEXT: ret @@ -622,14 +618,10 @@ define <8 x i64> @bitcast_v16i32_v8i64(<16 x i32> %a, <16 x i32> %b){ ; CHECK-GI-NEXT: add v1.4s, v1.4s, v5.4s ; CHECK-GI-NEXT: add v2.4s, v2.4s, v6.4s ; CHECK-GI-NEXT: add v3.4s, v3.4s, v7.4s -; CHECK-GI-NEXT: mov d4, v0.d[1] -; CHECK-GI-NEXT: mov d5, v1.d[1] -; CHECK-GI-NEXT: mov d6, v2.d[1] -; CHECK-GI-NEXT: mov d7, v3.d[1] -; CHECK-GI-NEXT: fmov x8, d4 -; CHECK-GI-NEXT: fmov x9, d5 -; CHECK-GI-NEXT: fmov x10, d6 -; CHECK-GI-NEXT: fmov x11, d7 +; CHECK-GI-NEXT: mov x8, v0.d[1] +; CHECK-GI-NEXT: mov x9, v1.d[1] +; CHECK-GI-NEXT: mov x10, v2.d[1] +; CHECK-GI-NEXT: mov x11, v3.d[1] ; CHECK-GI-NEXT: mov v0.d[1], x8 ; CHECK-GI-NEXT: mov v1.d[1], x9 ; CHECK-GI-NEXT: mov v2.d[1], x10 diff --git a/llvm/test/CodeGen/AArch64/insertextract.ll b/llvm/test/CodeGen/AArch64/insertextract.ll index c6b2d07231bf..8b82004388b0 100644 --- a/llvm/test/CodeGen/AArch64/insertextract.ll +++ b/llvm/test/CodeGen/AArch64/insertextract.ll @@ -983,13 +983,12 @@ define <3 x i32> @insert_v3i32_0(<3 x i32> %a, i32 %b, i32 %c) { ; ; CHECK-GI-LABEL: insert_v3i32_0: ; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: mov s1, v0.s[1] -; CHECK-GI-NEXT: mov s2, v0.s[2] -; CHECK-GI-NEXT: fmov s0, w0 -; CHECK-GI-NEXT: fmov w8, s1 -; CHECK-GI-NEXT: mov v0.s[1], w8 -; CHECK-GI-NEXT: fmov w8, s2 -; CHECK-GI-NEXT: mov v0.s[2], w8 +; CHECK-GI-NEXT: mov w8, v0.s[1] +; CHECK-GI-NEXT: fmov s1, w0 +; CHECK-GI-NEXT: mov w9, v0.s[2] +; CHECK-GI-NEXT: mov v1.s[1], w8 +; CHECK-GI-NEXT: mov v1.s[2], w9 +; CHECK-GI-NEXT: mov v0.16b, v1.16b ; CHECK-GI-NEXT: ret entry: %d = insertelement <3 x i32> %a, i32 %b, i32 0 diff --git a/llvm/test/CodeGen/AArch64/ptradd.ll b/llvm/test/CodeGen/AArch64/ptradd.ll index 107db8723c64..af283f6a093e 100644 --- a/llvm/test/CodeGen/AArch64/ptradd.ll +++ b/llvm/test/CodeGen/AArch64/ptradd.ll @@ -81,13 +81,12 @@ define void @vector_gep_v3i32(<3 x ptr> %b, <3 x i32> %off, ptr %p) { ; CHECK-GI-NEXT: // kill: def $d0 killed $d0 def $q0 ; CHECK-GI-NEXT: // kill: def $d1 killed $d1 def $q1 ; CHECK-GI-NEXT: smov x9, v3.s[1] -; CHECK-GI-NEXT: mov s3, v3.s[2] ; CHECK-GI-NEXT: mov v0.d[1], v1.d[0] ; CHECK-GI-NEXT: fmov d1, x8 -; CHECK-GI-NEXT: fmov x8, d2 +; CHECK-GI-NEXT: mov w8, v3.s[2] ; CHECK-GI-NEXT: mov v1.d[1], x9 -; CHECK-GI-NEXT: fmov w9, s3 -; CHECK-GI-NEXT: add x8, x8, w9, sxtw +; CHECK-GI-NEXT: fmov x9, d2 +; CHECK-GI-NEXT: add x8, x9, w8, sxtw ; CHECK-GI-NEXT: add v0.2d, v0.2d, v1.2d ; CHECK-GI-NEXT: str x8, [x0, #16] ; CHECK-GI-NEXT: str q0, [x0] diff --git a/llvm/test/CodeGen/AArch64/reduce-and.ll b/llvm/test/CodeGen/AArch64/reduce-and.ll index 62ad45b21296..8ca521327c2e 100644 --- a/llvm/test/CodeGen/AArch64/reduce-and.ll +++ b/llvm/test/CodeGen/AArch64/reduce-and.ll @@ -30,10 +30,9 @@ define i1 @test_redand_v2i1(<2 x i1> %a) { ; GISEL-LABEL: test_redand_v2i1: ; GISEL: // %bb.0: ; GISEL-NEXT: // kill: def $d0 killed $d0 def $q0 -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: and w8, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: and w8, w9, w8 ; GISEL-NEXT: and w0, w8, #0x1 ; GISEL-NEXT: ret %or_result = call i1 @llvm.vector.reduce.and.v2i1(<2 x i1> %a) @@ -457,10 +456,9 @@ define i32 @test_redand_v2i32(<2 x i32> %a) { ; GISEL-LABEL: test_redand_v2i32: ; GISEL: // %bb.0: ; GISEL-NEXT: // kill: def $d0 killed $d0 def $q0 -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: and w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: and w0, w9, w8 ; GISEL-NEXT: ret %and_result = call i32 @llvm.vector.reduce.and.v2i32(<2 x i32> %a) ret i32 %and_result @@ -480,10 +478,9 @@ define i32 @test_redand_v4i32(<4 x i32> %a) { ; GISEL: // %bb.0: ; GISEL-NEXT: mov d1, v0.d[1] ; GISEL-NEXT: and v0.8b, v0.8b, v1.8b -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: and w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: and w0, w9, w8 ; GISEL-NEXT: ret %and_result = call i32 @llvm.vector.reduce.and.v4i32(<4 x i32> %a) ret i32 %and_result @@ -505,10 +502,9 @@ define i32 @test_redand_v8i32(<8 x i32> %a) { ; GISEL-NEXT: and v0.16b, v0.16b, v1.16b ; GISEL-NEXT: mov d1, v0.d[1] ; GISEL-NEXT: and v0.8b, v0.8b, v1.8b -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: and w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: and w0, w9, w8 ; GISEL-NEXT: ret %and_result = call i32 @llvm.vector.reduce.and.v8i32(<8 x i32> %a) ret i32 %and_result @@ -524,10 +520,9 @@ define i64 @test_redand_v2i64(<2 x i64> %a) { ; ; GISEL-LABEL: test_redand_v2i64: ; GISEL: // %bb.0: -; GISEL-NEXT: mov d1, v0.d[1] -; GISEL-NEXT: fmov x8, d0 -; GISEL-NEXT: fmov x9, d1 -; GISEL-NEXT: and x0, x8, x9 +; GISEL-NEXT: mov x8, v0.d[1] +; GISEL-NEXT: fmov x9, d0 +; GISEL-NEXT: and x0, x9, x8 ; GISEL-NEXT: ret %and_result = call i64 @llvm.vector.reduce.and.v2i64(<2 x i64> %a) ret i64 %and_result @@ -545,10 +540,9 @@ define i64 @test_redand_v4i64(<4 x i64> %a) { ; GISEL-LABEL: test_redand_v4i64: ; GISEL: // %bb.0: ; GISEL-NEXT: and v0.16b, v0.16b, v1.16b -; GISEL-NEXT: mov d1, v0.d[1] -; GISEL-NEXT: fmov x8, d0 -; GISEL-NEXT: fmov x9, d1 -; GISEL-NEXT: and x0, x8, x9 +; GISEL-NEXT: mov x8, v0.d[1] +; GISEL-NEXT: fmov x9, d0 +; GISEL-NEXT: and x0, x9, x8 ; GISEL-NEXT: ret %and_result = call i64 @llvm.vector.reduce.and.v4i64(<4 x i64> %a) ret i64 %and_result diff --git a/llvm/test/CodeGen/AArch64/reduce-or.ll b/llvm/test/CodeGen/AArch64/reduce-or.ll index 20c498d36fde..aac31ce8b71b 100644 --- a/llvm/test/CodeGen/AArch64/reduce-or.ll +++ b/llvm/test/CodeGen/AArch64/reduce-or.ll @@ -30,10 +30,9 @@ define i1 @test_redor_v2i1(<2 x i1> %a) { ; GISEL-LABEL: test_redor_v2i1: ; GISEL: // %bb.0: ; GISEL-NEXT: // kill: def $d0 killed $d0 def $q0 -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: orr w8, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: orr w8, w9, w8 ; GISEL-NEXT: and w0, w8, #0x1 ; GISEL-NEXT: ret %or_result = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> %a) @@ -459,10 +458,9 @@ define i32 @test_redor_v2i32(<2 x i32> %a) { ; GISEL-LABEL: test_redor_v2i32: ; GISEL: // %bb.0: ; GISEL-NEXT: // kill: def $d0 killed $d0 def $q0 -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: orr w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: orr w0, w9, w8 ; GISEL-NEXT: ret %or_result = call i32 @llvm.vector.reduce.or.v2i32(<2 x i32> %a) ret i32 %or_result @@ -482,10 +480,9 @@ define i32 @test_redor_v4i32(<4 x i32> %a) { ; GISEL: // %bb.0: ; GISEL-NEXT: mov d1, v0.d[1] ; GISEL-NEXT: orr v0.8b, v0.8b, v1.8b -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: orr w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: orr w0, w9, w8 ; GISEL-NEXT: ret %or_result = call i32 @llvm.vector.reduce.or.v4i32(<4 x i32> %a) ret i32 %or_result @@ -507,10 +504,9 @@ define i32 @test_redor_v8i32(<8 x i32> %a) { ; GISEL-NEXT: orr v0.16b, v0.16b, v1.16b ; GISEL-NEXT: mov d1, v0.d[1] ; GISEL-NEXT: orr v0.8b, v0.8b, v1.8b -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: orr w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: orr w0, w9, w8 ; GISEL-NEXT: ret %or_result = call i32 @llvm.vector.reduce.or.v8i32(<8 x i32> %a) ret i32 %or_result @@ -526,10 +522,9 @@ define i64 @test_redor_v2i64(<2 x i64> %a) { ; ; GISEL-LABEL: test_redor_v2i64: ; GISEL: // %bb.0: -; GISEL-NEXT: mov d1, v0.d[1] -; GISEL-NEXT: fmov x8, d0 -; GISEL-NEXT: fmov x9, d1 -; GISEL-NEXT: orr x0, x8, x9 +; GISEL-NEXT: mov x8, v0.d[1] +; GISEL-NEXT: fmov x9, d0 +; GISEL-NEXT: orr x0, x9, x8 ; GISEL-NEXT: ret %or_result = call i64 @llvm.vector.reduce.or.v2i64(<2 x i64> %a) ret i64 %or_result @@ -547,10 +542,9 @@ define i64 @test_redor_v4i64(<4 x i64> %a) { ; GISEL-LABEL: test_redor_v4i64: ; GISEL: // %bb.0: ; GISEL-NEXT: orr v0.16b, v0.16b, v1.16b -; GISEL-NEXT: mov d1, v0.d[1] -; GISEL-NEXT: fmov x8, d0 -; GISEL-NEXT: fmov x9, d1 -; GISEL-NEXT: orr x0, x8, x9 +; GISEL-NEXT: mov x8, v0.d[1] +; GISEL-NEXT: fmov x9, d0 +; GISEL-NEXT: orr x0, x9, x8 ; GISEL-NEXT: ret %or_result = call i64 @llvm.vector.reduce.or.v4i64(<4 x i64> %a) ret i64 %or_result diff --git a/llvm/test/CodeGen/AArch64/reduce-xor.ll b/llvm/test/CodeGen/AArch64/reduce-xor.ll index b8ca99e003b6..9a00172f9476 100644 --- a/llvm/test/CodeGen/AArch64/reduce-xor.ll +++ b/llvm/test/CodeGen/AArch64/reduce-xor.ll @@ -27,10 +27,9 @@ define i1 @test_redxor_v2i1(<2 x i1> %a) { ; GISEL-LABEL: test_redxor_v2i1: ; GISEL: // %bb.0: ; GISEL-NEXT: // kill: def $d0 killed $d0 def $q0 -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: eor w8, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: eor w8, w9, w8 ; GISEL-NEXT: and w0, w8, #0x1 ; GISEL-NEXT: ret %or_result = call i1 @llvm.vector.reduce.xor.v2i1(<2 x i1> %a) @@ -448,10 +447,9 @@ define i32 @test_redxor_v2i32(<2 x i32> %a) { ; GISEL-LABEL: test_redxor_v2i32: ; GISEL: // %bb.0: ; GISEL-NEXT: // kill: def $d0 killed $d0 def $q0 -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: eor w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: eor w0, w9, w8 ; GISEL-NEXT: ret %xor_result = call i32 @llvm.vector.reduce.xor.v2i32(<2 x i32> %a) ret i32 %xor_result @@ -471,10 +469,9 @@ define i32 @test_redxor_v4i32(<4 x i32> %a) { ; GISEL: // %bb.0: ; GISEL-NEXT: mov d1, v0.d[1] ; GISEL-NEXT: eor v0.8b, v0.8b, v1.8b -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: eor w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: eor w0, w9, w8 ; GISEL-NEXT: ret %xor_result = call i32 @llvm.vector.reduce.xor.v4i32(<4 x i32> %a) ret i32 %xor_result @@ -496,10 +493,9 @@ define i32 @test_redxor_v8i32(<8 x i32> %a) { ; GISEL-NEXT: eor v0.16b, v0.16b, v1.16b ; GISEL-NEXT: mov d1, v0.d[1] ; GISEL-NEXT: eor v0.8b, v0.8b, v1.8b -; GISEL-NEXT: mov s1, v0.s[1] -; GISEL-NEXT: fmov w8, s0 -; GISEL-NEXT: fmov w9, s1 -; GISEL-NEXT: eor w0, w8, w9 +; GISEL-NEXT: mov w8, v0.s[1] +; GISEL-NEXT: fmov w9, s0 +; GISEL-NEXT: eor w0, w9, w8 ; GISEL-NEXT: ret %xor_result = call i32 @llvm.vector.reduce.xor.v8i32(<8 x i32> %a) ret i32 %xor_result @@ -515,10 +511,9 @@ define i64 @test_redxor_v2i64(<2 x i64> %a) { ; ; GISEL-LABEL: test_redxor_v2i64: ; GISEL: // %bb.0: -; GISEL-NEXT: mov d1, v0.d[1] -; GISEL-NEXT: fmov x8, d0 -; GISEL-NEXT: fmov x9, d1 -; GISEL-NEXT: eor x0, x8, x9 +; GISEL-NEXT: mov x8, v0.d[1] +; GISEL-NEXT: fmov x9, d0 +; GISEL-NEXT: eor x0, x9, x8 ; GISEL-NEXT: ret %xor_result = call i64 @llvm.vector.reduce.xor.v2i64(<2 x i64> %a) ret i64 %xor_result @@ -536,10 +531,9 @@ define i64 @test_redxor_v4i64(<4 x i64> %a) { ; GISEL-LABEL: test_redxor_v4i64: ; GISEL: // %bb.0: ; GISEL-NEXT: eor v0.16b, v0.16b, v1.16b -; GISEL-NEXT: mov d1, v0.d[1] -; GISEL-NEXT: fmov x8, d0 -; GISEL-NEXT: fmov x9, d1 -; GISEL-NEXT: eor x0, x8, x9 +; GISEL-NEXT: mov x8, v0.d[1] +; GISEL-NEXT: fmov x9, d0 +; GISEL-NEXT: eor x0, x9, x8 ; GISEL-NEXT: ret %xor_result = call i64 @llvm.vector.reduce.xor.v4i64(<4 x i64> %a) ret i64 %xor_result -- GitLab From 31c903890a905d203de3303eaaa63063754ffbca Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 16 May 2024 11:53:11 +0900 Subject: [PATCH 059/403] [SeparateConstOffsetFromGEP] Add additional inbounds preservation tests (NFC) Adding these for NVPTX because for AMDGPU the problematic -1 case does not get reordered in the first place. --- .../NVPTX/lower-gep-reorder.ll | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll index 43dda1ae1517..ec1cbb9e61c0 100644 --- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll +++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll @@ -63,3 +63,44 @@ end: call void asm sideeffect "; use $0", "v"(ptr %idx3) ret void } + +define void @inboundsPossiblyNegative1(ptr %in.ptr, i64 %in.idx1) { +; CHECK-LABEL: define void @inboundsPossiblyNegative1( +; CHECK-SAME: ptr [[IN_PTR:%.*]], i64 [[IN_IDX1:%.*]]) { +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr <2 x i8>, ptr [[IN_PTR]], i64 [[IN_IDX1]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr <2 x i8>, ptr [[TMP0]], i64 1 +; CHECK-NEXT: ret void +; + %const1 = getelementptr inbounds <2 x i8>, ptr %in.ptr, i64 1 + %idx1 = getelementptr inbounds <2 x i8>, ptr %const1, i64 %in.idx1 + ret void +} + +define void @inboundsPossiblyNegative2(ptr %in.ptr, i64 %in.idx1) { +; CHECK-LABEL: define void @inboundsPossiblyNegative2( +; CHECK-SAME: ptr [[IN_PTR:%.*]], i64 [[IN_IDX1:%.*]]) { +; CHECK-NEXT: [[IN_IDX1_NNEG:%.*]] = and i64 [[IN_IDX1]], 9223372036854775807 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds <2 x i8>, ptr [[IN_PTR]], i64 [[IN_IDX1_NNEG]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds <2 x i8>, ptr [[TMP0]], i64 -1 +; CHECK-NEXT: ret void +; + %in.idx1.nneg = and i64 %in.idx1, 9223372036854775807 + %const1 = getelementptr inbounds <2 x i8>, ptr %in.ptr, i64 -1 + %idx1 = getelementptr inbounds <2 x i8>, ptr %const1, i64 %in.idx1.nneg + ret void +} + +define void @inboundsNonNegative(ptr %in.ptr, i64 %in.idx1) { +; CHECK-LABEL: define void @inboundsNonNegative( +; CHECK-SAME: ptr [[IN_PTR:%.*]], i64 [[IN_IDX1:%.*]]) { +; CHECK-NEXT: [[IDXPROM:%.*]] = and i64 [[IN_IDX1]], 9223372036854775807 +; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds <2 x i8>, ptr [[IN_PTR]], i64 [[IDXPROM]] +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds <2 x i8>, ptr [[TMP0]], i64 1 +; CHECK-NEXT: ret void +; + %in.idx1.nneg = and i64 %in.idx1, 9223372036854775807 + %const1 = getelementptr inbounds <2 x i8>, ptr %in.ptr, i64 1 + %idx1 = getelementptr inbounds <2 x i8>, ptr %const1, i64 %in.idx1.nneg + ret void +} + -- GitLab From b4d1a606c7492d827aff6ff0c1c109adff1253b9 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 16 May 2024 11:54:38 +0900 Subject: [PATCH 060/403] [SeparateConstOffsetFromGEP] Check correct index for non-negativity We were checking the index of GEP twice, instead of checking both GEP and PtrGEP. --- llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp | 2 +- .../SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp index 1a9eaf28f6e4..7ac1f43b7b6a 100644 --- a/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp +++ b/llvm/lib/Transforms/Scalar/SeparateConstOffsetFromGEP.cpp @@ -1001,7 +1001,7 @@ bool SeparateConstOffsetFromGEP::reorderGEP(GetElementPtrInst *GEP, auto KnownGEPIdx = computeKnownBits(GEPIdx->get(), *DL); IsChainInBounds &= KnownGEPIdx.isNonNegative(); if (IsChainInBounds) { - auto PtrGEPIdx = GEP->indices().begin(); + auto PtrGEPIdx = PtrGEP->indices().begin(); auto KnownPtrGEPIdx = computeKnownBits(PtrGEPIdx->get(), *DL); IsChainInBounds &= KnownPtrGEPIdx.isNonNegative(); } diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll index ec1cbb9e61c0..23b4a4f788ae 100644 --- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll +++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/NVPTX/lower-gep-reorder.ll @@ -80,8 +80,8 @@ define void @inboundsPossiblyNegative2(ptr %in.ptr, i64 %in.idx1) { ; CHECK-LABEL: define void @inboundsPossiblyNegative2( ; CHECK-SAME: ptr [[IN_PTR:%.*]], i64 [[IN_IDX1:%.*]]) { ; CHECK-NEXT: [[IN_IDX1_NNEG:%.*]] = and i64 [[IN_IDX1]], 9223372036854775807 -; CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds <2 x i8>, ptr [[IN_PTR]], i64 [[IN_IDX1_NNEG]] -; CHECK-NEXT: [[TMP1:%.*]] = getelementptr inbounds <2 x i8>, ptr [[TMP0]], i64 -1 +; CHECK-NEXT: [[TMP1:%.*]] = getelementptr <2 x i8>, ptr [[IN_PTR]], i64 [[IN_IDX1_NNEG]] +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr <2 x i8>, ptr [[TMP1]], i64 -1 ; CHECK-NEXT: ret void ; %in.idx1.nneg = and i64 %in.idx1, 9223372036854775807 -- GitLab From 83e61d03deaaa8f4dd8395cfa753af7b38f74b24 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 16 May 2024 12:20:18 +0900 Subject: [PATCH 061/403] [SeparateConstOffsetFromGEP] Add tests for multiple indices (NFC) --- .../AMDGPU/reorder-gep.ll | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reorder-gep.ll b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reorder-gep.ll index b4119f0b50b4..a7ca5b93c361 100644 --- a/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reorder-gep.ll +++ b/llvm/test/Transforms/SeparateConstOffsetFromGEP/AMDGPU/reorder-gep.ll @@ -284,3 +284,29 @@ entry: %idx3 = getelementptr i8, ptr addrspace(3) %const3, i64 %in.idx2 ret void } + +define void @multiple_index_maybe_neg(ptr %in.ptr, i64 %in.idx1) { +; CHECK-LABEL: define void @multiple_index_maybe_neg( +; CHECK-SAME: ptr [[IN_PTR:%.*]], i64 [[IN_IDX1:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[CONST1:%.*]] = getelementptr inbounds [2 x <2 x i8>], ptr [[IN_PTR]], i64 0, i64 1 +; CHECK-NEXT: [[IDX1:%.*]] = getelementptr inbounds [2 x <2 x i8>], ptr [[CONST1]], i64 0, i64 [[IN_IDX1]] +; CHECK-NEXT: ret void +; + %const1 = getelementptr inbounds [2 x <2 x i8>], ptr %in.ptr, i64 0, i64 1 + %idx1 = getelementptr inbounds [2 x <2 x i8>], ptr %const1, i64 0, i64 %in.idx1 + ret void +} + +define void @multiple_index_nonneg(ptr %in.ptr, i64 %in.idx1) { +; CHECK-LABEL: define void @multiple_index_nonneg( +; CHECK-SAME: ptr [[IN_PTR:%.*]], i64 [[IN_IDX1:%.*]]) #[[ATTR0]] { +; CHECK-NEXT: [[IN_IDX1_NNEG:%.*]] = and i64 [[IN_IDX1]], 9223372036854775807 +; CHECK-NEXT: [[CONST1:%.*]] = getelementptr inbounds [2 x <2 x i8>], ptr [[IN_PTR]], i64 0, i64 1 +; CHECK-NEXT: [[IDX1:%.*]] = getelementptr inbounds [2 x <2 x i8>], ptr [[CONST1]], i64 0, i64 [[IN_IDX1_NNEG]] +; CHECK-NEXT: ret void +; + %in.idx1.nneg = and i64 %in.idx1, 9223372036854775807 + %const1 = getelementptr inbounds [2 x <2 x i8>], ptr %in.ptr, i64 0, i64 1 + %idx1 = getelementptr inbounds [2 x <2 x i8>], ptr %const1, i64 0, i64 %in.idx1.nneg + ret void +} -- GitLab From e91ea1b5d88805ebf7657da57ca6a7577374e4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jim=20M=2E=20R=2E=20Teichgr=C3=A4ber?= Date: Thu, 16 May 2024 05:38:15 +0200 Subject: [PATCH 062/403] [Clang] Disallow VLA type compound literals (#91891) C99-C23 6.5.2.5 says: The type name shall specify an object type or an array of unknown size, but not a variable length array type. Fixes #89835. --- clang/docs/ReleaseNotes.rst | 3 +++ .../clang/Basic/DiagnosticSemaKinds.td | 2 ++ clang/lib/Sema/SemaExpr.cpp | 19 +++++++++++++------ clang/test/C/C2x/n2900_n3011.c | 8 +++++++- clang/test/C/C2x/n2900_n3011_2.c | 16 ---------------- clang/test/Sema/compound-literal.c | 13 ++++++++++++- 6 files changed, 37 insertions(+), 24 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 11812c355f8d..be4cded27632 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -570,6 +570,9 @@ Bug Fixes in This Version - Clang will no longer emit a duplicate -Wunused-value warning for an expression `(A, B)` which evaluates to glvalue `B` that can be converted to non ODR-use. (#GH45783) +- Clang now correctly disallows VLA type compound literals, e.g. ``(int[size]){}``, + as the C standard mandates. (#GH89835) + Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6100fba51005..e648b503ac03 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3371,6 +3371,8 @@ def err_field_with_address_space : Error< "field may not be qualified with an address space">; def err_compound_literal_with_address_space : Error< "compound literal in function scope may not be qualified with an address space">; +def err_compound_literal_with_vla_type : Error< + "compound literal cannot be of variable-length array type">; def err_address_space_mismatch_templ_inst : Error< "conflicting address space qualifiers are provided between types %0 and %1">; def err_attr_objc_ownership_redundant : Error< diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 50569c1cd536..cc507524e2fc 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -7130,12 +7130,19 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, // init a VLA in C++ in all cases (such as with non-trivial constructors). // FIXME: should we allow this construct in C++ when it makes sense to do // so? - std::optional NumInits; - if (const auto *ILE = dyn_cast(LiteralExpr)) - NumInits = ILE->getNumInits(); - if ((LangOpts.CPlusPlus || NumInits.value_or(0)) && - !tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc, - diag::err_variable_object_no_init)) + // + // But: C99-C23 6.5.2.5 Compound literals constraint 1: The type name + // shall specify an object type or an array of unknown size, but not a + // variable length array type. This seems odd, as it allows int a[size] = + // {}; but forbids int a[size] = (int[size]){}; As this is what the + // standard says, this is what's implemented here for C (except for the + // extension that permits constant foldable size arrays) + + auto diagID = LangOpts.CPlusPlus + ? diag::err_variable_object_no_init + : diag::err_compound_literal_with_vla_type; + if (!tryToFixVariablyModifiedVarType(TInfo, literalType, LParenLoc, + diagID)) return ExprError(); } } else if (!literalType->isDependentType() && diff --git a/clang/test/C/C2x/n2900_n3011.c b/clang/test/C/C2x/n2900_n3011.c index 4350aa140691..82a3b16c8acd 100644 --- a/clang/test/C/C2x/n2900_n3011.c +++ b/clang/test/C/C2x/n2900_n3011.c @@ -27,8 +27,14 @@ void test(void) { compat-warning {{use of an empty initializer is incompatible with C standards before C23}} int vla[i] = {}; // compat-warning {{use of an empty initializer is incompatible with C standards before C23}} \ pedantic-warning {{use of an empty initializer is a C23 extension}} + // C99 6.5.2.5 Compound literals constraint 1: The type name shall specify an + // object type or an array of unknown size, but not a variable length array + // type. int *compound_literal_vla = (int[i]){}; // compat-warning {{use of an empty initializer is incompatible with C standards before C23}} \ - pedantic-warning {{use of an empty initializer is a C23 extension}} + pedantic-warning {{use of an empty initializer is a C23 extension}}\ + compat-error {{compound literal cannot be of variable-length array type}} \ + pedantic-error {{compound literal cannot be of variable-length array type}}\ + struct T { int i; diff --git a/clang/test/C/C2x/n2900_n3011_2.c b/clang/test/C/C2x/n2900_n3011_2.c index eb15fbf905c8..ab659d636d15 100644 --- a/clang/test/C/C2x/n2900_n3011_2.c +++ b/clang/test/C/C2x/n2900_n3011_2.c @@ -76,22 +76,6 @@ void test_zero_size_vla() { // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[VLA]], i8 0, i64 %[[BYTES_TO_COPY]], i1 false) } -void test_compound_literal_vla() { - int num_elts = 12; - int *compound_literal_vla = (int[num_elts]){}; - // CHECK: define {{.*}} void @test_compound_literal_vla - // CHECK-NEXT: entry: - // CHECK-NEXT: %[[NUM_ELTS_PTR:.+]] = alloca i32 - // CHECK-NEXT: %[[COMP_LIT_VLA:.+]] = alloca ptr - // CHECK-NEXT: %[[COMP_LIT:.+]] = alloca i32 - // CHECK-NEXT: store i32 12, ptr %[[NUM_ELTS_PTR]] - // CHECK-NEXT: %[[NUM_ELTS:.+]] = load i32, ptr %[[NUM_ELTS_PTR]] - // CHECK-NEXT: %[[NUM_ELTS_EXT:.+]] = zext i32 %[[NUM_ELTS]] to i64 - // CHECK-NEXT: %[[BYTES_TO_COPY:.+]] = mul nuw i64 %[[NUM_ELTS_EXT]], 4 - // CHECK-NEXT: call void @llvm.memset.p0.i64(ptr {{.*}} %[[COMP_LIT]], i8 0, i64 %[[BYTES_TO_COPY]], i1 false) - // CHECK-NEXT: store ptr %[[COMP_LIT]], ptr %[[COMP_LIT_VLA]] -} - void test_nested_structs() { struct T t1 = { 1, {} }; struct T t2 = { 1, { 2, {} } }; diff --git a/clang/test/Sema/compound-literal.c b/clang/test/Sema/compound-literal.c index a64b6f9e5dfa..3ed53d670d38 100644 --- a/clang/test/Sema/compound-literal.c +++ b/clang/test/Sema/compound-literal.c @@ -29,7 +29,7 @@ int main(int argc, char **argv) { struct Incomplete; // expected-note{{forward declaration of 'struct Incomplete'}} struct Incomplete* I1 = &(struct Incomplete){1, 2, 3}; // expected-error {{variable has incomplete type}} void IncompleteFunc(unsigned x) { - struct Incomplete* I2 = (struct foo[x]){1, 2, 3}; // expected-error {{variable-sized object may not be initialized}} + struct Incomplete* I2 = (struct foo[x]){1, 2, 3}; // expected-error {{compound literal cannot be of variable-length array type}} (void){1,2,3}; // expected-error {{variable has incomplete type}} (void(void)) { 0 }; // expected-error{{illegal initializer type 'void (void)'}} } @@ -42,3 +42,14 @@ int (^block)(int) = ^(int i) { int *array = (int[]) {i, i + 2, i + 4}; return array[i]; }; + +// C99 6.5.2.5 Compound literals constraint 1: The type name shall specify an object type or an array of unknown size, but not a variable length array type. +// So check that VLA type compound literals are rejected (see https://github.com/llvm/llvm-project/issues/89835). +void vla(int n) { + int size = 5; + (void)(int[size]){}; // expected-warning {{use of an empty initializer is a C23 extension}} + // expected-error@-1 {{compound literal cannot be of variable-length array type}} + (void)(int[size]){1}; // expected-error {{compound literal cannot be of variable-length array type}} + (void)(int[size]){1,2,3}; // expected-error {{compound literal cannot be of variable-length array type}} + (void)(int[size]){1,2,3,4,5}; // expected-error {{compound literal cannot be of variable-length array type}} +} -- GitLab From 90fbc5bbcdc7d35d57157e4cc0459470d473f2ae Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 15 May 2024 20:38:55 -0700 Subject: [PATCH 063/403] [MCAsmParser] Simplify. NFC --- llvm/lib/MC/MCParser/AsmParser.cpp | 22 ++++++---------------- llvm/lib/MC/MCParser/MCAsmParser.cpp | 1 - 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 46c1caa940c5..009465d11d78 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -833,11 +833,8 @@ AsmParser::~AsmParser() { void AsmParser::printMacroInstantiations() { // Print the active macro instantiation stack. - for (std::vector::const_reverse_iterator - it = ActiveMacros.rbegin(), - ie = ActiveMacros.rend(); - it != ie; ++it) - printMessage((*it)->InstantiationLoc, SourceMgr::DK_Note, + for (MacroInstantiation *M : reverse(ActiveMacros)) + printMessage(M->InstantiationLoc, SourceMgr::DK_Note, "while in macro instantiation"); } @@ -1510,9 +1507,7 @@ bool AsmParser::parseExpression(const MCExpr *&Res, SMLoc &EndLoc) { // As a special case, we support 'a op b @ modifier' by rewriting the // expression to include the modifier. This is inefficient, but in general we // expect users to use 'a@modifier op b'. - if (Lexer.getKind() == AsmToken::At) { - Lex(); - + if (parseOptionalToken(AsmToken::At)) { if (Lexer.isNot(AsmToken::Identifier)) return TokError("unexpected symbol modifier following '@'"); @@ -2708,10 +2703,8 @@ bool AsmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) { if (Lexer.is(AsmToken::Comma)) break; - if (Lexer.is(AsmToken::Space)) { + if (parseOptionalToken(AsmToken::Space)) SpaceEaten = true; - Lexer.Lex(); // Eat spaces - } // Spaces can delimit parameters, but could also be part an expression. // If the token after a space is an operator, add the token and the next @@ -2722,9 +2715,7 @@ bool AsmParser::parseMacroArgument(MCAsmMacroArgument &MA, bool Vararg) { Lexer.Lex(); // Whitespace after an operator can be ignored. - if (Lexer.is(AsmToken::Space)) - Lexer.Lex(); - + parseOptionalToken(AsmToken::Space); continue; } } @@ -2865,8 +2856,7 @@ bool AsmParser::parseMacroArguments(const MCAsmMacro *M, return Failure; } - if (Lexer.is(AsmToken::Comma)) - Lex(); + parseOptionalToken(AsmToken::Comma); } return TokError("too many positional arguments"); diff --git a/llvm/lib/MC/MCParser/MCAsmParser.cpp b/llvm/lib/MC/MCParser/MCAsmParser.cpp index bfeba3108cb4..236585fc9082 100644 --- a/llvm/lib/MC/MCParser/MCAsmParser.cpp +++ b/llvm/lib/MC/MCParser/MCAsmParser.cpp @@ -99,7 +99,6 @@ bool MCAsmParser::TokError(const Twine &Msg, SMRange Range) { } bool MCAsmParser::Error(SMLoc L, const Twine &Msg, SMRange Range) { - MCPendingError PErr; PErr.Loc = L; Msg.toVector(PErr.Msg); -- GitLab From ce961c5607dd5c2d181117938720e410b406a49f Mon Sep 17 00:00:00 2001 From: Dmitry Vasilyev Date: Thu, 16 May 2024 07:44:08 +0400 Subject: [PATCH 064/403] [lldb] Fixed the TestFdLeak test (#92273) Use `os.devnull` instead of `/dev/null`. --- lldb/test/API/functionalities/avoids-fd-leak/TestFdLeak.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lldb/test/API/functionalities/avoids-fd-leak/TestFdLeak.py b/lldb/test/API/functionalities/avoids-fd-leak/TestFdLeak.py index e4f5cd3a03f8..c840d38df5c7 100644 --- a/lldb/test/API/functionalities/avoids-fd-leak/TestFdLeak.py +++ b/lldb/test/API/functionalities/avoids-fd-leak/TestFdLeak.py @@ -26,7 +26,7 @@ class AvoidsFdLeakTestCase(TestBase): @skipIfTargetAndroid() # Android have some other file descriptors open by the shell @skipIfDarwinEmbedded # # debugserver on ios has an extra fd open on launch def test_fd_leak_log(self): - self.do_test(["log enable -f '/dev/null' lldb commands"]) + self.do_test(["log enable -f '{}' lldb commands".format(os.devnull)]) def do_test(self, commands): self.build() -- GitLab From b11a6607cb6522c58dfbd5f54239e7daa281368e Mon Sep 17 00:00:00 2001 From: Owen Pan Date: Wed, 15 May 2024 21:01:57 -0700 Subject: [PATCH 065/403] [clang-format][NFC] Reformat with 18.1.5 --- clang/lib/Format/UnwrappedLineParser.cpp | 3 ++- clang/tools/clang-format/ClangFormat.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 4f1c2c5114e9..2236a49e4b76 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -47,7 +47,8 @@ void printLine(llvm::raw_ostream &OS, const UnwrappedLine &Line, OS << Prefix; NewLine = false; } - OS << I->Tok->Tok.getName() << "[" << "T=" << (unsigned)I->Tok->getType() + OS << I->Tok->Tok.getName() << "[" + << "T=" << (unsigned)I->Tok->getType() << ", OC=" << I->Tok->OriginalColumn << ", \"" << I->Tok->TokenText << "\"] "; for (SmallVectorImpl::const_iterator diff --git a/clang/tools/clang-format/ClangFormat.cpp b/clang/tools/clang-format/ClangFormat.cpp index 01f7c6047726..3fa5f81a3576 100644 --- a/clang/tools/clang-format/ClangFormat.cpp +++ b/clang/tools/clang-format/ClangFormat.cpp @@ -336,7 +336,8 @@ static void outputReplacementXML(StringRef Text) { static void outputReplacementsXML(const Replacements &Replaces) { for (const auto &R : Replaces) { - outs() << ""; outputReplacementXML(R.getReplacementText()); outs() << "\n"; -- GitLab From 526553b25131a69d9d6426e17c7b69c2ba27144f Mon Sep 17 00:00:00 2001 From: Yusuke MINATO Date: Thu, 16 May 2024 13:16:07 +0900 Subject: [PATCH 066/403] [flang] Add nsw flag to do-variable increment with a new option (#91579) This patch adds nsw flag to the increment of do-variables when a new option is enabled. NOTE 11.10 in the Fortran 2018 standard says they never overflow. See also the discussion in #74709 and the following discourse post. https://discourse.llvm.org/t/rfc-add-nsw-flags-to-arithmetic-integer-operations-using-the-option-fno-wrapv/77584/5 --- clang/include/clang/Driver/Options.td | 4 + clang/lib/Driver/ToolChains/Flang.cpp | 1 + flang/include/flang/Lower/LoweringOptions.def | 4 + .../flang/Optimizer/Transforms/Passes.h | 4 +- .../flang/Optimizer/Transforms/Passes.td | 5 +- flang/include/flang/Tools/CLOptions.inc | 13 +- flang/include/flang/Tools/CrossToolHelpers.h | 1 + flang/lib/Frontend/CompilerInvocation.cpp | 6 + flang/lib/Frontend/FrontendActions.cpp | 3 + flang/lib/Lower/Bridge.cpp | 12 +- flang/lib/Lower/IO.cpp | 9 +- .../Transforms/ControlFlowConverter.cpp | 44 +++- flang/test/Driver/frontend-forwarding.f90 | 2 + flang/test/Fir/loop01.fir | 211 ++++++++++++++++++ flang/test/Lower/array-substring.f90 | 40 ++++ flang/test/Lower/do_loop.f90 | 42 ++++ flang/test/Lower/do_loop_unstructured.f90 | 189 +++++++++++++++- flang/test/Lower/infinite_loop.f90 | 34 +++ flang/test/Lower/io-implied-do-fixes.f90 | 51 ++++- flang/tools/bbc/bbc.cpp | 7 + 20 files changed, 659 insertions(+), 23 deletions(-) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index e579f1a0a366..7bb781667e92 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -6550,6 +6550,10 @@ def flang_deprecated_no_hlfir : Flag<["-"], "flang-deprecated-no-hlfir">, Flags<[HelpHidden]>, Visibility<[FlangOption, FC1Option]>, HelpText<"Do not use HLFIR lowering (deprecated)">; +def flang_experimental_integer_overflow : Flag<["-"], "flang-experimental-integer-overflow">, + Flags<[HelpHidden]>, Visibility<[FlangOption, FC1Option]>, + HelpText<"Add nsw flag to internal operations such as do-variable increment (experimental)">; + //===----------------------------------------------------------------------===// // FLangOption + CoreOption + NoXarchOption //===----------------------------------------------------------------------===// diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index d275528b6905..42ca060186fd 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -139,6 +139,7 @@ void Flang::addCodegenOptions(const ArgList &Args, Args.addAllArgs(CmdArgs, {options::OPT_flang_experimental_hlfir, options::OPT_flang_deprecated_no_hlfir, + options::OPT_flang_experimental_integer_overflow, options::OPT_fno_ppc_native_vec_elem_order, options::OPT_fppc_native_vec_elem_order}); } diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def index be080a4d29d7..7594a57a2629 100644 --- a/flang/include/flang/Lower/LoweringOptions.def +++ b/flang/include/flang/Lower/LoweringOptions.def @@ -34,5 +34,9 @@ ENUM_LOWERINGOPT(NoPPCNativeVecElemOrder, unsigned, 1, 0) /// On by default. ENUM_LOWERINGOPT(Underscoring, unsigned, 1, 1) +/// If true, add nsw flags to loop variable increments. +/// Off by default. +ENUM_LOWERINGOPT(NSWOnLoopVarInc, unsigned, 1, 0) + #undef LOWERINGOPT #undef ENUM_LOWERINGOPT diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h index ae1d72a3526b..25fe61488f4f 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.h +++ b/flang/include/flang/Optimizer/Transforms/Passes.h @@ -54,6 +54,7 @@ namespace fir { std::unique_ptr createAffineDemotionPass(); std::unique_ptr createArrayValueCopyPass(fir::ArrayValueCopyOptions options = {}); +std::unique_ptr createCFGConversionPassWithNSW(); std::unique_ptr createExternalNameConversionPass(); std::unique_ptr createExternalNameConversionPass(bool appendUnderscore); @@ -89,7 +90,8 @@ createFunctionAttrPass(FunctionAttrTypes &functionAttr, bool noInfsFPMath, bool noSignedZerosFPMath, bool unsafeFPMath); void populateCfgConversionRewrites(mlir::RewritePatternSet &patterns, - bool forceLoopToExecuteOnce = false); + bool forceLoopToExecuteOnce = false, + bool setNSW = false); // declarative passes #define GEN_PASS_REGISTRATION diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td index e22c1b5f338b..622c9465754c 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -151,7 +151,10 @@ def CFGConversion : Pass<"cfg-conversion"> { let options = [ Option<"forceLoopToExecuteOnce", "always-execute-loop-body", "bool", /*default=*/"false", - "force the body of a loop to execute at least once"> + "force the body of a loop to execute at least once">, + Option<"setNSW", "set-nsw", "bool", + /*default=*/"false", + "set nsw on loop variable increment"> ]; } diff --git a/flang/include/flang/Tools/CLOptions.inc b/flang/include/flang/Tools/CLOptions.inc index cc3431d5b71d..1817dd6ca4a7 100644 --- a/flang/include/flang/Tools/CLOptions.inc +++ b/flang/include/flang/Tools/CLOptions.inc @@ -148,9 +148,14 @@ static void addCanonicalizerPassWithoutRegionSimplification( pm.addPass(mlir::createCanonicalizerPass(config)); } -inline void addCfgConversionPass(mlir::PassManager &pm) { - addNestedPassToAllTopLevelOperationsConditionally( - pm, disableCfgConversion, fir::createCFGConversion); +inline void addCfgConversionPass( + mlir::PassManager &pm, const MLIRToLLVMPassPipelineConfig &config) { + if (config.NSWOnLoopVarInc) + addNestedPassToAllTopLevelOperationsConditionally( + pm, disableCfgConversion, fir::createCFGConversionPassWithNSW); + else + addNestedPassToAllTopLevelOperationsConditionally( + pm, disableCfgConversion, fir::createCFGConversion); } inline void addAVC( @@ -290,7 +295,7 @@ inline void createDefaultFIROptimizerPassPipeline( pm.addPass(fir::createAliasTagsPass()); // convert control flow to CFG form - fir::addCfgConversionPass(pm); + fir::addCfgConversionPass(pm, pc); pm.addPass(mlir::createConvertSCFToCFPass()); pm.addPass(mlir::createCanonicalizerPass(config)); diff --git a/flang/include/flang/Tools/CrossToolHelpers.h b/flang/include/flang/Tools/CrossToolHelpers.h index f79520707714..77b68fc6187f 100644 --- a/flang/include/flang/Tools/CrossToolHelpers.h +++ b/flang/include/flang/Tools/CrossToolHelpers.h @@ -122,6 +122,7 @@ struct MLIRToLLVMPassPipelineConfig : public FlangEPCallBacks { bool NoSignedZerosFPMath = false; ///< Set no-signed-zeros-fp-math attribute for functions. bool UnsafeFPMath = false; ///< Set unsafe-fp-math attribute for functions. + bool NSWOnLoopVarInc = false; ///< Add nsw flag to loop variable increments. }; struct OffloadModuleOpts { diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index e8a8c90045d9..50c3e8b0113b 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -1206,6 +1206,12 @@ bool CompilerInvocation::createFromArgs( invoc.loweringOpts.setNoPPCNativeVecElemOrder(true); } + // -flang-experimental-integer-overflow + if (args.hasArg( + clang::driver::options::OPT_flang_experimental_integer_overflow)) { + invoc.loweringOpts.setNSWOnLoopVarInc(true); + } + // Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or // -Rpass-analysis. This will be used later when processing and outputting the // remarks generated by LLVM in ExecuteCompilerInvocation.cpp. diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp index 4341c104a69d..b1b6391f1439 100644 --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -818,6 +818,9 @@ void CodeGenAction::generateLLVMIR() { config.VScaleMax = vsr->second; } + if (ci.getInvocation().getLoweringOpts().getNSWOnLoopVarInc()) + config.NSWOnLoopVarInc = true; + // Create the pass pipeline fir::createMLIRToLLVMPassPipeline(pm, config, getCurrentFile()); (void)mlir::applyPassManagerCLOptions(pm); diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp index 596049fcfc92..afbc1122de86 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -2007,6 +2007,11 @@ private: void genFIRIncrementLoopEnd(IncrementLoopNestInfo &incrementLoopNestInfo) { assert(!incrementLoopNestInfo.empty() && "empty loop nest"); mlir::Location loc = toLocation(); + mlir::arith::IntegerOverflowFlags flags{}; + if (getLoweringOptions().getNSWOnLoopVarInc()) + flags = bitEnumSet(flags, mlir::arith::IntegerOverflowFlags::nsw); + auto iofAttr = mlir::arith::IntegerOverflowFlagsAttr::get( + builder->getContext(), flags); for (auto it = incrementLoopNestInfo.rbegin(), rend = incrementLoopNestInfo.rend(); it != rend; ++it) { @@ -2021,7 +2026,8 @@ private: builder->setInsertionPointToEnd(info.doLoop.getBody()); llvm::SmallVector results; results.push_back(builder->create( - loc, info.doLoop.getInductionVar(), info.doLoop.getStep())); + loc, info.doLoop.getInductionVar(), info.doLoop.getStep(), + iofAttr)); // Step loopVariable to help optimizations such as vectorization. // Induction variable elimination will clean up as necessary. mlir::Value step = builder->createConvert( @@ -2029,7 +2035,7 @@ private: mlir::Value loopVar = builder->create(loc, info.loopVariable); results.push_back( - builder->create(loc, loopVar, step)); + builder->create(loc, loopVar, step, iofAttr)); builder->create(loc, results); builder->setInsertionPointAfter(info.doLoop); // The loop control variable may be used after the loop. @@ -2054,7 +2060,7 @@ private: if (info.hasRealControl) value = builder->create(loc, value, step); else - value = builder->create(loc, value, step); + value = builder->create(loc, value, step, iofAttr); builder->create(loc, value, info.loopVariable); genBranch(info.headerBlock); diff --git a/flang/lib/Lower/IO.cpp b/flang/lib/Lower/IO.cpp index ed0afad9197d..97ef991cb399 100644 --- a/flang/lib/Lower/IO.cpp +++ b/flang/lib/Lower/IO.cpp @@ -928,6 +928,11 @@ static void genIoLoop(Fortran::lower::AbstractConverter &converter, Fortran::lower::StatementContext stmtCtx; fir::FirOpBuilder &builder = converter.getFirOpBuilder(); mlir::Location loc = converter.getCurrentLocation(); + mlir::arith::IntegerOverflowFlags flags{}; + if (converter.getLoweringOptions().getNSWOnLoopVarInc()) + flags = bitEnumSet(flags, mlir::arith::IntegerOverflowFlags::nsw); + auto iofAttr = + mlir::arith::IntegerOverflowFlagsAttr::get(builder.getContext(), flags); makeNextConditionalOn(builder, loc, checkResult, ok, inLoop); const auto &itemList = std::get<0>(ioImpliedDo.t); const auto &control = std::get<1>(ioImpliedDo.t); @@ -965,7 +970,7 @@ static void genIoLoop(Fortran::lower::AbstractConverter &converter, genItemList(ioImpliedDo); builder.setInsertionPointToEnd(doLoopOp.getBody()); mlir::Value result = builder.create( - loc, doLoopOp.getInductionVar(), doLoopOp.getStep()); + loc, doLoopOp.getInductionVar(), doLoopOp.getStep(), iofAttr); builder.create(loc, result); builder.setInsertionPointAfter(doLoopOp); // The loop control variable may be used after the loop. @@ -1007,7 +1012,7 @@ static void genIoLoop(Fortran::lower::AbstractConverter &converter, mlir::OpResult iterateResult = builder.getBlock()->back().getResult(0); mlir::Value inductionResult0 = iterWhileOp.getInductionVar(); auto inductionResult1 = builder.create( - loc, inductionResult0, iterWhileOp.getStep()); + loc, inductionResult0, iterWhileOp.getStep(), iofAttr); auto inductionResult = builder.create( loc, iterateResult, inductionResult1, inductionResult0); llvm::SmallVector results = {inductionResult, iterateResult}; diff --git a/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp b/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp index a62f6cde0e09..a233e7fbdcd1 100644 --- a/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp +++ b/flang/lib/Optimizer/Transforms/ControlFlowConverter.cpp @@ -43,14 +43,19 @@ class CfgLoopConv : public mlir::OpRewritePattern { public: using OpRewritePattern::OpRewritePattern; - CfgLoopConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce) + CfgLoopConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce, bool setNSW) : mlir::OpRewritePattern(ctx), - forceLoopToExecuteOnce(forceLoopToExecuteOnce) {} + forceLoopToExecuteOnce(forceLoopToExecuteOnce), setNSW(setNSW) {} mlir::LogicalResult matchAndRewrite(DoLoopOp loop, mlir::PatternRewriter &rewriter) const override { auto loc = loop.getLoc(); + mlir::arith::IntegerOverflowFlags flags{}; + if (setNSW) + flags = bitEnumSet(flags, mlir::arith::IntegerOverflowFlags::nsw); + auto iofAttr = mlir::arith::IntegerOverflowFlagsAttr::get( + rewriter.getContext(), flags); // Create the start and end blocks that will wrap the DoLoopOp with an // initalizer and an end point @@ -104,7 +109,7 @@ public: rewriter.setInsertionPointToEnd(lastBlock); auto iv = conditionalBlock->getArgument(0); mlir::Value steppedIndex = - rewriter.create(loc, iv, step); + rewriter.create(loc, iv, step, iofAttr); assert(steppedIndex && "must be a Value"); auto lastArg = conditionalBlock->getNumArguments() - 1; auto itersLeft = conditionalBlock->getArgument(lastArg); @@ -142,6 +147,7 @@ public: private: bool forceLoopToExecuteOnce; + bool setNSW; }; /// Convert `fir.if` to control-flow @@ -149,7 +155,7 @@ class CfgIfConv : public mlir::OpRewritePattern { public: using OpRewritePattern::OpRewritePattern; - CfgIfConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce) + CfgIfConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce, bool setNSW) : mlir::OpRewritePattern(ctx) {} mlir::LogicalResult @@ -214,13 +220,19 @@ class CfgIterWhileConv : public mlir::OpRewritePattern { public: using OpRewritePattern::OpRewritePattern; - CfgIterWhileConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce) - : mlir::OpRewritePattern(ctx) {} + CfgIterWhileConv(mlir::MLIRContext *ctx, bool forceLoopToExecuteOnce, + bool setNSW) + : mlir::OpRewritePattern(ctx), setNSW(setNSW) {} mlir::LogicalResult matchAndRewrite(fir::IterWhileOp whileOp, mlir::PatternRewriter &rewriter) const override { auto loc = whileOp.getLoc(); + mlir::arith::IntegerOverflowFlags flags{}; + if (setNSW) + flags = bitEnumSet(flags, mlir::arith::IntegerOverflowFlags::nsw); + auto iofAttr = mlir::arith::IntegerOverflowFlagsAttr::get( + rewriter.getContext(), flags); // Start by splitting the block containing the 'fir.do_loop' into two parts. // The part before will get the init code, the part after will be the end @@ -248,7 +260,8 @@ public: auto *terminator = lastBodyBlock->getTerminator(); rewriter.setInsertionPointToEnd(lastBodyBlock); auto step = whileOp.getStep(); - mlir::Value stepped = rewriter.create(loc, iv, step); + mlir::Value stepped = + rewriter.create(loc, iv, step, iofAttr); assert(stepped && "must be a Value"); llvm::SmallVector loopCarried; @@ -305,6 +318,9 @@ public: rewriter.replaceOp(whileOp, args); return success(); } + +private: + bool setNSW; }; /// Convert FIR structured control flow ops to CFG ops. @@ -312,10 +328,13 @@ class CfgConversion : public fir::impl::CFGConversionBase { public: using CFGConversionBase::CFGConversionBase; + CfgConversion(bool setNSW) { this->setNSW = setNSW; } + void runOnOperation() override { auto *context = &this->getContext(); mlir::RewritePatternSet patterns(context); - fir::populateCfgConversionRewrites(patterns, this->forceLoopToExecuteOnce); + fir::populateCfgConversionRewrites(patterns, this->forceLoopToExecuteOnce, + this->setNSW); mlir::ConversionTarget target(*context); target.addLegalDialect( - patterns.getContext(), forceLoopToExecuteOnce); + patterns.getContext(), forceLoopToExecuteOnce, setNSW); +} + +std::unique_ptr fir::createCFGConversionPassWithNSW() { + return std::make_unique(true); } diff --git a/flang/test/Driver/frontend-forwarding.f90 b/flang/test/Driver/frontend-forwarding.f90 index eac9773ce25c..35adb47b5686 100644 --- a/flang/test/Driver/frontend-forwarding.f90 +++ b/flang/test/Driver/frontend-forwarding.f90 @@ -19,6 +19,7 @@ ! RUN: -fversion-loops-for-stride \ ! RUN: -flang-experimental-hlfir \ ! RUN: -flang-deprecated-no-hlfir \ +! RUN: -flang-experimental-integer-overflow \ ! RUN: -fno-ppc-native-vector-element-order \ ! RUN: -fppc-native-vector-element-order \ ! RUN: -mllvm -print-before-all \ @@ -50,6 +51,7 @@ ! CHECK: "-fversion-loops-for-stride" ! CHECK: "-flang-experimental-hlfir" ! CHECK: "-flang-deprecated-no-hlfir" +! CHECK: "-flang-experimental-integer-overflow" ! CHECK: "-fno-ppc-native-vector-element-order" ! CHECK: "-fppc-native-vector-element-order" ! CHECK: "-Rpass" diff --git a/flang/test/Fir/loop01.fir b/flang/test/Fir/loop01.fir index 72ca1c3989e4..c1cbb522c378 100644 --- a/flang/test/Fir/loop01.fir +++ b/flang/test/Fir/loop01.fir @@ -1,4 +1,5 @@ // RUN: fir-opt --split-input-file --cfg-conversion %s | FileCheck %s +// RUN: fir-opt --split-input-file --cfg-conversion="set-nsw=true" %s | FileCheck %s --check-prefix=NSW func.func @x(%lb : index, %ub : index, %step : index, %b : i1, %addr : !fir.ref) { fir.do_loop %iv = %lb to %ub step %step unordered { @@ -43,6 +44,34 @@ func.func private @f2() -> i1 // CHECK: } // CHECK: func private @f2() -> i1 +// NSW: func @x(%[[VAL_0:.*]]: index, %[[VAL_1:.*]]: index, %[[VAL_2:.*]]: index, %[[VAL_3:.*]]: i1, %[[VAL_4:.*]]: !fir.ref) { +// NSW: %[[VAL_5:.*]] = arith.subi %[[VAL_1]], %[[VAL_0]] : index +// NSW: %[[VAL_6:.*]] = arith.addi %[[VAL_5]], %[[VAL_2]] : index +// NSW: %[[VAL_7:.*]] = arith.divsi %[[VAL_6]], %[[VAL_2]] : index +// NSW: br ^bb1(%[[VAL_0]], %[[VAL_7]] : index, index) +// NSW: ^bb1(%[[VAL_8:.*]]: index, %[[VAL_9:.*]]: index): +// NSW: %[[VAL_10:.*]] = arith.constant 0 : index +// NSW: %[[VAL_11:.*]] = arith.cmpi sgt, %[[VAL_9]], %[[VAL_10]] : index +// NSW: cond_br %[[VAL_11]], ^bb2, ^bb6 +// NSW: ^bb2: +// NSW: cond_br %[[VAL_3]], ^bb3, ^bb4 +// NSW: ^bb3: +// NSW: fir.store %[[VAL_8]] to %[[VAL_4]] : !fir.ref +// NSW: br ^bb5 +// NSW: ^bb4: +// NSW: %[[VAL_12:.*]] = arith.constant 0 : index +// NSW: fir.store %[[VAL_12]] to %[[VAL_4]] : !fir.ref +// NSW: br ^bb5 +// NSW: ^bb5: +// NSW: %[[VAL_13:.*]] = arith.addi %[[VAL_8]], %[[VAL_2]] overflow : index +// NSW: %[[VAL_14:.*]] = arith.constant 1 : index +// NSW: %[[VAL_15:.*]] = arith.subi %[[VAL_9]], %[[VAL_14]] : index +// NSW: br ^bb1(%[[VAL_13]], %[[VAL_15]] : index, index) +// NSW: ^bb6: +// NSW: return +// NSW: } +// NSW: func private @f2() -> i1 + // ----- func.func @x2(%lo : index, %up : index, %ok : i1) { @@ -79,6 +108,29 @@ func.func private @f3(i16) // CHECK: } // CHECK: func private @f3(i16) +// NSW: func @x2(%[[VAL_0:.*]]: index, %[[VAL_1:.*]]: index, %[[VAL_2:.*]]: i1) { +// NSW: %[[VAL_3:.*]] = arith.constant 1 : index +// NSW: br ^bb1(%[[VAL_0]], %[[VAL_2]] : index, i1) +// NSW: ^bb1(%[[VAL_4:.*]]: index, %[[VAL_5:.*]]: i1): +// NSW: %[[VAL_6:.*]] = arith.constant 0 : index +// NSW: %[[VAL_7:.*]] = arith.cmpi slt, %[[VAL_6]], %[[VAL_3]] : index +// NSW: %[[VAL_8:.*]] = arith.cmpi sle, %[[VAL_4]], %[[VAL_1]] : index +// NSW: %[[VAL_9:.*]] = arith.cmpi slt, %[[VAL_3]], %[[VAL_6]] : index +// NSW: %[[VAL_10:.*]] = arith.cmpi sle, %[[VAL_1]], %[[VAL_4]] : index +// NSW: %[[VAL_11:.*]] = arith.andi %[[VAL_7]], %[[VAL_8]] : i1 +// NSW: %[[VAL_12:.*]] = arith.andi %[[VAL_9]], %[[VAL_10]] : i1 +// NSW: %[[VAL_13:.*]] = arith.ori %[[VAL_11]], %[[VAL_12]] : i1 +// NSW: %[[VAL_14:.*]] = arith.andi %[[VAL_5]], %[[VAL_13]] : i1 +// NSW: cond_br %[[VAL_14]], ^bb2, ^bb3 +// NSW: ^bb2: +// NSW: %[[VAL_15:.*]] = fir.call @f2() : () -> i1 +// NSW: %[[VAL_16:.*]] = arith.addi %[[VAL_4]], %[[VAL_3]] overflow : index +// NSW: br ^bb1(%[[VAL_16]], %[[VAL_15]] : index, i1) +// NSW: ^bb3: +// NSW: return +// NSW: } +// NSW: func private @f3(i16) + // ----- // do_loop with an extra loop-carried value @@ -115,6 +167,29 @@ func.func @x3(%lo : index, %up : index) -> i1 { // CHECK: return %[[VAL_8]] : i1 // CHECK: } +// NSW-LABEL: func @x3( +// NSW-SAME: %[[VAL_0:.*]]: index, +// NSW-SAME: %[[VAL_1:.*]]: index) -> i1 { +// NSW: %[[VAL_2:.*]] = arith.constant 1 : index +// NSW: %[[VAL_3:.*]] = arith.constant true +// NSW: %[[VAL_4:.*]] = arith.subi %[[VAL_1]], %[[VAL_0]] : index +// NSW: %[[VAL_5:.*]] = arith.addi %[[VAL_4]], %[[VAL_2]] : index +// NSW: %[[VAL_6:.*]] = arith.divsi %[[VAL_5]], %[[VAL_2]] : index +// NSW: br ^bb1(%[[VAL_0]], %[[VAL_3]], %[[VAL_6]] : index, i1, index) +// NSW: ^bb1(%[[VAL_7:.*]]: index, %[[VAL_8:.*]]: i1, %[[VAL_9:.*]]: index): +// NSW: %[[VAL_10:.*]] = arith.constant 0 : index +// NSW: %[[VAL_11:.*]] = arith.cmpi sgt, %[[VAL_9]], %[[VAL_10]] : index +// NSW: cond_br %[[VAL_11]], ^bb2, ^bb3 +// NSW: ^bb2: +// NSW: %[[VAL_12:.*]] = fir.call @f2() : () -> i1 +// NSW: %[[VAL_13:.*]] = arith.addi %[[VAL_7]], %[[VAL_2]] overflow : index +// NSW: %[[VAL_14:.*]] = arith.constant 1 : index +// NSW: %[[VAL_15:.*]] = arith.subi %[[VAL_9]], %[[VAL_14]] : index +// NSW: br ^bb1(%[[VAL_13]], %[[VAL_12]], %[[VAL_15]] : index, i1, index) +// NSW: ^bb3: +// NSW: return %[[VAL_8]] : i1 +// NSW: } + // ----- // iterate_while with an extra loop-carried value @@ -160,6 +235,34 @@ func.func private @f4(i32) -> i1 // CHECK: } // CHECK: func private @f4(i32) -> i1 +// NSW-LABEL: func @y3( +// NSW-SAME: %[[VAL_0:.*]]: index, +// NSW-SAME: %[[VAL_1:.*]]: index) -> i1 { +// NSW: %[[VAL_2:.*]] = arith.constant 1 : index +// NSW: %[[VAL_3:.*]] = arith.constant true +// NSW: %[[VAL_4:.*]] = fir.call @f2() : () -> i1 +// NSW: br ^bb1(%[[VAL_0]], %[[VAL_3]], %[[VAL_4]] : index, i1, i1) +// NSW: ^bb1(%[[VAL_5:.*]]: index, %[[VAL_6:.*]]: i1, %[[VAL_7:.*]]: i1): +// NSW: %[[VAL_8:.*]] = arith.constant 0 : index +// NSW: %[[VAL_9:.*]] = arith.cmpi slt, %[[VAL_8]], %[[VAL_2]] : index +// NSW: %[[VAL_10:.*]] = arith.cmpi sle, %[[VAL_5]], %[[VAL_1]] : index +// NSW: %[[VAL_11:.*]] = arith.cmpi slt, %[[VAL_2]], %[[VAL_8]] : index +// NSW: %[[VAL_12:.*]] = arith.cmpi sle, %[[VAL_1]], %[[VAL_5]] : index +// NSW: %[[VAL_13:.*]] = arith.andi %[[VAL_9]], %[[VAL_10]] : i1 +// NSW: %[[VAL_14:.*]] = arith.andi %[[VAL_11]], %[[VAL_12]] : i1 +// NSW: %[[VAL_15:.*]] = arith.ori %[[VAL_13]], %[[VAL_14]] : i1 +// NSW: %[[VAL_16:.*]] = arith.andi %[[VAL_6]], %[[VAL_15]] : i1 +// NSW: cond_br %[[VAL_16]], ^bb2, ^bb3 +// NSW: ^bb2: +// NSW: %[[VAL_17:.*]] = fir.call @f2() : () -> i1 +// NSW: %[[VAL_18:.*]] = arith.addi %[[VAL_5]], %[[VAL_2]] overflow : index +// NSW: br ^bb1(%[[VAL_18]], %[[VAL_6]], %[[VAL_17]] : index, i1, i1) +// NSW: ^bb3: +// NSW: %[[VAL_19:.*]] = arith.andi %[[VAL_6]], %[[VAL_7]] : i1 +// NSW: return %[[VAL_19]] : i1 +// NSW: } +// NSW: func private @f4(i32) -> i1 + // ----- // do_loop that returns the final value of the induction @@ -196,6 +299,29 @@ func.func @x4(%lo : index, %up : index) -> index { // CHECK: return %[[VAL_6]] : index // CHECK: } +// NSW-LABEL: func @x4( +// NSW-SAME: %[[VAL_0:.*]]: index, +// NSW-SAME: %[[VAL_1:.*]]: index) -> index { +// NSW: %[[VAL_2:.*]] = arith.constant 1 : index +// NSW: %[[VAL_3:.*]] = arith.subi %[[VAL_1]], %[[VAL_0]] : index +// NSW: %[[VAL_4:.*]] = arith.addi %[[VAL_3]], %[[VAL_2]] : index +// NSW: %[[VAL_5:.*]] = arith.divsi %[[VAL_4]], %[[VAL_2]] : index +// NSW: br ^bb1(%[[VAL_0]], %[[VAL_5]] : index, index) +// NSW: ^bb1(%[[VAL_6:.*]]: index, %[[VAL_7:.*]]: index): +// NSW: %[[VAL_8:.*]] = arith.constant 0 : index +// NSW: %[[VAL_9:.*]] = arith.cmpi sgt, %[[VAL_7]], %[[VAL_8]] : index +// NSW: cond_br %[[VAL_9]], ^bb2, ^bb3 +// NSW: ^bb2: +// NSW: %[[VAL_10:.*]] = fir.convert %[[VAL_6]] : (index) -> i32 +// NSW: %[[VAL_11:.*]] = fir.call @f4(%[[VAL_10]]) : (i32) -> i1 +// NSW: %[[VAL_12:.*]] = arith.addi %[[VAL_6]], %[[VAL_2]] overflow : index +// NSW: %[[VAL_13:.*]] = arith.constant 1 : index +// NSW: %[[VAL_14:.*]] = arith.subi %[[VAL_7]], %[[VAL_13]] : index +// NSW: br ^bb1(%[[VAL_12]], %[[VAL_14]] : index, index) +// NSW: ^bb3: +// NSW: return %[[VAL_6]] : index +// NSW: } + // ----- // iterate_while that returns the final value of both inductions @@ -236,6 +362,32 @@ func.func @y4(%lo : index, %up : index) -> index { // CHECK: return %[[VAL_4]] : index // CHECK: } +// NSW-LABEL: func @y4( +// NSW-SAME: %[[VAL_0:.*]]: index, +// NSW-SAME: %[[VAL_1:.*]]: index) -> index { +// NSW: %[[VAL_2:.*]] = arith.constant 1 : index +// NSW: %[[VAL_3:.*]] = arith.constant true +// NSW: br ^bb1(%[[VAL_0]], %[[VAL_3]] : index, i1) +// NSW: ^bb1(%[[VAL_4:.*]]: index, %[[VAL_5:.*]]: i1): +// NSW: %[[VAL_6:.*]] = arith.constant 0 : index +// NSW: %[[VAL_7:.*]] = arith.cmpi slt, %[[VAL_6]], %[[VAL_2]] : index +// NSW: %[[VAL_8:.*]] = arith.cmpi sle, %[[VAL_4]], %[[VAL_1]] : index +// NSW: %[[VAL_9:.*]] = arith.cmpi slt, %[[VAL_2]], %[[VAL_6]] : index +// NSW: %[[VAL_10:.*]] = arith.cmpi sle, %[[VAL_1]], %[[VAL_4]] : index +// NSW: %[[VAL_11:.*]] = arith.andi %[[VAL_7]], %[[VAL_8]] : i1 +// NSW: %[[VAL_12:.*]] = arith.andi %[[VAL_9]], %[[VAL_10]] : i1 +// NSW: %[[VAL_13:.*]] = arith.ori %[[VAL_11]], %[[VAL_12]] : i1 +// NSW: %[[VAL_14:.*]] = arith.andi %[[VAL_5]], %[[VAL_13]] : i1 +// NSW: cond_br %[[VAL_14]], ^bb2, ^bb3 +// NSW: ^bb2: +// NSW: %[[VAL_15:.*]] = fir.convert %[[VAL_4]] : (index) -> i32 +// NSW: %[[VAL_16:.*]] = fir.call @f4(%[[VAL_15]]) : (i32) -> i1 +// NSW: %[[VAL_17:.*]] = arith.addi %[[VAL_4]], %[[VAL_2]] overflow : index +// NSW: br ^bb1(%[[VAL_17]], %[[VAL_16]] : index, i1) +// NSW: ^bb3: +// NSW: return %[[VAL_4]] : index +// NSW: } + // ----- // do_loop that returns the final induction value @@ -277,6 +429,31 @@ func.func @x5(%lo : index, %up : index) -> index { // CHECK: return %[[VAL_7]] : index // CHECK: } +// NSW-LABEL: func @x5( +// NSW-SAME: %[[VAL_0:.*]]: index, +// NSW-SAME: %[[VAL_1:.*]]: index) -> index { +// NSW: %[[VAL_2:.*]] = arith.constant 1 : index +// NSW: %[[VAL_3:.*]] = arith.constant 42 : i16 +// NSW: %[[VAL_4:.*]] = arith.subi %[[VAL_1]], %[[VAL_0]] : index +// NSW: %[[VAL_5:.*]] = arith.addi %[[VAL_4]], %[[VAL_2]] : index +// NSW: %[[VAL_6:.*]] = arith.divsi %[[VAL_5]], %[[VAL_2]] : index +// NSW: br ^bb1(%[[VAL_0]], %[[VAL_3]], %[[VAL_6]] : index, i16, index) +// NSW: ^bb1(%[[VAL_7:.*]]: index, %[[VAL_8:.*]]: i16, %[[VAL_9:.*]]: index): +// NSW: %[[VAL_10:.*]] = arith.constant 0 : index +// NSW: %[[VAL_11:.*]] = arith.cmpi sgt, %[[VAL_9]], %[[VAL_10]] : index +// NSW: cond_br %[[VAL_11]], ^bb2, ^bb3 +// NSW: ^bb2: +// NSW: %[[VAL_12:.*]] = fir.call @f2() : () -> i1 +// NSW: %[[VAL_13:.*]] = fir.convert %[[VAL_12]] : (i1) -> i16 +// NSW: %[[VAL_14:.*]] = arith.addi %[[VAL_7]], %[[VAL_2]] overflow : index +// NSW: %[[VAL_15:.*]] = arith.constant 1 : index +// NSW: %[[VAL_16:.*]] = arith.subi %[[VAL_9]], %[[VAL_15]] : index +// NSW: br ^bb1(%[[VAL_14]], %[[VAL_13]], %[[VAL_16]] : index, i16, index) +// NSW: ^bb3: +// NSW: fir.call @f3(%[[VAL_8]]) : (i16) -> () +// NSW: return %[[VAL_7]] : index +// NSW: } + // ----- // iterate_while that returns the both induction values @@ -331,3 +508,37 @@ func.func @y5(%lo : index, %up : index) -> index { // CHECK: fir.call @f3(%[[VAL_7]]) : (i16) -> () // CHECK: return %[[VAL_5]] : index // CHECK: } + +// NSW-LABEL: func @y5( +// NSW-SAME: %[[VAL_0:.*]]: index, +// NSW-SAME: %[[VAL_1:.*]]: index) -> index { +// NSW: %[[VAL_2:.*]] = arith.constant 1 : index +// NSW: %[[VAL_3:.*]] = arith.constant 42 : i16 +// NSW: %[[VAL_4:.*]] = arith.constant true +// NSW: br ^bb1(%[[VAL_0]], %[[VAL_4]], %[[VAL_3]] : index, i1, i16) +// NSW: ^bb1(%[[VAL_5:.*]]: index, %[[VAL_6:.*]]: i1, %[[VAL_7:.*]]: i16): +// NSW: %[[VAL_8:.*]] = arith.constant 0 : index +// NSW: %[[VAL_9:.*]] = arith.cmpi slt, %[[VAL_8]], %[[VAL_2]] : index +// NSW: %[[VAL_10:.*]] = arith.cmpi sle, %[[VAL_5]], %[[VAL_1]] : index +// NSW: %[[VAL_11:.*]] = arith.cmpi slt, %[[VAL_2]], %[[VAL_8]] : index +// NSW: %[[VAL_12:.*]] = arith.cmpi sle, %[[VAL_1]], %[[VAL_5]] : index +// NSW: %[[VAL_13:.*]] = arith.andi %[[VAL_9]], %[[VAL_10]] : i1 +// NSW: %[[VAL_14:.*]] = arith.andi %[[VAL_11]], %[[VAL_12]] : i1 +// NSW: %[[VAL_15:.*]] = arith.ori %[[VAL_13]], %[[VAL_14]] : i1 +// NSW: %[[VAL_16:.*]] = arith.andi %[[VAL_6]], %[[VAL_15]] : i1 +// NSW: cond_br %[[VAL_16]], ^bb2, ^bb3 +// NSW: ^bb2: +// NSW: %[[VAL_17:.*]] = fir.call @f2() : () -> i1 +// NSW: %[[VAL_18:.*]] = fir.convert %[[VAL_17]] : (i1) -> i16 +// NSW: %[[VAL_19:.*]] = arith.addi %[[VAL_5]], %[[VAL_2]] overflow : index +// NSW: br ^bb1(%[[VAL_19]], %[[VAL_17]], %[[VAL_18]] : index, i1, i16) +// NSW: ^bb3: +// NSW: cond_br %[[VAL_6]], ^bb4, ^bb5 +// NSW: ^bb4: +// NSW: %[[VAL_20:.*]] = arith.constant 0 : i32 +// NSW: %[[VAL_21:.*]] = fir.call @f4(%[[VAL_20]]) : (i32) -> i1 +// NSW: br ^bb5 +// NSW: ^bb5: +// NSW: fir.call @f3(%[[VAL_7]]) : (i16) -> () +// NSW: return %[[VAL_5]] : index +// NSW: } diff --git a/flang/test/Lower/array-substring.f90 b/flang/test/Lower/array-substring.f90 index 421c4b28ac8f..2e283997e3e0 100644 --- a/flang/test/Lower/array-substring.f90 +++ b/flang/test/Lower/array-substring.f90 @@ -1,4 +1,5 @@ ! RUN: bbc -hlfir=false %s -o - | FileCheck %s +! RUN: bbc -hlfir=false -integer-overflow %s -o - | FileCheck %s --check-prefix=NSW ! CHECK-LABEL: func @_QPtest( ! CHECK-SAME: %[[VAL_0:.*]]: !fir.boxchar<1>{{.*}}) -> !fir.array<1x!fir.logical<4>> { @@ -45,3 +46,42 @@ function test(C) test = C(1:1)(1:8) == (/'ABCDabcd'/) end function test + +! NSW-LABEL: func @_QPtest( +! NSW-SAME: %[[VAL_0:.*]]: !fir.boxchar<1>{{.*}}) -> !fir.array<1x!fir.logical<4>> { +! NSW-DAG: %[[VAL_1:.*]] = arith.constant 1 : index +! NSW-DAG: %[[VAL_2:.*]] = arith.constant 0 : index +! NSW-DAG: %[[VAL_3:.*]] = arith.constant 0 : i32 +! NSW-DAG: %[[VAL_4:.*]] = arith.constant 8 : index +! NSW: %[[VAL_6:.*]]:2 = fir.unboxchar %[[VAL_0]] : (!fir.boxchar<1>) -> (!fir.ref>, index) +! NSW: %[[VAL_7:.*]] = fir.convert %[[VAL_6]]#0 : (!fir.ref>) -> !fir.ref>> +! NSW: %[[VAL_8:.*]] = fir.alloca !fir.array<1x!fir.logical<4>> {bindc_name = "test", uniq_name = "_QFtestEtest"} +! NSW: %[[VAL_9:.*]] = fir.shape %[[VAL_1]] : (index) -> !fir.shape<1> +! NSW: %[[VAL_10:.*]] = fir.slice %[[VAL_1]], %[[VAL_1]], %[[VAL_1]] : (index, index, index) -> !fir.slice<1> +! NSW: %[[VAL_11:.*]] = fir.address_of(@_QQ{{.*}}) : !fir.ref>> +! NSW: br ^bb1(%[[VAL_2]], %[[VAL_1]] : index, index) +! NSW: ^bb1(%[[VAL_12:.*]]: index, %[[VAL_13:.*]]: index): +! NSW: %[[VAL_14:.*]] = arith.cmpi sgt, %[[VAL_13]], %[[VAL_2]] : index +! NSW: cond_br %[[VAL_14]], ^bb2, ^bb3 +! NSW: ^bb2: +! NSW: %[[VAL_15:.*]] = arith.addi %[[VAL_12]], %[[VAL_1]] : index +! NSW: %[[VAL_16:.*]] = fir.array_coor %[[VAL_7]](%[[VAL_9]]) {{\[}}%[[VAL_10]]] %[[VAL_15]] : (!fir.ref>>, !fir.shape<1>, !fir.slice<1>, index) -> !fir.ref> +! NSW: %[[VAL_17:.*]] = fir.convert %[[VAL_16]] : (!fir.ref>) -> !fir.ref>> +! NSW: %[[VAL_18:.*]] = fir.coordinate_of %[[VAL_17]], %[[VAL_2]] : (!fir.ref>>, index) -> !fir.ref> +! NSW: %[[VAL_19:.*]] = fir.convert %[[VAL_18]] : (!fir.ref>) -> !fir.ref> +! NSW: %[[VAL_20:.*]] = fir.array_coor %[[VAL_11]](%[[VAL_9]]) %[[VAL_15]] : (!fir.ref>>, !fir.shape<1>, index) -> !fir.ref> +! NSW: %[[VAL_21:.*]] = fir.convert %[[VAL_19]] : (!fir.ref>) -> !fir.ref +! NSW: %[[VAL_22:.*]] = fir.convert %[[VAL_20]] : (!fir.ref>) -> !fir.ref +! NSW: %[[VAL_23:.*]] = fir.convert %[[VAL_4]] : (index) -> i64 +! NSW: %[[VAL_24:.*]] = fir.call @_FortranACharacterCompareScalar1(%[[VAL_21]], %[[VAL_22]], %[[VAL_23]], %[[VAL_23]]) {{.*}}: (!fir.ref, !fir.ref, i64, i64) -> i32 +! NSW: %[[VAL_25:.*]] = arith.cmpi eq, %[[VAL_24]], %[[VAL_3]] : i32 +! NSW: %[[VAL_26:.*]] = fir.convert %[[VAL_25]] : (i1) -> !fir.logical<4> +! NSW: %[[VAL_27:.*]] = fir.array_coor %[[VAL_8]](%[[VAL_9]]) %[[VAL_15]] : (!fir.ref>>, !fir.shape<1>, index) -> !fir.ref> +! NSW: fir.store %[[VAL_26]] to %[[VAL_27]] : !fir.ref> +! NSW: %[[VAL_15_NSW:.*]] = arith.addi %[[VAL_12]], %[[VAL_1]] overflow : index +! NSW: %[[VAL_28:.*]] = arith.subi %[[VAL_13]], %[[VAL_1]] : index +! NSW: br ^bb1(%[[VAL_15_NSW]], %[[VAL_28]] : index, index) +! NSW: ^bb3: +! NSW: %[[VAL_29:.*]] = fir.load %[[VAL_8]] : !fir.ref>> +! NSW: return %[[VAL_29]] : !fir.array<1x!fir.logical<4>> +! NSW: } diff --git a/flang/test/Lower/do_loop.f90 b/flang/test/Lower/do_loop.f90 index d9c83658ee25..a46e6c947391 100644 --- a/flang/test/Lower/do_loop.f90 +++ b/flang/test/Lower/do_loop.f90 @@ -1,5 +1,6 @@ ! RUN: bbc --use-desc-for-alloc=false -emit-fir -hlfir=false -o - %s | FileCheck %s ! RUN: %flang_fc1 -mllvm --use-desc-for-alloc=false -emit-fir -flang-deprecated-no-hlfir -o - %s | FileCheck %s +! RUN: %flang_fc1 -mllvm --use-desc-for-alloc=false -emit-fir -flang-deprecated-no-hlfir -flang-experimental-integer-overflow -o - %s | FileCheck %s --check-prefix=NSW ! Simple tests for structured ordered loops with loop-control. ! Tests the structure of the loop, storage to index variable and return and @@ -7,8 +8,10 @@ ! Test a simple loop with the final value of the index variable read outside the loop ! CHECK-LABEL: simple_loop +! NSW-LABEL: simple_loop subroutine simple_loop ! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsimple_loopEi"} + ! NSW: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsimple_loopEi"} integer :: i ! CHECK: %[[C1:.*]] = arith.constant 1 : i32 @@ -18,14 +21,18 @@ subroutine simple_loop ! CHECK: %[[C1:.*]] = arith.constant 1 : index ! CHECK: %[[LB:.*]] = fir.convert %[[C1_CVT]] : (index) -> i32 ! CHECK: %[[LI_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = + ! NSW: %[[LI_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = ! CHECK-SAME: %[[C1_CVT]] to %[[C5_CVT]] step %[[C1]] ! CHECK-SAME: iter_args(%[[IV:.*]] = %[[LB]]) -> (index, i32) { do i=1,5 ! CHECK: fir.store %[[IV]] to %[[I_REF]] : !fir.ref ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[C1]] : index + ! NSW: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[C1:.*]] overflow : index ! CHECK: %[[STEPCAST:.*]] = fir.convert %[[C1]] : (index) -> i32 ! CHECK: %[[IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref + ! NSW: %[[IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref ! CHECK: %[[IVINC:.*]] = arith.addi %[[IVLOAD]], %[[STEPCAST]] : i32 + ! NSW: %[[IVINC:.*]] = arith.addi %[[IVLOAD]], %[[STEPCAST:.*]] overflow : i32 ! CHECK: fir.result %[[LI_NEXT]], %[[IVINC]] : index, i32 ! CHECK: } end do @@ -37,11 +44,14 @@ end subroutine ! Test a 2-nested loop with a body composed of a reduction. Values are read from a 2d array. ! CHECK-LABEL: nested_loop +! NSW-LABEL: nested_loop subroutine nested_loop ! CHECK: %[[ARR_REF:.*]] = fir.alloca !fir.array<5x5xi32> {bindc_name = "arr", uniq_name = "_QFnested_loopEarr"} ! CHECK: %[[ASUM_REF:.*]] = fir.alloca i32 {bindc_name = "asum", uniq_name = "_QFnested_loopEasum"} ! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFnested_loopEi"} + ! NSW: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFnested_loopEi"} ! CHECK: %[[J_REF:.*]] = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFnested_loopEj"} + ! NSW: %[[J_REF:.*]] = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFnested_loopEj"} integer :: asum, arr(5,5) integer :: i, j asum = 0 @@ -52,6 +62,7 @@ subroutine nested_loop ! CHECK: %[[ST_I:.*]] = arith.constant 1 : index ! CHECK: %[[I_LB:.*]] = fir.convert %[[S_I_CVT]] : (index) -> i32 ! CHECK: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = + ! NSW: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = ! CHECK-SAME: %[[S_I_CVT]] to %[[E_I_CVT]] step %[[ST_I]] ! CHECK-SAME: iter_args(%[[I_IV:.*]] = %[[I_LB]]) -> (index, i32) { do i=1,5 @@ -63,6 +74,7 @@ subroutine nested_loop ! CHECK: %[[ST_J:.*]] = arith.constant 1 : index ! CHECK: %[[J_LB:.*]] = fir.convert %[[S_J_CVT]] : (index) -> i32 ! CHECK: %[[J_RES:.*]]:2 = fir.do_loop %[[LJ:[^ ]*]] = + ! NSW: %[[J_RES:.*]]:2 = fir.do_loop %[[LJ:[^ ]*]] = ! CHECK-SAME: %[[S_J_CVT]] to %[[E_J_CVT]] step %[[ST_J]] ! CHECK-SAME: iter_args(%[[J_IV:.*]] = %[[J_LB]]) -> (index, i32) { do j=1,5 @@ -82,17 +94,23 @@ subroutine nested_loop ! CHECK: fir.store %[[ASUM_NEW]] to %[[ASUM_REF]] : !fir.ref asum = asum + arr(i,j) ! CHECK: %[[LJ_NEXT:.*]] = arith.addi %[[LJ]], %[[ST_J]] : index + ! NSW: %[[LJ_NEXT:.*]] = arith.addi %[[LJ]], %[[ST_J:.*]] overflow : index ! CHECK: %[[J_STEPCAST:.*]] = fir.convert %[[ST_J]] : (index) -> i32 ! CHECK: %[[J_IVLOAD:.*]] = fir.load %[[J_REF]] : !fir.ref + ! NSW: %[[J_IVLOAD:.*]] = fir.load %[[J_REF]] : !fir.ref ! CHECK: %[[J_IVINC:.*]] = arith.addi %[[J_IVLOAD]], %[[J_STEPCAST]] : i32 + ! NSW: %[[J_IVINC:.*]] = arith.addi %[[J_IVLOAD]], %[[J_STEPCAST:.*]] overflow : i32 ! CHECK: fir.result %[[LJ_NEXT]], %[[J_IVINC]] : index, i32 ! CHECK: } end do ! CHECK: fir.store %[[J_RES]]#1 to %[[J_REF]] : !fir.ref ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_I]] : index + ! NSW: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_I:.*]] overflow : index ! CHECK: %[[I_STEPCAST:.*]] = fir.convert %[[ST_I]] : (index) -> i32 ! CHECK: %[[I_IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref + ! NSW: %[[I_IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref ! CHECK: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST]] : i32 + ! NSW: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST:.*]] overflow : i32 ! CHECK: fir.result %[[LI_NEXT]], %[[I_IVINC]] : index, i32 ! CHECK: } end do @@ -101,9 +119,11 @@ end subroutine ! Test a downcounting loop ! CHECK-LABEL: down_counting_loop +! NSW-LABEL: down_counting_loop subroutine down_counting_loop() integer :: i ! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFdown_counting_loopEi"} + ! NSW: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFdown_counting_loopEi"} ! CHECK: %[[C5:.*]] = arith.constant 5 : i32 ! CHECK: %[[C5_CVT:.*]] = fir.convert %[[C5]] : (i32) -> index @@ -113,14 +133,18 @@ subroutine down_counting_loop() ! CHECK: %[[CMINUS1_STEP_CVT:.*]] = fir.convert %[[CMINUS1]] : (i32) -> index ! CHECK: %[[I_LB:.*]] = fir.convert %[[C5_CVT]] : (index) -> i32 ! CHECK: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = + ! NSW: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = ! CHECK-SAME: %[[C5_CVT]] to %[[C1_CVT]] step %[[CMINUS1_STEP_CVT]] ! CHECK-SAME: iter_args(%[[I_IV:.*]] = %[[I_LB]]) -> (index, i32) { do i=5,1,-1 ! CHECK: fir.store %[[I_IV]] to %[[I_REF]] : !fir.ref ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[CMINUS1_STEP_CVT]] : index + ! NSW: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[CMINUS1_STEP_CVT:.*]] overflow : index ! CHECK: %[[I_STEPCAST:.*]] = fir.convert %[[CMINUS1_STEP_CVT]] : (index) -> i32 ! CHECK: %[[I_IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref + ! NSW: %[[I_IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref ! CHECK: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST]] : i32 + ! NSW: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST:.*]] overflow : i32 ! CHECK: fir.result %[[LI_NEXT]], %[[I_IVINC]] : index, i32 ! CHECK: } end do @@ -129,6 +153,7 @@ end subroutine ! Test a general loop with a variable step ! CHECK-LABEL: loop_with_variable_step +! NSW-LABEL: loop_with_variable_step ! CHECK-SAME: (%[[S_REF:.*]]: !fir.ref {fir.bindc_name = "s"}, %[[E_REF:.*]]: !fir.ref {fir.bindc_name = "e"}, %[[ST_REF:.*]]: !fir.ref {fir.bindc_name = "st"}) { subroutine loop_with_variable_step(s,e,st) integer :: s, e, st @@ -141,14 +166,18 @@ subroutine loop_with_variable_step(s,e,st) ! CHECK: %[[ST_CVT:.*]] = fir.convert %[[ST]] : (i32) -> index ! CHECK: %[[I_LB:.*]] = fir.convert %[[S_CVT]] : (index) -> i32 ! CHECK: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = + ! NSW: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = ! CHECK-SAME: %[[S_CVT]] to %[[E_CVT]] step %[[ST_CVT]] ! CHECK-SAME: iter_args(%[[I_IV:.*]] = %[[I_LB]]) -> (index, i32) { do i=s,e,st ! CHECK: fir.store %[[I_IV]] to %[[I_REF]] : !fir.ref ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT]] : index + ! NSW: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT:.*]] overflow : index ! CHECK: %[[I_STEPCAST:.*]] = fir.convert %[[ST_CVT]] : (index) -> i32 ! CHECK: %[[I_IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref + ! NSW: %[[I_IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref ! CHECK: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST]] : i32 + ! NSW: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST:.*]] overflow : i32 ! CHECK: fir.result %[[LI_NEXT]], %[[I_IVINC]] : index, i32 ! CHECK: } end do @@ -157,11 +186,13 @@ end subroutine ! Test usage of pointer variables as index, start, end and step variables ! CHECK-LABEL: loop_with_pointer_variables +! NSW-LABEL: loop_with_pointer_variables ! CHECK-SAME: (%[[S_REF:.*]]: !fir.ref {fir.bindc_name = "s", fir.target}, %[[E_REF:.*]]: !fir.ref {fir.bindc_name = "e", fir.target}, %[[ST_REF:.*]]: !fir.ref {fir.bindc_name = "st", fir.target}) { subroutine loop_with_pointer_variables(s,e,st) ! CHECK: %[[E_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEeptr.addr"} ! CHECK: %[[I_REF:.*]] = fir.alloca i32 {bindc_name = "i", fir.target, uniq_name = "_QFloop_with_pointer_variablesEi"} ! CHECK: %[[I_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEiptr.addr"} +! NSW: %[[I_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEiptr.addr"} ! CHECK: %[[S_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEsptr.addr"} ! CHECK: %[[ST_PTR_REF:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFloop_with_pointer_variablesEstptr.addr"} integer, target :: i @@ -182,6 +213,7 @@ subroutine loop_with_pointer_variables(s,e,st) stptr => st ! CHECK: %[[I_PTR:.*]] = fir.load %[[I_PTR_REF]] : !fir.ref> +! NSW: %[[I_PTR:.*]] = fir.load %[[I_PTR_REF]] : !fir.ref> ! CHECK: %[[S_PTR:.*]] = fir.load %[[S_PTR_REF]] : !fir.ref> ! CHECK: %[[S:.*]] = fir.load %[[S_PTR]] : !fir.ptr ! CHECK: %[[S_CVT:.*]] = fir.convert %[[S]] : (i32) -> index @@ -193,14 +225,18 @@ subroutine loop_with_pointer_variables(s,e,st) ! CHECK: %[[ST_CVT:.*]] = fir.convert %[[ST]] : (i32) -> index ! CHECK: %[[I_LB:.*]] = fir.convert %[[S_CVT]] : (index) -> i32 ! CHECK: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = +! NSW: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = ! CHECK-SAME: %[[S_CVT]] to %[[E_CVT]] step %[[ST_CVT]] ! CHECK-SAME: iter_args(%[[I_IV:.*]] = %[[I_LB]]) -> (index, i32) { do iptr=sptr,eptr,stptr ! CHECK: fir.store %[[I_IV]] to %[[I_PTR]] : !fir.ptr ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT]] : index +! NSW: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT:.*]] overflow : index ! CHECK: %[[I_STEPCAST:.*]] = fir.convert %[[ST_CVT]] : (index) -> i32 ! CHECK: %[[I_IVLOAD:.*]] = fir.load %[[I_PTR]] : !fir.ptr +! NSW: %[[I_IVLOAD:.*]] = fir.load %[[I_PTR]] : !fir.ptr ! CHECK: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST]] : i32 +! NSW: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST:.*]] overflow : i32 ! CHECK: fir.result %[[LI_NEXT]], %[[I_IVINC]] : index, i32 end do ! CHECK: } @@ -209,9 +245,11 @@ end subroutine ! Test usage of non-default integer kind for loop control and loop index variable ! CHECK-LABEL: loop_with_non_default_integer +! NSW-LABEL: loop_with_non_default_integer ! CHECK-SAME: (%[[S_REF:.*]]: !fir.ref {fir.bindc_name = "s"}, %[[E_REF:.*]]: !fir.ref {fir.bindc_name = "e"}, %[[ST_REF:.*]]: !fir.ref {fir.bindc_name = "st"}) { subroutine loop_with_non_default_integer(s,e,st) ! CHECK: %[[I_REF:.*]] = fir.alloca i64 {bindc_name = "i", uniq_name = "_QFloop_with_non_default_integerEi"} + ! NSW: %[[I_REF:.*]] = fir.alloca i64 {bindc_name = "i", uniq_name = "_QFloop_with_non_default_integerEi"} integer(kind=8):: i ! CHECK: %[[S:.*]] = fir.load %[[S_REF]] : !fir.ref ! CHECK: %[[S_CVT:.*]] = fir.convert %[[S]] : (i64) -> index @@ -223,14 +261,18 @@ subroutine loop_with_non_default_integer(s,e,st) ! CHECK: %[[I_LB:.*]] = fir.convert %[[S_CVT]] : (index) -> i64 ! CHECK: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = + ! NSW: %[[I_RES:.*]]:2 = fir.do_loop %[[LI:[^ ]*]] = ! CHECK-SAME: %[[S_CVT]] to %[[E_CVT]] step %[[ST_CVT]] ! CHECK-SAME: iter_args(%[[I_IV:.*]] = %[[I_LB]]) -> (index, i64) { do i=s,e,st ! CHECK: fir.store %[[I_IV]] to %[[I_REF]] : !fir.ref ! CHECK: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT]] : index + ! NSW: %[[LI_NEXT:.*]] = arith.addi %[[LI]], %[[ST_CVT:.*]] overflow : index ! CHECK: %[[I_STEPCAST:.*]] = fir.convert %[[ST_CVT]] : (index) -> i64 ! CHECK: %[[I_IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref + ! NSW: %[[I_IVLOAD:.*]] = fir.load %[[I_REF]] : !fir.ref ! CHECK: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST]] : i64 + ! NSW: %[[I_IVINC:.*]] = arith.addi %[[I_IVLOAD]], %[[I_STEPCAST:.*]] overflow : i64 ! CHECK: fir.result %[[LI_NEXT]], %[[I_IVINC]] : index, i64 end do ! CHECK: } diff --git a/flang/test/Lower/do_loop_unstructured.f90 b/flang/test/Lower/do_loop_unstructured.f90 index c6bdd4b64ce3..e1a669e09c9a 100644 --- a/flang/test/Lower/do_loop_unstructured.f90 +++ b/flang/test/Lower/do_loop_unstructured.f90 @@ -1,5 +1,6 @@ ! RUN: bbc -emit-fir -hlfir=false -o - %s | FileCheck %s ! RUN: %flang_fc1 -emit-fir -flang-deprecated-no-hlfir -o - %s | FileCheck %s +! RUN: %flang_fc1 -emit-fir -flang-deprecated-no-hlfir -flang-experimental-integer-overflow -o - %s | FileCheck %s --check-prefix=NSW ! Tests for unstructured loops. @@ -44,6 +45,36 @@ end subroutine ! CHECK: ^[[EXIT]]: ! CHECK: return +! NSW-LABEL: simple_unstructured +! NSW: %[[TRIP_VAR_REF:.*]] = fir.alloca i32 +! NSW: %[[LOOP_VAR_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsimple_unstructuredEi"} +! NSW: %[[ONE:.*]] = arith.constant 1 : i32 +! NSW: %[[HUNDRED:.*]] = arith.constant 100 : i32 +! NSW: %[[STEP_ONE:.*]] = arith.constant 1 : i32 +! NSW: %[[TMP1:.*]] = arith.subi %[[HUNDRED]], %[[ONE]] : i32 +! NSW: %[[TMP2:.*]] = arith.addi %[[TMP1]], %[[STEP_ONE]] : i32 +! NSW: %[[TRIP_COUNT:.*]] = arith.divsi %[[TMP2]], %[[STEP_ONE]] : i32 +! NSW: fir.store %[[TRIP_COUNT]] to %[[TRIP_VAR_REF]] : !fir.ref +! NSW: fir.store %[[ONE]] to %[[LOOP_VAR_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER:.*]] +! NSW: ^[[HEADER]]: +! NSW: %[[TRIP_VAR:.*]] = fir.load %[[TRIP_VAR_REF]] : !fir.ref +! NSW: %[[ZERO:.*]] = arith.constant 0 : i32 +! NSW: %[[COND:.*]] = arith.cmpi sgt, %[[TRIP_VAR]], %[[ZERO]] : i32 +! NSW: cf.cond_br %[[COND]], ^[[BODY:.*]], ^[[EXIT:.*]] +! NSW: ^[[BODY]]: +! NSW: %[[TRIP_VAR:.*]] = fir.load %[[TRIP_VAR_REF]] : !fir.ref +! NSW: %[[ONE_1:.*]] = arith.constant 1 : i32 +! NSW: %[[TRIP_VAR_NEXT:.*]] = arith.subi %[[TRIP_VAR]], %[[ONE_1]] : i32 +! NSW: fir.store %[[TRIP_VAR_NEXT]] to %[[TRIP_VAR_REF]] : !fir.ref +! NSW: %[[LOOP_VAR:.*]] = fir.load %[[LOOP_VAR_REF]] : !fir.ref +! NSW: %[[STEP_ONE_2:.*]] = arith.constant 1 : i32 +! NSW: %[[LOOP_VAR_NEXT:.*]] = arith.addi %[[LOOP_VAR]], %[[STEP_ONE_2]] overflow : i32 +! NSW: fir.store %[[LOOP_VAR_NEXT]] to %[[LOOP_VAR_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER]] +! NSW: ^[[EXIT]]: +! NSW: return + ! Test an unstructured loop with a step. Mostly similar to the previous one. ! Only difference is a non-unit step. subroutine simple_unstructured_with_step() @@ -83,6 +114,36 @@ end subroutine ! CHECK: ^[[EXIT]]: ! CHECK: return +! NSW-LABEL: simple_unstructured_with_step +! NSW: %[[TRIP_VAR_REF:.*]] = fir.alloca i32 +! NSW: %[[LOOP_VAR_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsimple_unstructured_with_stepEi"} +! NSW: %[[ONE:.*]] = arith.constant 1 : i32 +! NSW: %[[HUNDRED:.*]] = arith.constant 100 : i32 +! NSW: %[[STEP:.*]] = arith.constant 2 : i32 +! NSW: %[[TMP1:.*]] = arith.subi %[[HUNDRED]], %[[ONE]] : i32 +! NSW: %[[TMP2:.*]] = arith.addi %[[TMP1]], %[[STEP]] : i32 +! NSW: %[[TRIP_COUNT:.*]] = arith.divsi %[[TMP2]], %[[STEP]] : i32 +! NSW: fir.store %[[TRIP_COUNT]] to %[[TRIP_VAR_REF]] : !fir.ref +! NSW: fir.store %[[ONE]] to %[[LOOP_VAR_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER:.*]] +! NSW: ^[[HEADER]]: +! NSW: %[[TRIP_VAR:.*]] = fir.load %[[TRIP_VAR_REF]] : !fir.ref +! NSW: %[[ZERO:.*]] = arith.constant 0 : i32 +! NSW: %[[COND:.*]] = arith.cmpi sgt, %[[TRIP_VAR]], %[[ZERO]] : i32 +! NSW: cf.cond_br %[[COND]], ^[[BODY:.*]], ^[[EXIT:.*]] +! NSW: ^[[BODY]]: +! NSW: %[[TRIP_VAR:.*]] = fir.load %[[TRIP_VAR_REF]] : !fir.ref +! NSW: %[[ONE_1:.*]] = arith.constant 1 : i32 +! NSW: %[[TRIP_VAR_NEXT:.*]] = arith.subi %[[TRIP_VAR]], %[[ONE_1]] : i32 +! NSW: fir.store %[[TRIP_VAR_NEXT]] to %[[TRIP_VAR_REF]] : !fir.ref +! NSW: %[[LOOP_VAR:.*]] = fir.load %[[LOOP_VAR_REF]] : !fir.ref +! NSW: %[[STEP_2:.*]] = arith.constant 2 : i32 +! NSW: %[[LOOP_VAR_NEXT:.*]] = arith.addi %[[LOOP_VAR]], %[[STEP_2]] overflow : i32 +! NSW: fir.store %[[LOOP_VAR_NEXT]] to %[[LOOP_VAR_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER]] +! NSW: ^[[EXIT]]: +! NSW: return + ! Test a three nested unstructured loop. Three nesting is the basic case where ! we have loops that are neither innermost or outermost. subroutine nested_unstructured() @@ -180,6 +241,90 @@ end subroutine ! CHECK: ^[[EXIT_I]]: ! CHECK: return +! NSW-LABEL: nested_unstructured +! NSW: %[[TRIP_VAR_K_REF:.*]] = fir.alloca i32 +! NSW: %[[TRIP_VAR_J_REF:.*]] = fir.alloca i32 +! NSW: %[[TRIP_VAR_I_REF:.*]] = fir.alloca i32 +! NSW: %[[LOOP_VAR_I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFnested_unstructuredEi"} +! NSW: %[[LOOP_VAR_J_REF:.*]] = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFnested_unstructuredEj"} +! NSW: %[[LOOP_VAR_K_REF:.*]] = fir.alloca i32 {bindc_name = "k", uniq_name = "_QFnested_unstructuredEk"} +! NSW: %[[I_START:.*]] = arith.constant 1 : i32 +! NSW: %[[I_END:.*]] = arith.constant 100 : i32 +! NSW: %[[I_STEP:.*]] = arith.constant 1 : i32 +! NSW: %[[TMP1:.*]] = arith.subi %[[I_END]], %[[I_START]] : i32 +! NSW: %[[TMP2:.*]] = arith.addi %[[TMP1]], %[[I_STEP]] : i32 +! NSW: %[[TRIP_COUNT_I:.*]] = arith.divsi %[[TMP2]], %[[I_STEP]] : i32 +! NSW: fir.store %[[TRIP_COUNT_I]] to %[[TRIP_VAR_I_REF]] : !fir.ref +! NSW: fir.store %[[I_START]] to %[[LOOP_VAR_I_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER_I:.*]] +! NSW: ^[[HEADER_I]]: +! NSW: %[[TRIP_VAR_I:.*]] = fir.load %[[TRIP_VAR_I_REF]] : !fir.ref +! NSW: %[[ZERO_1:.*]] = arith.constant 0 : i32 +! NSW: %[[COND_I:.*]] = arith.cmpi sgt, %[[TRIP_VAR_I]], %[[ZERO_1]] : i32 +! NSW: cf.cond_br %[[COND_I]], ^[[BODY_I:.*]], ^[[EXIT_I:.*]] +! NSW: ^[[BODY_I]]: +! NSW: %[[J_START:.*]] = arith.constant 1 : i32 +! NSW: %[[J_END:.*]] = arith.constant 200 : i32 +! NSW: %[[J_STEP:.*]] = arith.constant 1 : i32 +! NSW: %[[TMP3:.*]] = arith.subi %[[J_END]], %[[J_START]] : i32 +! NSW: %[[TMP4:.*]] = arith.addi %[[TMP3]], %[[J_STEP]] : i32 +! NSW: %[[TRIP_COUNT_J:.*]] = arith.divsi %[[TMP4]], %[[J_STEP]] : i32 +! NSW: fir.store %[[TRIP_COUNT_J]] to %[[TRIP_VAR_J_REF]] : !fir.ref +! NSW: fir.store %[[J_START]] to %[[LOOP_VAR_J_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER_J:.*]] +! NSW: ^[[HEADER_J]]: +! NSW: %[[TRIP_VAR_J:.*]] = fir.load %[[TRIP_VAR_J_REF]] : !fir.ref +! NSW: %[[ZERO_2:.*]] = arith.constant 0 : i32 +! NSW: %[[COND_J:.*]] = arith.cmpi sgt, %[[TRIP_VAR_J]], %[[ZERO_2]] : i32 +! NSW: cf.cond_br %[[COND_J]], ^[[BODY_J:.*]], ^[[EXIT_J:.*]] +! NSW: ^[[BODY_J]]: +! NSW: %[[K_START:.*]] = arith.constant 1 : i32 +! NSW: %[[K_END:.*]] = arith.constant 300 : i32 +! NSW: %[[K_STEP:.*]] = arith.constant 1 : i32 +! NSW: %[[TMP3:.*]] = arith.subi %[[K_END]], %[[K_START]] : i32 +! NSW: %[[TMP4:.*]] = arith.addi %[[TMP3]], %[[K_STEP]] : i32 +! NSW: %[[TRIP_COUNT_K:.*]] = arith.divsi %[[TMP4]], %[[K_STEP]] : i32 +! NSW: fir.store %[[TRIP_COUNT_K]] to %[[TRIP_VAR_K_REF]] : !fir.ref +! NSW: fir.store %[[K_START]] to %[[LOOP_VAR_K_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER_K:.*]] +! NSW: ^[[HEADER_K]]: +! NSW: %[[TRIP_VAR_K:.*]] = fir.load %[[TRIP_VAR_K_REF]] : !fir.ref +! NSW: %[[ZERO_2:.*]] = arith.constant 0 : i32 +! NSW: %[[COND_K:.*]] = arith.cmpi sgt, %[[TRIP_VAR_K]], %[[ZERO_2]] : i32 +! NSW: cf.cond_br %[[COND_K]], ^[[BODY_K:.*]], ^[[EXIT_K:.*]] +! NSW: ^[[BODY_K]]: +! NSW: %[[TRIP_VAR_K:.*]] = fir.load %[[TRIP_VAR_K_REF]] : !fir.ref +! NSW: %[[ONE_1:.*]] = arith.constant 1 : i32 +! NSW: %[[TRIP_VAR_K_NEXT:.*]] = arith.subi %[[TRIP_VAR_K]], %[[ONE_1]] : i32 +! NSW: fir.store %[[TRIP_VAR_K_NEXT]] to %[[TRIP_VAR_K_REF]] : !fir.ref +! NSW: %[[LOOP_VAR_K:.*]] = fir.load %[[LOOP_VAR_K_REF]] : !fir.ref +! NSW: %[[K_STEP_2:.*]] = arith.constant 1 : i32 +! NSW: %[[LOOP_VAR_K_NEXT:.*]] = arith.addi %[[LOOP_VAR_K]], %[[K_STEP_2]] overflow : i32 +! NSW: fir.store %[[LOOP_VAR_K_NEXT]] to %[[LOOP_VAR_K_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER_K]] +! NSW: ^[[EXIT_K]]: +! NSW: %[[TRIP_VAR_J:.*]] = fir.load %[[TRIP_VAR_J_REF]] : !fir.ref +! NSW: %[[ONE_1:.*]] = arith.constant 1 : i32 +! NSW: %[[TRIP_VAR_J_NEXT:.*]] = arith.subi %[[TRIP_VAR_J]], %[[ONE_1]] : i32 +! NSW: fir.store %[[TRIP_VAR_J_NEXT]] to %[[TRIP_VAR_J_REF]] : !fir.ref +! NSW: %[[LOOP_VAR_J:.*]] = fir.load %[[LOOP_VAR_J_REF]] : !fir.ref +! NSW: %[[J_STEP_2:.*]] = arith.constant 1 : i32 +! NSW: %[[LOOP_VAR_J_NEXT:.*]] = arith.addi %[[LOOP_VAR_J]], %[[J_STEP_2]] overflow : i32 +! NSW: fir.store %[[LOOP_VAR_J_NEXT]] to %[[LOOP_VAR_J_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER_J]] +! NSW: ^[[EXIT_J]]: +! NSW: %[[TRIP_VAR_I:.*]] = fir.load %[[TRIP_VAR_I_REF]] : !fir.ref +! NSW: %[[ONE_1:.*]] = arith.constant 1 : i32 +! NSW: %[[TRIP_VAR_I_NEXT:.*]] = arith.subi %[[TRIP_VAR_I]], %[[ONE_1]] : i32 +! NSW: fir.store %[[TRIP_VAR_I_NEXT]] to %[[TRIP_VAR_I_REF]] : !fir.ref +! NSW: %[[LOOP_VAR_I:.*]] = fir.load %[[LOOP_VAR_I_REF]] : !fir.ref +! NSW: %[[I_STEP_2:.*]] = arith.constant 1 : i32 +! NSW: %[[LOOP_VAR_I_NEXT:.*]] = arith.addi %[[LOOP_VAR_I]], %[[I_STEP_2]] overflow : i32 +! NSW: fir.store %[[LOOP_VAR_I_NEXT]] to %[[LOOP_VAR_I_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER_I]] +! NSW: ^[[EXIT_I]]: +! NSW: return + ! Test the existence of a structured loop inside an unstructured loop. ! Only minimal checks are inserted for the structured loop. subroutine nested_structured_in_unstructured() @@ -211,9 +356,12 @@ end subroutine ! CHECK: cf.cond_br %[[COND]], ^[[BODY:.*]], ^[[EXIT:.*]] ! CHECK: ^[[BODY]]: ! CHECK: %{{.*}} = fir.do_loop %[[J_INDEX:[^ ]*]] = -! CHECK-SAME: %{{.*}} to %{{.*}} step %{{[^ ]*}} +! CHECK-SAME: %{{.*}} to %{{.*}} step %[[ST:[^ ]*]] ! CHECK-SAME: iter_args(%[[J_IV:.*]] = %{{.*}}) -> (index, i32) { ! CHECK: fir.store %[[J_IV]] to %[[LOOP_VAR_J_REF]] : !fir.ref +! CHECK: %[[J_INDEX_NEXT:.*]] = arith.addi %[[J_INDEX]], %[[ST]] : index +! CHECK: %[[LOOP_VAR_J:.*]] = fir.load %[[LOOP_VAR_J_REF]] : !fir.ref +! CHECK: %[[LOOP_VAR_J_NEXT:.*]] = arith.addi %[[LOOP_VAR_J]], %{{[^ ]*}} : i32 ! CHECK: } ! CHECK: %[[TRIP_VAR_I:.*]] = fir.load %[[TRIP_VAR_I_REF]] : !fir.ref ! CHECK: %[[C1_3:.*]] = arith.constant 1 : i32 @@ -226,3 +374,42 @@ end subroutine ! CHECK: cf.br ^[[HEADER]] ! CHECK: ^[[EXIT]]: ! CHECK: return + +! NSW-LABEL: nested_structured_in_unstructured +! NSW: %[[TRIP_VAR_I_REF:.*]] = fir.alloca i32 +! NSW: %[[LOOP_VAR_I_REF:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFnested_structured_in_unstructuredEi"} +! NSW: %[[LOOP_VAR_J_REF:.*]] = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFnested_structured_in_unstructuredEj"} +! NSW: %[[I_START:.*]] = arith.constant 1 : i32 +! NSW: %[[I_END:.*]] = arith.constant 100 : i32 +! NSW: %[[I_STEP:.*]] = arith.constant 1 : i32 +! NSW: %[[TMP1:.*]] = arith.subi %[[I_END]], %[[I_START]] : i32 +! NSW: %[[TMP2:.*]] = arith.addi %[[TMP1]], %[[I_STEP]] : i32 +! NSW: %[[TRIP_COUNT:.*]] = arith.divsi %[[TMP2]], %[[I_STEP]] : i32 +! NSW: fir.store %[[TRIP_COUNT]] to %[[TRIP_VAR_I_REF]] : !fir.ref +! NSW: fir.store %[[I_START]] to %[[LOOP_VAR_I_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER:.*]] +! NSW: ^[[HEADER]]: +! NSW: %[[TRIP_VAR:.*]] = fir.load %[[TRIP_VAR_I_REF]] : !fir.ref +! NSW: %[[ZERO:.*]] = arith.constant 0 : i32 +! NSW: %[[COND:.*]] = arith.cmpi sgt, %[[TRIP_VAR]], %[[ZERO]] : i32 +! NSW: cf.cond_br %[[COND]], ^[[BODY:.*]], ^[[EXIT:.*]] +! NSW: ^[[BODY]]: +! NSW: %{{.*}} = fir.do_loop %[[J_INDEX:[^ ]*]] = +! NSW-SAME: %{{.*}} to %{{.*}} step %[[ST:[^ ]*]] +! NSW-SAME: iter_args(%[[J_IV:.*]] = %{{.*}}) -> (index, i32) { +! NSW: fir.store %[[J_IV]] to %[[LOOP_VAR_J_REF]] : !fir.ref +! NSW: %[[J_INDEX_NEXT:.*]] = arith.addi %[[J_INDEX]], %[[ST]] overflow : index +! NSW: %[[LOOP_VAR_J:.*]] = fir.load %[[LOOP_VAR_J_REF]] : !fir.ref +! NSW: %[[LOOP_VAR_J_NEXT:.*]] = arith.addi %[[LOOP_VAR_J]], %{{[^ ]*}} overflow : i32 +! NSW: } +! NSW: %[[TRIP_VAR_I:.*]] = fir.load %[[TRIP_VAR_I_REF]] : !fir.ref +! NSW: %[[C1_3:.*]] = arith.constant 1 : i32 +! NSW: %[[TRIP_VAR_I_NEXT:.*]] = arith.subi %[[TRIP_VAR_I]], %[[C1_3]] : i32 +! NSW: fir.store %[[TRIP_VAR_I_NEXT]] to %[[TRIP_VAR_I_REF]] : !fir.ref +! NSW: %[[LOOP_VAR_I:.*]] = fir.load %[[LOOP_VAR_I_REF]] : !fir.ref +! NSW: %[[I_STEP_2:.*]] = arith.constant 1 : i32 +! NSW: %[[LOOP_VAR_I_NEXT:.*]] = arith.addi %[[LOOP_VAR_I]], %[[I_STEP_2]] overflow : i32 +! NSW: fir.store %[[LOOP_VAR_I_NEXT]] to %[[LOOP_VAR_I_REF]] : !fir.ref +! NSW: cf.br ^[[HEADER]] +! NSW: ^[[EXIT]]: +! NSW: return diff --git a/flang/test/Lower/infinite_loop.f90 b/flang/test/Lower/infinite_loop.f90 index 0450e2c4485f..6942dda8d7a2 100644 --- a/flang/test/Lower/infinite_loop.f90 +++ b/flang/test/Lower/infinite_loop.f90 @@ -1,5 +1,6 @@ ! RUN: bbc -emit-fir -hlfir=false -o - %s | FileCheck %s ! RUN: %flang_fc1 -emit-fir -flang-deprecated-no-hlfir -o - %s | FileCheck %s +! RUN: %flang_fc1 -emit-fir -flang-deprecated-no-hlfir -flang-experimental-integer-overflow -o - %s | FileCheck %s --check-prefix=NSW ! Tests for infinite loop. @@ -106,6 +107,39 @@ end subroutine ! CHECK: ^[[RETURN]]: ! CHECK: return +! NSW-LABEL: structured_loop_in_infinite +! NSW-SAME: %[[I_REF:.*]]: !fir.ref +! NSW: %[[J_REF:.*]] = fir.alloca i32 {bindc_name = "j", uniq_name = "_QFstructured_loop_in_infiniteEj"} +! NSW: cf.br ^[[BODY1:.*]] +! NSW: ^[[BODY1]]: +! NSW: %[[I:.*]] = fir.load %[[I_REF]] : !fir.ref +! NSW: %[[C100:.*]] = arith.constant 100 : i32 +! NSW: %[[COND:.*]] = arith.cmpi sgt, %[[I]], %[[C100]] : i32 +! NSW: cf.cond_br %[[COND]], ^[[EXIT:.*]], ^[[BODY2:.*]] +! NSW: ^[[EXIT]]: +! NSW: cf.br ^[[RETURN:.*]] +! NSW: ^[[BODY2:.*]]: +! NSW: %[[C1:.*]] = arith.constant 1 : i32 +! NSW: %[[C1_INDEX:.*]] = fir.convert %[[C1]] : (i32) -> index +! NSW: %[[C10:.*]] = arith.constant 10 : i32 +! NSW: %[[C10_INDEX:.*]] = fir.convert %[[C10]] : (i32) -> index +! NSW: %[[C1_1:.*]] = arith.constant 1 : index +! NSW: %[[J_LB:.*]] = fir.convert %[[C1_INDEX]] : (index) -> i32 +! NSW: %[[J_FINAL:.*]]:2 = fir.do_loop %[[J:[^ ]*]] = +! NSW-SAME: %[[C1_INDEX]] to %[[C10_INDEX]] step %[[C1_1]] +! NSW-SAME: iter_args(%[[J_IV:.*]] = %[[J_LB]]) -> (index, i32) { +! NSW: fir.store %[[J_IV]] to %[[J_REF]] : !fir.ref +! NSW: %[[J_NEXT:.*]] = arith.addi %[[J]], %[[C1_1]] overflow : index +! NSW: %[[J_STEPCAST:.*]] = fir.convert %[[C1_1]] : (index) -> i32 +! NSW: %[[J_IVLOAD:.*]] = fir.load %[[J_REF]] : !fir.ref +! NSW: %[[J_IVINC:.*]] = arith.addi %[[J_IVLOAD]], %[[J_STEPCAST]] overflow : i32 +! NSW: fir.result %[[J_NEXT]], %[[J_IVINC]] : index, i32 +! NSW: } +! NSW: fir.store %[[J_FINAL]]#1 to %[[J_REF]] : !fir.ref +! NSW: cf.br ^[[BODY1]] +! NSW: ^[[RETURN]]: +! NSW: return + subroutine empty_infinite_in_while(i) integer :: i do while (i .gt. 50) diff --git a/flang/test/Lower/io-implied-do-fixes.f90 b/flang/test/Lower/io-implied-do-fixes.f90 index a309efa17f12..a6c115fa80de 100644 --- a/flang/test/Lower/io-implied-do-fixes.f90 +++ b/flang/test/Lower/io-implied-do-fixes.f90 @@ -1,4 +1,5 @@ ! RUN: bbc --use-desc-for-alloc=false -emit-fir -hlfir=false %s -o - | FileCheck %s +! RUN: bbc --use-desc-for-alloc=false -emit-fir -hlfir=false -integer-overflow %s -o - | FileCheck %s --check-prefix=NSW ! UNSUPPORTED: system-windows ! CHECK-LABEL: func @_QPido1 @@ -7,9 +8,23 @@ ! CHECK: %[[J_VAL_FINAL:.*]] = fir.do_loop %[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}} -> index { ! CHECK: %[[J_VAL_CVT1:.*]] = fir.convert %[[J_VAL]] : (index) -> i32 ! CHECK: fir.store %[[J_VAL_CVT1]] to %[[J_ADDR]] : !fir.ptr +! CHECK: %[[J_VAL_NEXT:.*]] = arith.addi %[[J_VAL]], %{{[^ ]*}} : index +! CHECK: fir.result %[[J_VAL_NEXT]] : index ! CHECK: } ! CHECK: %[[J_VAL_CVT2:.*]] = fir.convert %[[J_VAL_FINAL]] : (index) -> i32 ! CHECK: fir.store %[[J_VAL_CVT2]] to %[[J_ADDR]] : !fir.ptr + +! NSW-LABEL: func @_QPido1 +! NSW: %[[J_REF_ADDR:.*]] = fir.alloca !fir.ptr {uniq_name = "_QFido1Eiptr.addr"} +! NSW: %[[J_ADDR:.*]] = fir.load %[[J_REF_ADDR]] : !fir.ref> +! NSW: %[[J_VAL_FINAL:.*]] = fir.do_loop %[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}} -> index { +! NSW: %[[J_VAL_CVT1:.*]] = fir.convert %[[J_VAL]] : (index) -> i32 +! NSW: fir.store %[[J_VAL_CVT1]] to %[[J_ADDR]] : !fir.ptr +! NSW: %[[J_VAL_NEXT:.*]] = arith.addi %[[J_VAL]], %{{[^ ]*}} overflow : index +! NSW: fir.result %[[J_VAL_NEXT]] : index +! NSW: } +! NSW: %[[J_VAL_CVT2:.*]] = fir.convert %[[J_VAL_FINAL]] : (index) -> i32 +! NSW: fir.store %[[J_VAL_CVT2]] to %[[J_ADDR]] : !fir.ptr subroutine ido1 integer, pointer :: iptr integer, target :: itgt @@ -23,9 +38,23 @@ end subroutine ! CHECK: %[[J_VAL_FINAL:.*]] = fir.do_loop %[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}} -> index { ! CHECK: %[[J_VAL_CVT1:.*]] = fir.convert %[[J_VAL]] : (index) -> i32 ! CHECK: fir.store %[[J_VAL_CVT1]] to %[[J_ADDR]] : !fir.heap +! CHECK: %[[J_VAL_NEXT:.*]] = arith.addi %[[J_VAL]], %{{[^ ]*}} : index +! CHECK: fir.result %[[J_VAL_NEXT]] : index ! CHECK: } ! CHECK: %[[J_VAL_CVT2:.*]] = fir.convert %[[J_VAL_FINAL]] : (index) -> i32 ! CHECK: fir.store %[[J_VAL_CVT2]] to %[[J_ADDR]] : !fir.heap + +! NSW-LABEL: func @_QPido2 +! NSW: %[[J_REF_ADDR:.*]] = fir.alloca !fir.heap {uniq_name = "_QFido2Eiptr.addr"} +! NSW: %[[J_ADDR:.*]] = fir.load %[[J_REF_ADDR]] : !fir.ref> +! NSW: %[[J_VAL_FINAL:.*]] = fir.do_loop %[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}} -> index { +! NSW: %[[J_VAL_CVT1:.*]] = fir.convert %[[J_VAL]] : (index) -> i32 +! NSW: fir.store %[[J_VAL_CVT1]] to %[[J_ADDR]] : !fir.heap +! NSW: %[[J_VAL_NEXT:.*]] = arith.addi %[[J_VAL]], %{{[^ ]*}} overflow : index +! NSW: fir.result %[[J_VAL_NEXT]] : index +! NSW: } +! NSW: %[[J_VAL_CVT2:.*]] = fir.convert %[[J_VAL_FINAL]] : (index) -> i32 +! NSW: fir.store %[[J_VAL_CVT2]] to %[[J_ADDR]] : !fir.heap subroutine ido2 integer, allocatable :: iptr allocate(iptr) @@ -35,12 +64,32 @@ end subroutine ! CHECK-LABEL: func @_QPido3 ! CHECK: %[[J_REF_ADDR:.*]] = fir.alloca !fir.heap {uniq_name = "_QFido3Ej.addr"} ! CHECK: %[[J_ADDR:.*]] = fir.load %[[J_REF_ADDR]] : !fir.ref> -! CHECK: %[[J_VAL_FINAL:.*]]:2 = fir.iterate_while (%[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}}) and ({{.*}}) -> (index, i1) { +! CHECK: %[[J_VAL_FINAL:.*]]:2 = fir.iterate_while (%[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}}) and (%[[OK:.*]] = {{.*}}) -> (index, i1) { ! CHECK: %[[J_VAL_CVT1:.*]] = fir.convert %[[J_VAL]] : (index) -> i32 ! CHECK: fir.store %[[J_VAL_CVT1]] to %[[J_ADDR]] : !fir.heap +! CHECK: %[[RES:.*]] = fir.if %[[OK]] -> (i1) { +! CHECK: } +! CHECK: %[[J_VAL_INC:.*]] = arith.addi %[[J_VAL]], %{{[^ ]*}} : index +! CHECK: %[[J_VAL_NEXT:.*]] = arith.select %[[RES]], %[[J_VAL_INC]], %[[J_VAL]] : index +! CHECK: fir.result %[[J_VAL_NEXT]], %[[RES]] : index, i1 ! CHECK: } ! CHECK: %[[J_VAL_CVT2:.*]] = fir.convert %[[J_VAL_FINAL]]#0 : (index) -> i32 ! CHECK: fir.store %[[J_VAL_CVT2]] to %[[J_ADDR]] : !fir.heap {uniq_name = "_QFido3Ej.addr"} +! NSW: %[[J_ADDR:.*]] = fir.load %[[J_REF_ADDR]] : !fir.ref> +! NSW: %[[J_VAL_FINAL:.*]]:2 = fir.iterate_while (%[[J_VAL:.*]] = %{{.*}} to %{{.*}} step %{{.*}}) and (%[[OK:.*]] = {{.*}}) -> (index, i1) { +! NSW: %[[J_VAL_CVT1:.*]] = fir.convert %[[J_VAL]] : (index) -> i32 +! NSW: fir.store %[[J_VAL_CVT1]] to %[[J_ADDR]] : !fir.heap +! NSW: %[[RES:.*]] = fir.if %[[OK]] -> (i1) { +! NSW: } +! NSW: %[[J_VAL_INC:.*]] = arith.addi %[[J_VAL]], %{{[^ ]*}} overflow : index +! NSW: %[[J_VAL_NEXT:.*]] = arith.select %[[RES]], %[[J_VAL_INC]], %[[J_VAL]] : index +! NSW: fir.result %[[J_VAL_NEXT]], %[[RES]] : index, i1 +! NSW: } +! NSW: %[[J_VAL_CVT2:.*]] = fir.convert %[[J_VAL_FINAL]]#0 : (index) -> i32 +! NSW: fir.store %[[J_VAL_CVT2]] to %[[J_ADDR]] : !fir.heap llvm::cl::desc("Override host target triple"), llvm::cl::init("")); +static llvm::cl::opt + setNSW("integer-overflow", + llvm::cl::desc("add nsw flag to internal operations"), + llvm::cl::init(false)); + #define FLANG_EXCLUDE_CODEGEN #include "flang/Tools/CLOptions.inc" @@ -355,6 +360,7 @@ static mlir::LogicalResult convertFortranSourceToMLIR( Fortran::lower::LoweringOptions loweringOptions{}; loweringOptions.setNoPPCNativeVecElemOrder(enableNoPPCNativeVecElemOrder); loweringOptions.setLowerToHighLevelFIR(useHLFIR || emitHLFIR); + loweringOptions.setNSWOnLoopVarInc(setNSW); std::vector envDefaults = {}; auto burnside = Fortran::lower::LoweringBridge::create( ctx, semanticsContext, defKinds, semanticsContext.intrinsics(), @@ -432,6 +438,7 @@ static mlir::LogicalResult convertFortranSourceToMLIR( // Add O2 optimizer pass pipeline. MLIRToLLVMPassPipelineConfig config(llvm::OptimizationLevel::O2); + config.NSWOnLoopVarInc = setNSW; fir::registerDefaultInlinerPass(config); fir::createDefaultFIROptimizerPassPipeline(pm, config); } -- GitLab From 3cc445a6608dc0e88f7d5f16501ef827199cf0c4 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 15 May 2024 21:40:58 -0700 Subject: [PATCH 067/403] [MCAsmParser] Simplify expandMacro The error checking is only for .macro directives. Move it to the .macro parser to remove one parameter. --- llvm/lib/MC/MCParser/AsmParser.cpp | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/llvm/lib/MC/MCParser/AsmParser.cpp b/llvm/lib/MC/MCParser/AsmParser.cpp index 009465d11d78..33287c6529ca 100644 --- a/llvm/lib/MC/MCParser/AsmParser.cpp +++ b/llvm/lib/MC/MCParser/AsmParser.cpp @@ -295,10 +295,9 @@ private: void checkForBadMacro(SMLoc DirectiveLoc, StringRef Name, StringRef Body, ArrayRef Parameters); - bool expandMacro(raw_svector_ostream &OS, StringRef Body, + bool expandMacro(raw_svector_ostream &OS, const MCAsmMacro &Macro, ArrayRef Parameters, - ArrayRef A, bool EnableAtPseudoVariable, - SMLoc L); + ArrayRef A, bool EnableAtPseudoVariable); /// Are macros enabled in the parser? bool areMacrosEnabled() {return MacrosEnabledFlag;} @@ -2496,17 +2495,16 @@ static bool isIdentifierChar(char c) { c == '.'; } -bool AsmParser::expandMacro(raw_svector_ostream &OS, StringRef Body, +bool AsmParser::expandMacro(raw_svector_ostream &OS, const MCAsmMacro &Macro, ArrayRef Parameters, ArrayRef A, - bool EnableAtPseudoVariable, SMLoc L) { + bool EnableAtPseudoVariable) { unsigned NParameters = Parameters.size(); bool HasVararg = NParameters ? Parameters.back().Vararg : false; - if ((!IsDarwin || NParameters != 0) && NParameters != A.size()) - return Error(L, "Wrong number of arguments"); // A macro without parameters is handled differently on Darwin: // gas accepts no arguments and does no substitutions + StringRef Body = Macro.Body; while (!Body.empty()) { // Scan for the next substitution. std::size_t End = Body.size(), Pos = 0; @@ -2882,10 +2880,11 @@ bool AsmParser::handleMacroEntry(const MCAsmMacro *M, SMLoc NameLoc) { // Macro instantiation is lexical, unfortunately. We construct a new buffer // to hold the macro body with substitutions. SmallString<256> Buf; - StringRef Body = M->Body; raw_svector_ostream OS(Buf); - if (expandMacro(OS, Body, M->Parameters, A, true, getTok().getLoc())) + if ((!IsDarwin || M->Parameters.size()) && M->Parameters.size() != A.size()) + return Error(getTok().getLoc(), "Wrong number of arguments"); + if (expandMacro(OS, *M, M->Parameters, A, true)) return true; // We include the .endmacro in the buffer as our cue to exit the macro @@ -5694,8 +5693,7 @@ bool AsmParser::parseDirectiveRept(SMLoc DirectiveLoc, StringRef Dir) { raw_svector_ostream OS(Buf); while (Count--) { // Note that the AtPseudoVariable is disabled for instantiations of .rep(t). - if (expandMacro(OS, M->Body, std::nullopt, std::nullopt, false, - getTok().getLoc())) + if (expandMacro(OS, *M, std::nullopt, std::nullopt, false)) return true; } instantiateMacroLikeBody(M, DirectiveLoc, OS); @@ -5726,7 +5724,7 @@ bool AsmParser::parseDirectiveIrp(SMLoc DirectiveLoc) { for (const MCAsmMacroArgument &Arg : A) { // Note that the AtPseudoVariable is enabled for instantiations of .irp. // This is undocumented, but GAS seems to support it. - if (expandMacro(OS, M->Body, Parameter, Arg, true, getTok().getLoc())) + if (expandMacro(OS, *M, Parameter, Arg, true)) return true; } @@ -5768,7 +5766,7 @@ bool AsmParser::parseDirectiveIrpc(SMLoc DirectiveLoc) { // Note that the AtPseudoVariable is enabled for instantiations of .irpc. // This is undocumented, but GAS seems to support it. - if (expandMacro(OS, M->Body, Parameter, Arg, true, getTok().getLoc())) + if (expandMacro(OS, *M, Parameter, Arg, true)) return true; } -- GitLab From 245b7b65cb341ac5499fabf62f28fdbbc39bc7d7 Mon Sep 17 00:00:00 2001 From: jiajie zhang <56027356+JumpMasterJJ@users.noreply.github.com> Date: Thu, 16 May 2024 12:42:01 +0800 Subject: [PATCH 068/403] [flang] Add ETIME runtime and lowering intrinsics implementation (#90578) This patch add support of intrinsics GNU extension ETIME https://github.com/llvm/llvm-project/issues/84205. Some usage info and example has been added to `flang/docs/Intrinsics.md`. The patch contains both the lowering and the runtime code and works on both Windows and Linux. | System | Implmentation | |-----------|--------------------| | Windows| GetProcessTimes | | Linux |times | --- etime-function.mlir | 25 +++++ flang/docs/Intrinsics.md | 52 ++++++++++ .../flang/Optimizer/Builder/IntrinsicCall.h | 12 ++- .../Optimizer/Builder/Runtime/Intrinsics.h | 2 + flang/include/flang/Runtime/time-intrinsic.h | 3 + flang/lib/Evaluate/intrinsics.cpp | 26 ++++- flang/lib/Optimizer/Builder/IntrinsicCall.cpp | 99 +++++++++++++++++++ .../Optimizer/Builder/Runtime/Intrinsics.cpp | 14 +++ flang/runtime/time-intrinsic.cpp | 70 ++++++++++++- flang/runtime/tools.h | 9 ++ .../test/Lower/Intrinsics/etime-function.f90 | 24 +++++ flang/test/Lower/Intrinsics/etime.f90 | 21 ++++ flang/test/Semantics/etime.f90 | 30 ++++++ 13 files changed, 382 insertions(+), 5 deletions(-) create mode 100644 etime-function.mlir create mode 100644 flang/test/Lower/Intrinsics/etime-function.f90 create mode 100644 flang/test/Lower/Intrinsics/etime.f90 create mode 100644 flang/test/Semantics/etime.f90 diff --git a/etime-function.mlir b/etime-function.mlir new file mode 100644 index 000000000000..740dfd4866aa --- /dev/null +++ b/etime-function.mlir @@ -0,0 +1,25 @@ +module attributes {dlti.dl_spec = #dlti.dl_spec<#dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry : vector<4xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry, dense<32> : vector<4xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry, dense<64> : vector<4xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry : vector<2xi64>>, #dlti.dl_entry, dense<32> : vector<4xi64>>, #dlti.dl_entry<"dlti.endianness", "little">, #dlti.dl_entry<"dlti.stack_alignment", 128 : i64>>, fir.defaultkind = "a1c4d8i4l4r4", fir.kindmap = "", llvm.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128", llvm.target_triple = "x86_64-unknown-linux-gnu"} { + func.func @_QPetime_test(%arg0: !fir.ref> {fir.bindc_name = "values"}, %arg1: !fir.ref {fir.bindc_name = "time"}) { + %c9_i32 = arith.constant 9 : i32 + %c2 = arith.constant 2 : index + %0 = fir.alloca f32 + %1 = fir.declare %arg1 {uniq_name = "_QFetime_testEtime"} : (!fir.ref) -> !fir.ref + %2 = fir.shape %c2 : (index) -> !fir.shape<1> + %3 = fir.declare %arg0(%2) {uniq_name = "_QFetime_testEvalues"} : (!fir.ref>, !fir.shape<1>) -> !fir.ref> + %4 = fir.embox %3(%2) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + %5 = fir.embox %0 : (!fir.ref) -> !fir.box + %6 = fir.address_of(@_QQclX116781708dcf8f012d7ec1e40d743d97) : !fir.ref> + %7 = fir.convert %4 : (!fir.box>) -> !fir.box + %8 = fir.convert %5 : (!fir.box) -> !fir.box + %9 = fir.convert %6 : (!fir.ref>) -> !fir.ref + %10 = fir.call @_FortranAEtime(%7, %8, %9, %c9_i32) fastmath : (!fir.box, !fir.box, !fir.ref, i32) -> none + %11 = fir.load %0 : !fir.ref + fir.store %11 to %1 : !fir.ref + return + } + func.func private @_FortranAEtime(!fir.box, !fir.box, !fir.ref, i32) -> none attributes {fir.runtime} + fir.global linkonce @_QQclX116781708dcf8f012d7ec1e40d743d97 constant : !fir.char<1,71> { + %0 = fir.string_lit "/home/jump/llvm-project/flang/test/Lower/Intrinsics/etime-function.f90\00"(71) : !fir.char<1,71> + fir.has_value %0 : !fir.char<1,71> + } +} diff --git a/flang/docs/Intrinsics.md b/flang/docs/Intrinsics.md index 848619cb65d9..41129b10083b 100644 --- a/flang/docs/Intrinsics.md +++ b/flang/docs/Intrinsics.md @@ -916,3 +916,55 @@ used in constant expressions have currently no folding support at all. - If a condition occurs that would assign a nonzero value to `CMDSTAT` but the `CMDSTAT` variable is not present, error termination is initiated. - On POSIX-compatible systems, the child process (async process) will be terminated with no effect on the parent process (continues). - On Windows, error termination is not initiated. + +### Non-Standard Intrinsics: ETIME + +#### Description +`ETIME(VALUES, TIME)` returns the number of seconds of runtime since the start of the process’s execution in *TIME*. *VALUES* returns the user and system components of this time in `VALUES(1)` and `VALUES(2)` respectively. *TIME* is equal to `VALUES(1) + VALUES(2)`. + +On some systems, the underlying timings are represented using types with sufficiently small limits that overflows (wrap around) are possible, such as 32-bit types. Therefore, the values returned by this intrinsic might be, or become, negative, or numerically less than previous values, during a single run of the compiled program. + +This intrinsic is provided in both subroutine and function forms; however, only one form can be used in any given program unit. + +*VALUES* and *TIME* are `INTENT(OUT)` and provide the following: + + +| | | +|---------------|-----------------------------------| +| `VALUES(1)` | User time in seconds. | +| `VALUES(2)` | System time in seconds. | +| `TIME` | Run time since start in seconds. | + +#### Usage and Info + +- **Standard:** GNU extension +- **Class:** Subroutine, function +- **Syntax:** `CALL ETIME(VALUES, TIME)` +- **Arguments:** +- **Return value** Elapsed time in seconds since the start of program execution. + +| Argument | Description | +|------------|-----------------------------------------------------------------------| +| `VALUES` | The type shall be REAL(4), DIMENSION(2). | +| `TIME` | The type shall be REAL(4). | + +#### Example +Here is an example usage from [Gfortran ETIME](https://gcc.gnu.org/onlinedocs/gfortran/ETIME.html) +```Fortran +program test_etime + integer(8) :: i, j + real, dimension(2) :: tarray + real :: result + call ETIME(tarray, result) + print *, result + print *, tarray(1) + print *, tarray(2) + do i=1,100000000 ! Just a delay + j = i * i - i + end do + call ETIME(tarray, result) + print *, result + print *, tarray(1) + print *, tarray(2) +end program test_etime +``` \ No newline at end of file diff --git a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h index b7d060926761..977a69af5281 100644 --- a/flang/include/flang/Optimizer/Builder/IntrinsicCall.h +++ b/flang/include/flang/Optimizer/Builder/IntrinsicCall.h @@ -222,6 +222,8 @@ struct IntrinsicLibrary { fir::ExtendedValue genEoshift(mlir::Type, llvm::ArrayRef); void genExit(llvm::ArrayRef); void genExecuteCommandLine(mlir::ArrayRef args); + fir::ExtendedValue genEtime(std::optional, + mlir::ArrayRef args); mlir::Value genExponent(mlir::Type, llvm::ArrayRef); fir::ExtendedValue genExtendsTypeOf(mlir::Type, llvm::ArrayRef); @@ -400,8 +402,10 @@ struct IntrinsicLibrary { using ElementalGenerator = decltype(&IntrinsicLibrary::genAbs); using ExtendedGenerator = decltype(&IntrinsicLibrary::genLenTrim); using SubroutineGenerator = decltype(&IntrinsicLibrary::genDateAndTime); - using Generator = - std::variant; + /// The generator for intrinsic that has both function and subroutine form. + using DualGenerator = decltype(&IntrinsicLibrary::genEtime); + using Generator = std::variant; /// All generators can be outlined. This will build a function named /// "fir."+ + "." + and generate the @@ -442,6 +446,10 @@ struct IntrinsicLibrary { llvm::ArrayRef args); mlir::Value invokeGenerator(SubroutineGenerator generator, llvm::ArrayRef args); + mlir::Value invokeGenerator(DualGenerator generator, + llvm::ArrayRef args); + mlir::Value invokeGenerator(DualGenerator generator, mlir::Type resultType, + llvm::ArrayRef args); /// Get pointer to unrestricted intrinsic. Generate the related unrestricted /// intrinsic if it is not defined yet. diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h b/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h index 737c631e45c1..7497a4bc3564 100644 --- a/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h +++ b/flang/include/flang/Optimizer/Builder/Runtime/Intrinsics.h @@ -44,6 +44,8 @@ void genDateAndTime(fir::FirOpBuilder &, mlir::Location, std::optional date, std::optional time, std::optional zone, mlir::Value values); +void genEtime(fir::FirOpBuilder &builder, mlir::Location loc, + mlir::Value values, mlir::Value time); void genRandomInit(fir::FirOpBuilder &, mlir::Location, mlir::Value repeatable, mlir::Value imageDistinct); diff --git a/flang/include/flang/Runtime/time-intrinsic.h b/flang/include/flang/Runtime/time-intrinsic.h index 650c02436ee4..80490a17e455 100644 --- a/flang/include/flang/Runtime/time-intrinsic.h +++ b/flang/include/flang/Runtime/time-intrinsic.h @@ -43,6 +43,9 @@ void RTNAME(DateAndTime)(char *date, std::size_t dateChars, char *time, const char *source = nullptr, int line = 0, const Descriptor *values = nullptr); +void RTNAME(Etime)(const Descriptor *values, const Descriptor *time, + const char *sourceFile, int line); + } // extern "C" } // namespace Fortran::runtime #endif // FORTRAN_RUNTIME_TIME_INTRINSIC_H_ diff --git a/flang/lib/Evaluate/intrinsics.cpp b/flang/lib/Evaluate/intrinsics.cpp index 441a762c930d..ded277877f49 100644 --- a/flang/lib/Evaluate/intrinsics.cpp +++ b/flang/lib/Evaluate/intrinsics.cpp @@ -454,6 +454,10 @@ static const IntrinsicInterface genericIntrinsicFunction[]{ {"erf", {{"x", SameReal}}, SameReal}, {"erfc", {{"x", SameReal}}, SameReal}, {"erfc_scaled", {{"x", SameReal}}, SameReal}, + {"etime", + {{"values", TypePattern{RealType, KindCode::exactKind, 4}, Rank::vector, + Optionality::required, common::Intent::Out}}, + TypePattern{RealType, KindCode::exactKind, 4}}, {"exp", {{"x", SameFloating}}, SameFloating}, {"exp", {{"x", SameFloating}}, SameFloating}, {"exponent", {{"x", AnyReal}}, DefaultInt}, @@ -1342,6 +1346,12 @@ static const IntrinsicInterface intrinsicSubroutine[]{ {"values", AnyInt, Rank::vector, Optionality::optional, common::Intent::Out}}, {}, Rank::elemental, IntrinsicClass::impureSubroutine}, + {"etime", + {{"values", TypePattern{RealType, KindCode::exactKind, 4}, Rank::vector, + Optionality::required, common::Intent::Out}, + {"time", TypePattern{RealType, KindCode::exactKind, 4}, + Rank::scalar, Optionality::required, common::Intent::Out}}, + {}, Rank::elemental, IntrinsicClass::impureSubroutine}, {"execute_command_line", {{"command", DefaultChar, Rank::scalar}, {"wait", AnyLogical, Rank::scalar, Optionality::optional}, @@ -2484,6 +2494,7 @@ public: bool IsIntrinsic(const std::string &) const; bool IsIntrinsicFunction(const std::string &) const; bool IsIntrinsicSubroutine(const std::string &) const; + bool IsDualIntrinsic(const std::string &) const; IntrinsicClass GetIntrinsicClass(const std::string &) const; std::string GetGenericIntrinsicName(const std::string &) const; @@ -2545,6 +2556,17 @@ bool IntrinsicProcTable::Implementation::IsIntrinsic( const std::string &name) const { return IsIntrinsicFunction(name) || IsIntrinsicSubroutine(name); } +bool IntrinsicProcTable::Implementation::IsDualIntrinsic( + const std::string &name) const { + // Collection for some intrinsics with function and subroutine form, + // in order to pass the semantic check. + static const std::string dualIntrinsic[]{{"etime"}}; + + return std::find_if(std::begin(dualIntrinsic), std::end(dualIntrinsic), + [&name](const std::string &dualName) { + return dualName == name; + }) != std::end(dualIntrinsic); +} IntrinsicClass IntrinsicProcTable::Implementation::GetIntrinsicClass( const std::string &name) const { @@ -3083,7 +3105,7 @@ std::optional IntrinsicProcTable::Implementation::Probe( return specificCall; } } - if (IsIntrinsicFunction(call.name)) { + if (IsIntrinsicFunction(call.name) && !IsDualIntrinsic(call.name)) { context.messages().Say( "Cannot use intrinsic function '%s' as a subroutine"_err_en_US, call.name); @@ -3218,7 +3240,7 @@ std::optional IntrinsicProcTable::Implementation::Probe( } if (specificBuffer.empty() && genericBuffer.empty() && - IsIntrinsicSubroutine(call.name)) { + IsIntrinsicSubroutine(call.name) && !IsDualIntrinsic(call.name)) { context.messages().Say( "Cannot use intrinsic subroutine '%s' as a function"_err_en_US, call.name); diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp index 58064d23eb08..ae7e65098744 100644 --- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp +++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp @@ -35,6 +35,7 @@ #include "flang/Optimizer/Builder/Runtime/Stop.h" #include "flang/Optimizer/Builder/Runtime/Transformational.h" #include "flang/Optimizer/Builder/Todo.h" +#include "flang/Optimizer/Dialect/FIROps.h" #include "flang/Optimizer/Dialect/FIROpsSupport.h" #include "flang/Optimizer/Dialect/Support/FIRContext.h" #include "flang/Optimizer/Support/FatalError.h" @@ -49,6 +50,7 @@ #include "llvm/Support/Debug.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" +#include #include #define DEBUG_TYPE "flang-lower-intrinsic" @@ -222,6 +224,10 @@ static constexpr IntrinsicHandler handlers[]{ {"boundary", asBox, handleDynamicOptional}, {"dim", asValue}}}, /*isElemental=*/false}, + {"etime", + &I::genEtime, + {{{"values", asBox}, {"time", asBox}}}, + /*isElemental=*/false}, {"execute_command_line", &I::genExecuteCommandLine, {{{"command", asBox}, @@ -1682,6 +1688,24 @@ IntrinsicLibrary::genElementalCall( return mlir::Value(); } +template <> +fir::ExtendedValue +IntrinsicLibrary::genElementalCall( + DualGenerator generator, llvm::StringRef name, mlir::Type resultType, + llvm::ArrayRef args, bool outline) { + assert(resultType.getImpl() && "expect elemental intrinsic to be functions"); + + for (const fir::ExtendedValue &arg : args) + if (!arg.getUnboxed() && !arg.getCharBox()) + // fir::emitFatalError(loc, "nonscalar intrinsic argument"); + crashOnMissingIntrinsic(loc, name); + if (outline) + return outlineInExtendedWrapper(generator, name, resultType, args); + + return std::invoke(generator, *this, std::optional{resultType}, + args); +} + static fir::ExtendedValue invokeHandler(IntrinsicLibrary::ElementalGenerator generator, const IntrinsicHandler &handler, @@ -1725,6 +1749,22 @@ invokeHandler(IntrinsicLibrary::SubroutineGenerator generator, return mlir::Value{}; } +static fir::ExtendedValue +invokeHandler(IntrinsicLibrary::DualGenerator generator, + const IntrinsicHandler &handler, + std::optional resultType, + llvm::ArrayRef args, bool outline, + IntrinsicLibrary &lib) { + if (handler.isElemental) + return lib.genElementalCall(generator, handler.name, mlir::Type{}, args, + outline); + if (outline) + return lib.outlineInExtendedWrapper(generator, handler.name, resultType, + args); + + return std::invoke(generator, lib, resultType, args); +} + std::pair IntrinsicLibrary::genIntrinsicCall(llvm::StringRef specificName, std::optional resultType, @@ -1820,6 +1860,34 @@ IntrinsicLibrary::invokeGenerator(SubroutineGenerator generator, return {}; } +mlir::Value +IntrinsicLibrary::invokeGenerator(DualGenerator generator, + llvm::ArrayRef args) { + llvm::SmallVector extendedArgs; + for (mlir::Value arg : args) + extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); + std::invoke(generator, *this, std::optional{}, extendedArgs); + return {}; +} + +mlir::Value +IntrinsicLibrary::invokeGenerator(DualGenerator generator, + mlir::Type resultType, + llvm::ArrayRef args) { + llvm::SmallVector extendedArgs; + for (mlir::Value arg : args) + extendedArgs.emplace_back(toExtendedValue(arg, builder, loc)); + + if (resultType.getImpl() == nullptr) { + // TODO: + assert(false && "result type is null"); + } + + auto extendedResult = std::invoke( + generator, *this, std::optional{resultType}, extendedArgs); + return toValue(extendedResult, builder, loc); +} + //===----------------------------------------------------------------------===// // Intrinsic Procedure Mangling //===----------------------------------------------------------------------===// @@ -3235,6 +3303,37 @@ void IntrinsicLibrary::genExecuteCommandLine( exitstatBox, cmdstatBox, cmdmsgBox); } +// ETIME +fir::ExtendedValue +IntrinsicLibrary::genEtime(std::optional resultType, + llvm::ArrayRef args) { + assert((args.size() == 2 && !resultType.has_value()) || + (args.size() == 1 && resultType.has_value())); + + mlir::Value values = fir::getBase(args[0]); + if (resultType.has_value()) { + // function form + if (!values) + fir::emitFatalError(loc, "expected VALUES parameter"); + + auto timeAddr = builder.createTemporary(loc, *resultType); + auto timeBox = builder.createBox(loc, timeAddr); + fir::runtime::genEtime(builder, loc, values, timeBox); + return builder.create(loc, timeAddr); + } else { + // subroutine form + mlir::Value time = fir::getBase(args[1]); + if (!values) + fir::emitFatalError(loc, "expected VALUES parameter"); + if (!time) + fir::emitFatalError(loc, "expected TIME parameter"); + + fir::runtime::genEtime(builder, loc, values, time); + return {}; + } + return {}; +} + // EXIT void IntrinsicLibrary::genExit(llvm::ArrayRef args) { assert(args.size() == 1); diff --git a/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp b/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp index 8b78a1688c73..3f36d639861b 100644 --- a/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp +++ b/flang/lib/Optimizer/Builder/Runtime/Intrinsics.cpp @@ -106,6 +106,20 @@ void fir::runtime::genDateAndTime(fir::FirOpBuilder &builder, builder.create(loc, callee, args); } +void fir::runtime::genEtime(fir::FirOpBuilder &builder, mlir::Location loc, + mlir::Value values, mlir::Value time) { + auto runtimeFunc = fir::runtime::getRuntimeFunc(loc, builder); + mlir::FunctionType runtimeFuncTy = runtimeFunc.getFunctionType(); + + mlir::Value sourceFile = fir::factory::locationToFilename(builder, loc); + mlir::Value sourceLine = + fir::factory::locationToLineNo(builder, loc, runtimeFuncTy.getInput(3)); + + llvm::SmallVector args = fir::runtime::createArguments( + builder, loc, runtimeFuncTy, values, time, sourceFile, sourceLine); + builder.create(loc, runtimeFunc, args); +} + void fir::runtime::genRandomInit(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value repeatable, mlir::Value imageDistinct) { diff --git a/flang/runtime/time-intrinsic.cpp b/flang/runtime/time-intrinsic.cpp index 68d63253139f..989d4f804c5f 100644 --- a/flang/runtime/time-intrinsic.cpp +++ b/flang/runtime/time-intrinsic.cpp @@ -19,8 +19,12 @@ #include #include #include -#ifndef _WIN32 +#ifdef _WIN32 +#include "flang/Common/windows-include.h" +#else #include // gettimeofday +#include +#include #endif // CPU_TIME (Fortran 2018 16.9.57) @@ -370,5 +374,69 @@ void RTNAME(DateAndTime)(char *date, std::size_t dateChars, char *time, terminator, date, dateChars, time, timeChars, zone, zoneChars, values); } +void RTNAME(Etime)(const Descriptor *values, const Descriptor *time, + const char *sourceFile, int line) { + Fortran::runtime::Terminator terminator{sourceFile, line}; + + double usrTime = -1.0, sysTime = -1.0, realTime = -1.0; + +#ifdef _WIN32 + FILETIME creationTime; + FILETIME exitTime; + FILETIME kernelTime; + FILETIME userTime; + + if (GetProcessTimes(GetCurrentProcess(), &creationTime, &exitTime, + &kernelTime, &userTime) == 0) { + ULARGE_INTEGER userSystemTime; + ULARGE_INTEGER kernelSystemTime; + + memcpy(&userSystemTime, &userTime, sizeof(FILETIME)); + memcpy(&kernelSystemTime, &kernelTime, sizeof(FILETIME)); + + usrTime = ((double)(userSystemTime.QuadPart)) / 10000000.0; + sysTime = ((double)(kernelSystemTime.QuadPart)) / 10000000.0; + realTime = usrTime + sysTime; + } +#else + struct tms tms; + if (times(&tms) != -1) { + usrTime = ((double)(tms.tms_utime)) / sysconf(_SC_CLK_TCK); + sysTime = ((double)(tms.tms_stime)) / sysconf(_SC_CLK_TCK); + realTime = usrTime + sysTime; + } +#endif + + if (values) { + auto typeCode{values->type().GetCategoryAndKind()}; + // ETIME values argument must have decimal range == 2. + RUNTIME_CHECK(terminator, + values->rank() == 1 && values->GetDimension(0).Extent() == 2 && + typeCode && typeCode->first == Fortran::common::TypeCategory::Real); + // Only accept KIND=4 here. + int kind{typeCode->second}; + RUNTIME_CHECK(terminator, kind == 4); + + ApplyFloatingPointKind( + kind, terminator, *values, /* atIndex = */ 0, usrTime); + ApplyFloatingPointKind( + kind, terminator, *values, /* atIndex = */ 1, sysTime); + } + + if (time) { + auto typeCode{time->type().GetCategoryAndKind()}; + // ETIME time argument must have decimal range == 0. + RUNTIME_CHECK(terminator, + time->rank() == 0 && typeCode && + typeCode->first == Fortran::common::TypeCategory::Real); + // Only accept KIND=4 here. + int kind{typeCode->second}; + RUNTIME_CHECK(terminator, kind == 4); + + ApplyFloatingPointKind( + kind, terminator, *time, /* atIndex = */ 0, realTime); + } +} + } // extern "C" } // namespace Fortran::runtime diff --git a/flang/runtime/tools.h b/flang/runtime/tools.h index 52049c511f13..dc12e5c4533e 100644 --- a/flang/runtime/tools.h +++ b/flang/runtime/tools.h @@ -99,6 +99,15 @@ template struct StoreIntegerAt { } }; +// Helper to store floating value in result[at]. +template struct StoreFloatingPointAt { + RT_API_ATTRS void operator()(const Fortran::runtime::Descriptor &result, + std::size_t at, std::double_t value) const { + *result.ZeroBasedIndexedElement>(at) = value; + } +}; + // Validate a KIND= argument RT_API_ATTRS void CheckIntegerKind( Terminator &, int kind, const char *intrinsic); diff --git a/flang/test/Lower/Intrinsics/etime-function.f90 b/flang/test/Lower/Intrinsics/etime-function.f90 new file mode 100644 index 000000000000..c47d509af535 --- /dev/null +++ b/flang/test/Lower/Intrinsics/etime-function.f90 @@ -0,0 +1,24 @@ +! RUN: bbc -emit-fir %s -o - | FileCheck %s + +! CHECK-LABEL: func.func @_QPetime_test( +! CHECK-SAME: %[[valuesArg:.*]]: !fir.ref> {fir.bindc_name = "values"}, +! CHECK-SAME: %[[timeArg:.*]]: !fir.ref {fir.bindc_name = "time"}) { +subroutine etime_test(values, time) + REAL(4), DIMENSION(2) :: values + REAL(4) :: time + time = etime(values) + ! CHECK-NEXT: %[[c9:.*]] = arith.constant 9 : i32 + ! CHECK-NEXT: %[[c2:.*]] = arith.constant 2 : index + ! CHECK-NEXT: %[[timeTmpAddr:.*]] = fir.alloca f32 + ! CHECK-NEXT: %[[timeDeclare:.*]] = fir.declare %[[timeArg]] {uniq_name = "_QFetime_testEtime"} : (!fir.ref) -> !fir.ref + ! CHECK-NEXT: %[[shape:.*]] = fir.shape %[[c2]] : (index) -> !fir.shape<1> + ! CHECK-NEXT: %[[valuesDeclare:.*]] = fir.declare %[[valuesArg]](%[[shape]]) {uniq_name = "_QFetime_testEvalues"} : (!fir.ref>, !fir.shape<1>) -> !fir.ref> + ! CHECK-NEXT: %[[valuesBox:.*]] = fir.embox %[[valuesDeclare]](%[[shape]]) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + ! CHECK-NEXT: %[[timeTmpBox:.*]] = fir.embox %[[timeTmpAddr]] : (!fir.ref) -> !fir.box + ! CHECK: %[[values:.*]] = fir.convert %[[valuesBox]] : (!fir.box>) -> !fir.box + ! CHECK: %[[timeTmp:.*]] = fir.convert %[[timeTmpBox]] : (!fir.box) -> !fir.box + ! CHECK: %[[VAL_9:.*]] = fir.call @_FortranAEtime(%[[values]], %[[timeTmp]], %[[VAL_7:.*]], %[[c9]]) fastmath : (!fir.box, !fir.box, !fir.ref, i32) -> none + ! CHECK-NEXT: %[[timeValue:.*]] = fir.load %[[timeTmpAddr]] : !fir.ref + ! CHECK-NEXT: fir.store %[[timeValue]] to %[[timeDeclare]] : !fir.ref + ! CHECK-NEXT: return +end subroutine etime_test \ No newline at end of file diff --git a/flang/test/Lower/Intrinsics/etime.f90 b/flang/test/Lower/Intrinsics/etime.f90 new file mode 100644 index 000000000000..e5e7984a340c --- /dev/null +++ b/flang/test/Lower/Intrinsics/etime.f90 @@ -0,0 +1,21 @@ +! RUN: bbc -emit-fir %s -o - | FileCheck %s + +! CHECK-LABEL: func.func @_QPetime_test( +! CHECK-SAME: %[[valuesArg:.*]]: !fir.ref> {fir.bindc_name = "values"}, +! CHECK-SAME: %[[timeArg:.*]]: !fir.ref {fir.bindc_name = "time"}) { +subroutine etime_test(values, time) + REAL(4), DIMENSION(2) :: values + REAL(4) :: time + call etime(values, time) + ! CHECK-NEXT: %[[c9:.*]] = arith.constant 9 : i32 + ! CHECK-NEXT: %[[c2:.*]] = arith.constant 2 : index + ! CHECK-NEXT: %[[timeDeclare:.*]] = fir.declare %[[timeArg]] {uniq_name = "_QFetime_testEtime"} : (!fir.ref) -> !fir.ref + ! CHECK-NEXT: %[[shape:.*]] = fir.shape %[[c2]] : (index) -> !fir.shape<1> + ! CHECK-NEXT: %[[valuesDeclare:.*]] = fir.declare %[[valuesArg]](%[[shape]]) {uniq_name = "_QFetime_testEvalues"} : (!fir.ref>, !fir.shape<1>) -> !fir.ref> + ! CHECK-NEXT: %[[valuesBox:.*]] = fir.embox %[[valuesDeclare]](%[[shape]]) : (!fir.ref>, !fir.shape<1>) -> !fir.box> + ! CHECK-NEXT: %[[timeBox:.*]] = fir.embox %[[timeDeclare]] : (!fir.ref) -> !fir.box + ! CHECK: %[[values:.*]] = fir.convert %[[valuesBox]] : (!fir.box>) -> !fir.box + ! CHECK: %[[time:.*]] = fir.convert %[[timeBox]] : (!fir.box) -> !fir.box + ! CHECK: %[[VAL_9:.*]] = fir.call @_FortranAEtime(%[[values]], %[[time]], %[[VAL_7:.*]], %[[c9]]) fastmath : (!fir.box, !fir.box, !fir.ref, i32) -> none + ! CHECK-NEXT: return +end subroutine etime_test \ No newline at end of file diff --git a/flang/test/Semantics/etime.f90 b/flang/test/Semantics/etime.f90 new file mode 100644 index 000000000000..28735c2a7aac --- /dev/null +++ b/flang/test/Semantics/etime.f90 @@ -0,0 +1,30 @@ +! RUN: %python %S/test_errors.py %s %flang_fc1 -pedantic +! Tests for the ETIME intrinsics + +subroutine bad_kind_error(values, time) + REAL(KIND=8), DIMENSION(2) :: values + REAL(KIND=8) :: time + !ERROR: Actual argument for 'values=' has bad type or kind 'REAL(8)' + call etime(values, time) +end subroutine bad_kind_error + +subroutine bad_args_error(values) + REAL(KIND=4), DIMENSION(2) :: values + !ERROR: missing mandatory 'time=' argument + call etime(values) +end subroutine bad_args_error + +subroutine bad_apply_form(values) + REAL(KIND=4), DIMENSION(2) :: values + REAL(KIND=4) :: time + !Declaration of 'etime' + call etime(values, time) + !ERROR: Cannot call subroutine 'etime' like a function + time = etime(values) +end subroutine bad_apply_form + +subroutine good_kind_equal(values, time) + REAL(KIND=4), DIMENSION(2) :: values + REAL(KIND=4) :: time + call etime(values, time) +end subroutine good_kind_equal \ No newline at end of file -- GitLab From f2d74002fdad2171b62392eaedf38aac7e4fb50d Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Wed, 15 May 2024 21:46:31 -0700 Subject: [PATCH 069/403] [LegalizeVectorOps][X86] Add ISD::ABDS/ABSDU to the list of opcodes handled by LegalizeVectorOps. (#92332) The expand code is present, but we were missing the type query code so the nodes would be ignored until LegalizeDAG. --- .../SelectionDAG/LegalizeVectorOps.cpp | 2 ++ llvm/test/CodeGen/X86/midpoint-int-vec-128.ll | 32 +++++++++---------- llvm/test/CodeGen/X86/midpoint-int-vec-256.ll | 32 +++++++++---------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp index 423df9ae6b2a..6acbc044d673 100644 --- a/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/LegalizeVectorOps.cpp @@ -367,6 +367,8 @@ SDValue VectorLegalizer::LegalizeOp(SDValue Op) { case ISD::ROTL: case ISD::ROTR: case ISD::ABS: + case ISD::ABDS: + case ISD::ABDU: case ISD::BSWAP: case ISD::BITREVERSE: case ISD::CTLZ: diff --git a/llvm/test/CodeGen/X86/midpoint-int-vec-128.ll b/llvm/test/CodeGen/X86/midpoint-int-vec-128.ll index 5a6375e08bca..c6e8b7532505 100644 --- a/llvm/test/CodeGen/X86/midpoint-int-vec-128.ll +++ b/llvm/test/CodeGen/X86/midpoint-int-vec-128.ll @@ -1991,14 +1991,14 @@ define <8 x i16> @vec128_i16_unsigned_reg_reg(<8 x i16> %a1, <8 x i16> %a2) noun ; ; AVX512VL-FALLBACK-LABEL: vec128_i16_unsigned_reg_reg: ; AVX512VL-FALLBACK: # %bb.0: -; AVX512VL-FALLBACK-NEXT: vpmaxuw %xmm1, %xmm0, %xmm2 -; AVX512VL-FALLBACK-NEXT: vpminuw %xmm1, %xmm0, %xmm1 -; AVX512VL-FALLBACK-NEXT: vpsubw %xmm1, %xmm2, %xmm2 -; AVX512VL-FALLBACK-NEXT: vpsrlw $1, %xmm2, %xmm2 -; AVX512VL-FALLBACK-NEXT: vpcmpeqw %xmm1, %xmm0, %xmm1 -; AVX512VL-FALLBACK-NEXT: vpternlogq $15, %xmm1, %xmm1, %xmm1 -; AVX512VL-FALLBACK-NEXT: vpxor %xmm1, %xmm2, %xmm2 -; AVX512VL-FALLBACK-NEXT: vpsubw %xmm1, %xmm2, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpminuw %xmm1, %xmm0, %xmm2 +; AVX512VL-FALLBACK-NEXT: vpmaxuw %xmm1, %xmm0, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpsubw %xmm2, %xmm1, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpsrlw $1, %xmm1, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpcmpeqw %xmm2, %xmm0, %xmm2 +; AVX512VL-FALLBACK-NEXT: vpternlogq $15, %xmm2, %xmm2, %xmm2 +; AVX512VL-FALLBACK-NEXT: vpxor %xmm2, %xmm1, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpsubw %xmm2, %xmm1, %xmm1 ; AVX512VL-FALLBACK-NEXT: vpaddw %xmm0, %xmm1, %xmm0 ; AVX512VL-FALLBACK-NEXT: retq ; @@ -2784,14 +2784,14 @@ define <16 x i8> @vec128_i8_unsigned_reg_reg(<16 x i8> %a1, <16 x i8> %a2) nounw ; ; AVX512VL-FALLBACK-LABEL: vec128_i8_unsigned_reg_reg: ; AVX512VL-FALLBACK: # %bb.0: -; AVX512VL-FALLBACK-NEXT: vpmaxub %xmm1, %xmm0, %xmm2 -; AVX512VL-FALLBACK-NEXT: vpminub %xmm1, %xmm0, %xmm1 -; AVX512VL-FALLBACK-NEXT: vpsubb %xmm1, %xmm2, %xmm2 -; AVX512VL-FALLBACK-NEXT: vpsrlw $1, %xmm2, %xmm2 -; AVX512VL-FALLBACK-NEXT: vpcmpeqb %xmm1, %xmm0, %xmm1 -; AVX512VL-FALLBACK-NEXT: vpternlogq $15, %xmm1, %xmm1, %xmm1 -; AVX512VL-FALLBACK-NEXT: vpternlogd $108, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to4}, %xmm1, %xmm2 -; AVX512VL-FALLBACK-NEXT: vpsubb %xmm1, %xmm2, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpminub %xmm1, %xmm0, %xmm2 +; AVX512VL-FALLBACK-NEXT: vpmaxub %xmm1, %xmm0, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpsubb %xmm2, %xmm1, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpsrlw $1, %xmm1, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpcmpeqb %xmm2, %xmm0, %xmm2 +; AVX512VL-FALLBACK-NEXT: vpternlogq $15, %xmm2, %xmm2, %xmm2 +; AVX512VL-FALLBACK-NEXT: vpternlogd $108, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to4}, %xmm2, %xmm1 +; AVX512VL-FALLBACK-NEXT: vpsubb %xmm2, %xmm1, %xmm1 ; AVX512VL-FALLBACK-NEXT: vpaddb %xmm0, %xmm1, %xmm0 ; AVX512VL-FALLBACK-NEXT: retq ; diff --git a/llvm/test/CodeGen/X86/midpoint-int-vec-256.ll b/llvm/test/CodeGen/X86/midpoint-int-vec-256.ll index e880a1acc9e8..cc08396ae8c7 100644 --- a/llvm/test/CodeGen/X86/midpoint-int-vec-256.ll +++ b/llvm/test/CodeGen/X86/midpoint-int-vec-256.ll @@ -1445,14 +1445,14 @@ define <16 x i16> @vec256_i16_unsigned_reg_reg(<16 x i16> %a1, <16 x i16> %a2) n ; ; AVX512VL-FALLBACK-LABEL: vec256_i16_unsigned_reg_reg: ; AVX512VL-FALLBACK: # %bb.0: -; AVX512VL-FALLBACK-NEXT: vpmaxuw %ymm1, %ymm0, %ymm2 -; AVX512VL-FALLBACK-NEXT: vpminuw %ymm1, %ymm0, %ymm1 -; AVX512VL-FALLBACK-NEXT: vpsubw %ymm1, %ymm2, %ymm2 -; AVX512VL-FALLBACK-NEXT: vpsrlw $1, %ymm2, %ymm2 -; AVX512VL-FALLBACK-NEXT: vpcmpeqw %ymm1, %ymm0, %ymm1 -; AVX512VL-FALLBACK-NEXT: vpternlogq $15, %ymm1, %ymm1, %ymm1 -; AVX512VL-FALLBACK-NEXT: vpxor %ymm1, %ymm2, %ymm2 -; AVX512VL-FALLBACK-NEXT: vpsubw %ymm1, %ymm2, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpminuw %ymm1, %ymm0, %ymm2 +; AVX512VL-FALLBACK-NEXT: vpmaxuw %ymm1, %ymm0, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpsubw %ymm2, %ymm1, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpsrlw $1, %ymm1, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpcmpeqw %ymm2, %ymm0, %ymm2 +; AVX512VL-FALLBACK-NEXT: vpternlogq $15, %ymm2, %ymm2, %ymm2 +; AVX512VL-FALLBACK-NEXT: vpxor %ymm2, %ymm1, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpsubw %ymm2, %ymm1, %ymm1 ; AVX512VL-FALLBACK-NEXT: vpaddw %ymm0, %ymm1, %ymm0 ; AVX512VL-FALLBACK-NEXT: retq ; @@ -2210,14 +2210,14 @@ define <32 x i8> @vec256_i8_unsigned_reg_reg(<32 x i8> %a1, <32 x i8> %a2) nounw ; ; AVX512VL-FALLBACK-LABEL: vec256_i8_unsigned_reg_reg: ; AVX512VL-FALLBACK: # %bb.0: -; AVX512VL-FALLBACK-NEXT: vpmaxub %ymm1, %ymm0, %ymm2 -; AVX512VL-FALLBACK-NEXT: vpminub %ymm1, %ymm0, %ymm1 -; AVX512VL-FALLBACK-NEXT: vpsubb %ymm1, %ymm2, %ymm2 -; AVX512VL-FALLBACK-NEXT: vpsrlw $1, %ymm2, %ymm2 -; AVX512VL-FALLBACK-NEXT: vpcmpeqb %ymm1, %ymm0, %ymm1 -; AVX512VL-FALLBACK-NEXT: vpternlogq $15, %ymm1, %ymm1, %ymm1 -; AVX512VL-FALLBACK-NEXT: vpternlogd $108, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %ymm1, %ymm2 -; AVX512VL-FALLBACK-NEXT: vpsubb %ymm1, %ymm2, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpminub %ymm1, %ymm0, %ymm2 +; AVX512VL-FALLBACK-NEXT: vpmaxub %ymm1, %ymm0, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpsubb %ymm2, %ymm1, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpsrlw $1, %ymm1, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpcmpeqb %ymm2, %ymm0, %ymm2 +; AVX512VL-FALLBACK-NEXT: vpternlogq $15, %ymm2, %ymm2, %ymm2 +; AVX512VL-FALLBACK-NEXT: vpternlogd $108, {{\.?LCPI[0-9]+_[0-9]+}}(%rip){1to8}, %ymm2, %ymm1 +; AVX512VL-FALLBACK-NEXT: vpsubb %ymm2, %ymm1, %ymm1 ; AVX512VL-FALLBACK-NEXT: vpaddb %ymm0, %ymm1, %ymm0 ; AVX512VL-FALLBACK-NEXT: retq ; -- GitLab From 487b43cdc9fff9e370b8ea948c0cc19ca817aa86 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Wed, 15 May 2024 21:47:29 -0700 Subject: [PATCH 070/403] [RISCV] Pass subvector type to isLegalInterleavedAccessType in getInterleavedMemoryOpCost. (#91825) isLegalInterleavedAccessType expects the subvector type, but getInterleavedMemoryOpCost is called with the full vector type. So we need to divide by Factor. --- .../Target/RISCV/RISCVTargetTransformInfo.cpp | 19 +- .../RISCV/interleaved-accesses.ll | 566 ++++++++---------- .../LoopVectorize/RISCV/interleaved-cost.ll | 4 +- 3 files changed, 259 insertions(+), 330 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp index 4d2479fc233f..b73ed208ed74 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp @@ -613,14 +613,19 @@ InstructionCost RISCVTTIImpl::getInterleavedMemoryOpCost( std::pair LT = getTypeLegalizationCost(VTy); // Need to make sure type has't been scalarized if (LT.second.isVector()) { - auto *LegalVTy = VectorType::get(VTy->getElementType(), - LT.second.getVectorElementCount()); - // FIXME: We use the memory op cost of the *legalized* type here, becuase - // it's getMemoryOpCost returns a really expensive cost for types like - // <6 x i8>, which show up when doing interleaves of Factor=3 etc. - // Should the memory op cost of these be cheaper? - if (TLI->isLegalInterleavedAccessType(LegalVTy, Factor, Alignment, + auto *SubVecTy = + VectorType::get(VTy->getElementType(), + VTy->getElementCount().divideCoefficientBy(Factor)); + + if (VTy->getElementCount().isKnownMultipleOf(Factor) && + TLI->isLegalInterleavedAccessType(SubVecTy, Factor, Alignment, AddressSpace, DL)) { + // FIXME: We use the memory op cost of the *legalized* type here, + // because it's getMemoryOpCost returns a really expensive cost for + // types like <6 x i8>, which show up when doing interleaves of + // Factor=3 etc. Should the memory op cost of these be cheaper? + auto *LegalVTy = VectorType::get(VTy->getElementType(), + LT.second.getVectorElementCount()); InstructionCost LegalMemCost = getMemoryOpCost( Opcode, LegalVTy, Alignment, AddressSpace, CostKind); return LT.first + LegalMemCost; diff --git a/llvm/test/Transforms/LoopVectorize/RISCV/interleaved-accesses.ll b/llvm/test/Transforms/LoopVectorize/RISCV/interleaved-accesses.ll index 576dc0833fa3..87bc77cb7767 100644 --- a/llvm/test/Transforms/LoopVectorize/RISCV/interleaved-accesses.ll +++ b/llvm/test/Transforms/LoopVectorize/RISCV/interleaved-accesses.ll @@ -393,23 +393,23 @@ define void @load_store_factor3_i32(ptr %p) { ; CHECK-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 3 ; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 [[TMP1]] ; CHECK-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[TMP2]], i32 0 -; CHECK-NEXT: [[WIDE_VEC:%.*]] = load <6 x i32>, ptr [[TMP3]], align 4 -; CHECK-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; CHECK-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; CHECK-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; CHECK-NEXT: [[TMP4:%.*]] = add <2 x i32> [[STRIDED_VEC]], +; CHECK-NEXT: [[WIDE_VEC:%.*]] = load <24 x i32>, ptr [[TMP3]], align 4 +; CHECK-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; CHECK-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; CHECK-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; CHECK-NEXT: [[TMP4:%.*]] = add <8 x i32> [[STRIDED_VEC]], ; CHECK-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 -; CHECK-NEXT: [[TMP6:%.*]] = add <2 x i32> [[STRIDED_VEC1]], +; CHECK-NEXT: [[TMP6:%.*]] = add <8 x i32> [[STRIDED_VEC1]], ; CHECK-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 ; CHECK-NEXT: [[TMP8:%.*]] = getelementptr i32, ptr [[P]], i64 [[TMP7]] -; CHECK-NEXT: [[TMP9:%.*]] = add <2 x i32> [[STRIDED_VEC2]], +; CHECK-NEXT: [[TMP9:%.*]] = add <8 x i32> [[STRIDED_VEC2]], ; CHECK-NEXT: [[TMP10:%.*]] = getelementptr i32, ptr [[TMP8]], i32 -2 -; CHECK-NEXT: [[TMP11:%.*]] = shufflevector <2 x i32> [[TMP4]], <2 x i32> [[TMP6]], <4 x i32> -; CHECK-NEXT: [[TMP12:%.*]] = shufflevector <2 x i32> [[TMP9]], <2 x i32> poison, <4 x i32> -; CHECK-NEXT: [[TMP13:%.*]] = shufflevector <4 x i32> [[TMP11]], <4 x i32> [[TMP12]], <6 x i32> -; CHECK-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <6 x i32> [[TMP13]], <6 x i32> poison, <6 x i32> -; CHECK-NEXT: store <6 x i32> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 4 -; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2 +; CHECK-NEXT: [[TMP11:%.*]] = shufflevector <8 x i32> [[TMP4]], <8 x i32> [[TMP6]], <16 x i32> +; CHECK-NEXT: [[TMP12:%.*]] = shufflevector <8 x i32> [[TMP9]], <8 x i32> poison, <16 x i32> +; CHECK-NEXT: [[TMP13:%.*]] = shufflevector <16 x i32> [[TMP11]], <16 x i32> [[TMP12]], <24 x i32> +; CHECK-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <24 x i32> [[TMP13]], <24 x i32> poison, <24 x i32> +; CHECK-NEXT: store <24 x i32> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 4 +; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 8 ; CHECK-NEXT: [[TMP14:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 ; CHECK-NEXT: br i1 [[TMP14]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP6:![0-9]+]] ; CHECK: middle.block: @@ -451,23 +451,23 @@ define void @load_store_factor3_i32(ptr %p) { ; FIXED-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 3 ; FIXED-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 [[TMP1]] ; FIXED-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[TMP2]], i32 0 -; FIXED-NEXT: [[WIDE_VEC:%.*]] = load <6 x i32>, ptr [[TMP3]], align 4 -; FIXED-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; FIXED-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; FIXED-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; FIXED-NEXT: [[TMP4:%.*]] = add <2 x i32> [[STRIDED_VEC]], +; FIXED-NEXT: [[WIDE_VEC:%.*]] = load <24 x i32>, ptr [[TMP3]], align 4 +; FIXED-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; FIXED-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; FIXED-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; FIXED-NEXT: [[TMP4:%.*]] = add <8 x i32> [[STRIDED_VEC]], ; FIXED-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 -; FIXED-NEXT: [[TMP6:%.*]] = add <2 x i32> [[STRIDED_VEC1]], +; FIXED-NEXT: [[TMP6:%.*]] = add <8 x i32> [[STRIDED_VEC1]], ; FIXED-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 ; FIXED-NEXT: [[TMP8:%.*]] = getelementptr i32, ptr [[P]], i64 [[TMP7]] -; FIXED-NEXT: [[TMP9:%.*]] = add <2 x i32> [[STRIDED_VEC2]], +; FIXED-NEXT: [[TMP9:%.*]] = add <8 x i32> [[STRIDED_VEC2]], ; FIXED-NEXT: [[TMP10:%.*]] = getelementptr i32, ptr [[TMP8]], i32 -2 -; FIXED-NEXT: [[TMP11:%.*]] = shufflevector <2 x i32> [[TMP4]], <2 x i32> [[TMP6]], <4 x i32> -; FIXED-NEXT: [[TMP12:%.*]] = shufflevector <2 x i32> [[TMP9]], <2 x i32> poison, <4 x i32> -; FIXED-NEXT: [[TMP13:%.*]] = shufflevector <4 x i32> [[TMP11]], <4 x i32> [[TMP12]], <6 x i32> -; FIXED-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <6 x i32> [[TMP13]], <6 x i32> poison, <6 x i32> -; FIXED-NEXT: store <6 x i32> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 4 -; FIXED-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2 +; FIXED-NEXT: [[TMP11:%.*]] = shufflevector <8 x i32> [[TMP4]], <8 x i32> [[TMP6]], <16 x i32> +; FIXED-NEXT: [[TMP12:%.*]] = shufflevector <8 x i32> [[TMP9]], <8 x i32> poison, <16 x i32> +; FIXED-NEXT: [[TMP13:%.*]] = shufflevector <16 x i32> [[TMP11]], <16 x i32> [[TMP12]], <24 x i32> +; FIXED-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <24 x i32> [[TMP13]], <24 x i32> poison, <24 x i32> +; FIXED-NEXT: store <24 x i32> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 4 +; FIXED-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 8 ; FIXED-NEXT: [[TMP14:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 ; FIXED-NEXT: br i1 [[TMP14]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP6:![0-9]+]] ; FIXED: middle.block: @@ -509,23 +509,23 @@ define void @load_store_factor3_i32(ptr %p) { ; SCALABLE-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 3 ; SCALABLE-NEXT: [[TMP2:%.*]] = getelementptr i32, ptr [[P:%.*]], i64 [[TMP1]] ; SCALABLE-NEXT: [[TMP3:%.*]] = getelementptr i32, ptr [[TMP2]], i32 0 -; SCALABLE-NEXT: [[WIDE_VEC:%.*]] = load <6 x i32>, ptr [[TMP3]], align 4 -; SCALABLE-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; SCALABLE-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; SCALABLE-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <6 x i32> [[WIDE_VEC]], <6 x i32> poison, <2 x i32> -; SCALABLE-NEXT: [[TMP4:%.*]] = add <2 x i32> [[STRIDED_VEC]], +; SCALABLE-NEXT: [[WIDE_VEC:%.*]] = load <24 x i32>, ptr [[TMP3]], align 4 +; SCALABLE-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <24 x i32> [[WIDE_VEC]], <24 x i32> poison, <8 x i32> +; SCALABLE-NEXT: [[TMP4:%.*]] = add <8 x i32> [[STRIDED_VEC]], ; SCALABLE-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 -; SCALABLE-NEXT: [[TMP6:%.*]] = add <2 x i32> [[STRIDED_VEC1]], +; SCALABLE-NEXT: [[TMP6:%.*]] = add <8 x i32> [[STRIDED_VEC1]], ; SCALABLE-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 ; SCALABLE-NEXT: [[TMP8:%.*]] = getelementptr i32, ptr [[P]], i64 [[TMP7]] -; SCALABLE-NEXT: [[TMP9:%.*]] = add <2 x i32> [[STRIDED_VEC2]], +; SCALABLE-NEXT: [[TMP9:%.*]] = add <8 x i32> [[STRIDED_VEC2]], ; SCALABLE-NEXT: [[TMP10:%.*]] = getelementptr i32, ptr [[TMP8]], i32 -2 -; SCALABLE-NEXT: [[TMP11:%.*]] = shufflevector <2 x i32> [[TMP4]], <2 x i32> [[TMP6]], <4 x i32> -; SCALABLE-NEXT: [[TMP12:%.*]] = shufflevector <2 x i32> [[TMP9]], <2 x i32> poison, <4 x i32> -; SCALABLE-NEXT: [[TMP13:%.*]] = shufflevector <4 x i32> [[TMP11]], <4 x i32> [[TMP12]], <6 x i32> -; SCALABLE-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <6 x i32> [[TMP13]], <6 x i32> poison, <6 x i32> -; SCALABLE-NEXT: store <6 x i32> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 4 -; SCALABLE-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2 +; SCALABLE-NEXT: [[TMP11:%.*]] = shufflevector <8 x i32> [[TMP4]], <8 x i32> [[TMP6]], <16 x i32> +; SCALABLE-NEXT: [[TMP12:%.*]] = shufflevector <8 x i32> [[TMP9]], <8 x i32> poison, <16 x i32> +; SCALABLE-NEXT: [[TMP13:%.*]] = shufflevector <16 x i32> [[TMP11]], <16 x i32> [[TMP12]], <24 x i32> +; SCALABLE-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <24 x i32> [[TMP13]], <24 x i32> poison, <24 x i32> +; SCALABLE-NEXT: store <24 x i32> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 4 +; SCALABLE-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 8 ; SCALABLE-NEXT: [[TMP14:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 ; SCALABLE-NEXT: br i1 [[TMP14]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP6:![0-9]+]] ; SCALABLE: middle.block: @@ -589,54 +589,38 @@ exit: define void @load_store_factor3_i64(ptr %p) { ; CHECK-LABEL: @load_store_factor3_i64( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = call i64 @llvm.vscale.i64() -; CHECK-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 2 -; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 1024, [[TMP1]] -; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]] +; CHECK-NEXT: br i1 false, label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]] ; CHECK: vector.ph: -; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.vscale.i64() -; CHECK-NEXT: [[TMP3:%.*]] = mul i64 [[TMP2]], 2 -; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i64 1024, [[TMP3]] -; CHECK-NEXT: [[N_VEC:%.*]] = sub i64 1024, [[N_MOD_VF]] -; CHECK-NEXT: [[TMP4:%.*]] = call i64 @llvm.vscale.i64() -; CHECK-NEXT: [[TMP5:%.*]] = mul i64 [[TMP4]], 2 -; CHECK-NEXT: [[TMP6:%.*]] = call @llvm.experimental.stepvector.nxv2i64() -; CHECK-NEXT: [[TMP7:%.*]] = add [[TMP6]], zeroinitializer -; CHECK-NEXT: [[TMP8:%.*]] = mul [[TMP7]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[INDUCTION:%.*]] = add zeroinitializer, [[TMP8]] -; CHECK-NEXT: [[TMP9:%.*]] = call i64 @llvm.vscale.i64() -; CHECK-NEXT: [[TMP10:%.*]] = mul i64 [[TMP9]], 2 -; CHECK-NEXT: [[TMP11:%.*]] = mul i64 1, [[TMP10]] -; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement poison, i64 [[TMP11]], i64 0 -; CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector [[DOTSPLATINSERT]], poison, zeroinitializer ; CHECK-NEXT: br label [[VECTOR_BODY:%.*]] ; CHECK: vector.body: ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ] -; CHECK-NEXT: [[VEC_IND:%.*]] = phi [ [[INDUCTION]], [[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], [[VECTOR_BODY]] ] -; CHECK-NEXT: [[TMP12:%.*]] = mul [[VEC_IND]], shufflevector ( insertelement ( poison, i64 3, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP13:%.*]] = getelementptr i64, ptr [[P:%.*]], [[TMP12]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP13]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP14:%.*]] = add [[WIDE_MASKED_GATHER]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP14]], [[TMP13]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP15:%.*]] = add [[TMP12]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP16:%.*]] = getelementptr i64, ptr [[P]], [[TMP15]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER1:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP16]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP17:%.*]] = add [[WIDE_MASKED_GATHER1]], shufflevector ( insertelement ( poison, i64 2, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP17]], [[TMP16]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP18:%.*]] = add [[TMP15]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP19:%.*]] = getelementptr i64, ptr [[P]], [[TMP18]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER2:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP19]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP20:%.*]] = add [[WIDE_MASKED_GATHER2]], shufflevector ( insertelement ( poison, i64 3, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP20]], [[TMP19]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], [[TMP5]] -; CHECK-NEXT: [[VEC_IND_NEXT]] = add [[VEC_IND]], [[DOTSPLAT]] -; CHECK-NEXT: [[TMP21:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]] -; CHECK-NEXT: br i1 [[TMP21]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP8:![0-9]+]] +; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0 +; CHECK-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 3 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i64, ptr [[P:%.*]], i64 [[TMP1]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr i64, ptr [[TMP2]], i32 0 +; CHECK-NEXT: [[WIDE_VEC:%.*]] = load <12 x i64>, ptr [[TMP3]], align 8 +; CHECK-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; CHECK-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; CHECK-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; CHECK-NEXT: [[TMP4:%.*]] = add <4 x i64> [[STRIDED_VEC]], +; CHECK-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 +; CHECK-NEXT: [[TMP6:%.*]] = add <4 x i64> [[STRIDED_VEC1]], +; CHECK-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 +; CHECK-NEXT: [[TMP8:%.*]] = getelementptr i64, ptr [[P]], i64 [[TMP7]] +; CHECK-NEXT: [[TMP9:%.*]] = add <4 x i64> [[STRIDED_VEC2]], +; CHECK-NEXT: [[TMP10:%.*]] = getelementptr i64, ptr [[TMP8]], i32 -2 +; CHECK-NEXT: [[TMP11:%.*]] = shufflevector <4 x i64> [[TMP4]], <4 x i64> [[TMP6]], <8 x i32> +; CHECK-NEXT: [[TMP12:%.*]] = shufflevector <4 x i64> [[TMP9]], <4 x i64> poison, <8 x i32> +; CHECK-NEXT: [[TMP13:%.*]] = shufflevector <8 x i64> [[TMP11]], <8 x i64> [[TMP12]], <12 x i32> +; CHECK-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <12 x i64> [[TMP13]], <12 x i64> poison, <12 x i32> +; CHECK-NEXT: store <12 x i64> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 8 +; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 4 +; CHECK-NEXT: [[TMP14:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 +; CHECK-NEXT: br i1 [[TMP14]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP8:![0-9]+]] ; CHECK: middle.block: -; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 1024, [[N_VEC]] -; CHECK-NEXT: br i1 [[CMP_N]], label [[EXIT:%.*]], label [[SCALAR_PH]] +; CHECK-NEXT: br i1 true, label [[EXIT:%.*]], label [[SCALAR_PH]] ; CHECK: scalar.ph: -; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ 1024, [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ] ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ], [ [[NEXTI:%.*]], [[LOOP]] ] @@ -668,26 +652,29 @@ define void @load_store_factor3_i64(ptr %p) { ; FIXED-NEXT: br label [[VECTOR_BODY:%.*]] ; FIXED: vector.body: ; FIXED-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ] -; FIXED-NEXT: [[VEC_IND:%.*]] = phi <4 x i64> [ , [[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], [[VECTOR_BODY]] ] -; FIXED-NEXT: [[TMP0:%.*]] = mul <4 x i64> [[VEC_IND]], -; FIXED-NEXT: [[TMP1:%.*]] = getelementptr i64, ptr [[P:%.*]], <4 x i64> [[TMP0]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP1]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP2:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP2]], <4 x ptr> [[TMP1]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP3:%.*]] = add <4 x i64> [[TMP0]], -; FIXED-NEXT: [[TMP4:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP3]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER1:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP4]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP5:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER1]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP5]], <4 x ptr> [[TMP4]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP6:%.*]] = add <4 x i64> [[TMP3]], -; FIXED-NEXT: [[TMP7:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP6]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER2:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP7]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP8:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER2]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP8]], <4 x ptr> [[TMP7]], i32 8, <4 x i1> ) +; FIXED-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0 +; FIXED-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 3 +; FIXED-NEXT: [[TMP2:%.*]] = getelementptr i64, ptr [[P:%.*]], i64 [[TMP1]] +; FIXED-NEXT: [[TMP3:%.*]] = getelementptr i64, ptr [[TMP2]], i32 0 +; FIXED-NEXT: [[WIDE_VEC:%.*]] = load <12 x i64>, ptr [[TMP3]], align 8 +; FIXED-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; FIXED-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; FIXED-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; FIXED-NEXT: [[TMP4:%.*]] = add <4 x i64> [[STRIDED_VEC]], +; FIXED-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 +; FIXED-NEXT: [[TMP6:%.*]] = add <4 x i64> [[STRIDED_VEC1]], +; FIXED-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 +; FIXED-NEXT: [[TMP8:%.*]] = getelementptr i64, ptr [[P]], i64 [[TMP7]] +; FIXED-NEXT: [[TMP9:%.*]] = add <4 x i64> [[STRIDED_VEC2]], +; FIXED-NEXT: [[TMP10:%.*]] = getelementptr i64, ptr [[TMP8]], i32 -2 +; FIXED-NEXT: [[TMP11:%.*]] = shufflevector <4 x i64> [[TMP4]], <4 x i64> [[TMP6]], <8 x i32> +; FIXED-NEXT: [[TMP12:%.*]] = shufflevector <4 x i64> [[TMP9]], <4 x i64> poison, <8 x i32> +; FIXED-NEXT: [[TMP13:%.*]] = shufflevector <8 x i64> [[TMP11]], <8 x i64> [[TMP12]], <12 x i32> +; FIXED-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <12 x i64> [[TMP13]], <12 x i64> poison, <12 x i32> +; FIXED-NEXT: store <12 x i64> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 8 ; FIXED-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 4 -; FIXED-NEXT: [[VEC_IND_NEXT]] = add <4 x i64> [[VEC_IND]], -; FIXED-NEXT: [[TMP9:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 -; FIXED-NEXT: br i1 [[TMP9]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP8:![0-9]+]] +; FIXED-NEXT: [[TMP14:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 +; FIXED-NEXT: br i1 [[TMP14]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP8:![0-9]+]] ; FIXED: middle.block: ; FIXED-NEXT: br i1 true, label [[EXIT:%.*]], label [[SCALAR_PH]] ; FIXED: scalar.ph: @@ -718,54 +705,38 @@ define void @load_store_factor3_i64(ptr %p) { ; ; SCALABLE-LABEL: @load_store_factor3_i64( ; SCALABLE-NEXT: entry: -; SCALABLE-NEXT: [[TMP0:%.*]] = call i64 @llvm.vscale.i64() -; SCALABLE-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 2 -; SCALABLE-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 1024, [[TMP1]] -; SCALABLE-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]] +; SCALABLE-NEXT: br i1 false, label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]] ; SCALABLE: vector.ph: -; SCALABLE-NEXT: [[TMP2:%.*]] = call i64 @llvm.vscale.i64() -; SCALABLE-NEXT: [[TMP3:%.*]] = mul i64 [[TMP2]], 2 -; SCALABLE-NEXT: [[N_MOD_VF:%.*]] = urem i64 1024, [[TMP3]] -; SCALABLE-NEXT: [[N_VEC:%.*]] = sub i64 1024, [[N_MOD_VF]] -; SCALABLE-NEXT: [[TMP4:%.*]] = call i64 @llvm.vscale.i64() -; SCALABLE-NEXT: [[TMP5:%.*]] = mul i64 [[TMP4]], 2 -; SCALABLE-NEXT: [[TMP6:%.*]] = call @llvm.experimental.stepvector.nxv2i64() -; SCALABLE-NEXT: [[TMP7:%.*]] = add [[TMP6]], zeroinitializer -; SCALABLE-NEXT: [[TMP8:%.*]] = mul [[TMP7]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[INDUCTION:%.*]] = add zeroinitializer, [[TMP8]] -; SCALABLE-NEXT: [[TMP9:%.*]] = call i64 @llvm.vscale.i64() -; SCALABLE-NEXT: [[TMP10:%.*]] = mul i64 [[TMP9]], 2 -; SCALABLE-NEXT: [[TMP11:%.*]] = mul i64 1, [[TMP10]] -; SCALABLE-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement poison, i64 [[TMP11]], i64 0 -; SCALABLE-NEXT: [[DOTSPLAT:%.*]] = shufflevector [[DOTSPLATINSERT]], poison, zeroinitializer ; SCALABLE-NEXT: br label [[VECTOR_BODY:%.*]] ; SCALABLE: vector.body: ; SCALABLE-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ] -; SCALABLE-NEXT: [[VEC_IND:%.*]] = phi [ [[INDUCTION]], [[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], [[VECTOR_BODY]] ] -; SCALABLE-NEXT: [[TMP12:%.*]] = mul [[VEC_IND]], shufflevector ( insertelement ( poison, i64 3, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP13:%.*]] = getelementptr i64, ptr [[P:%.*]], [[TMP12]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP13]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP14:%.*]] = add [[WIDE_MASKED_GATHER]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP14]], [[TMP13]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP15:%.*]] = add [[TMP12]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP16:%.*]] = getelementptr i64, ptr [[P]], [[TMP15]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER1:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP16]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP17:%.*]] = add [[WIDE_MASKED_GATHER1]], shufflevector ( insertelement ( poison, i64 2, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP17]], [[TMP16]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP18:%.*]] = add [[TMP15]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP19:%.*]] = getelementptr i64, ptr [[P]], [[TMP18]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER2:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP19]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP20:%.*]] = add [[WIDE_MASKED_GATHER2]], shufflevector ( insertelement ( poison, i64 3, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP20]], [[TMP19]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], [[TMP5]] -; SCALABLE-NEXT: [[VEC_IND_NEXT]] = add [[VEC_IND]], [[DOTSPLAT]] -; SCALABLE-NEXT: [[TMP21:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]] -; SCALABLE-NEXT: br i1 [[TMP21]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP8:![0-9]+]] +; SCALABLE-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0 +; SCALABLE-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 3 +; SCALABLE-NEXT: [[TMP2:%.*]] = getelementptr i64, ptr [[P:%.*]], i64 [[TMP1]] +; SCALABLE-NEXT: [[TMP3:%.*]] = getelementptr i64, ptr [[TMP2]], i32 0 +; SCALABLE-NEXT: [[WIDE_VEC:%.*]] = load <12 x i64>, ptr [[TMP3]], align 8 +; SCALABLE-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <12 x i64> [[WIDE_VEC]], <12 x i64> poison, <4 x i32> +; SCALABLE-NEXT: [[TMP4:%.*]] = add <4 x i64> [[STRIDED_VEC]], +; SCALABLE-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 +; SCALABLE-NEXT: [[TMP6:%.*]] = add <4 x i64> [[STRIDED_VEC1]], +; SCALABLE-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 +; SCALABLE-NEXT: [[TMP8:%.*]] = getelementptr i64, ptr [[P]], i64 [[TMP7]] +; SCALABLE-NEXT: [[TMP9:%.*]] = add <4 x i64> [[STRIDED_VEC2]], +; SCALABLE-NEXT: [[TMP10:%.*]] = getelementptr i64, ptr [[TMP8]], i32 -2 +; SCALABLE-NEXT: [[TMP11:%.*]] = shufflevector <4 x i64> [[TMP4]], <4 x i64> [[TMP6]], <8 x i32> +; SCALABLE-NEXT: [[TMP12:%.*]] = shufflevector <4 x i64> [[TMP9]], <4 x i64> poison, <8 x i32> +; SCALABLE-NEXT: [[TMP13:%.*]] = shufflevector <8 x i64> [[TMP11]], <8 x i64> [[TMP12]], <12 x i32> +; SCALABLE-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <12 x i64> [[TMP13]], <12 x i64> poison, <12 x i32> +; SCALABLE-NEXT: store <12 x i64> [[INTERLEAVED_VEC]], ptr [[TMP10]], align 8 +; SCALABLE-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 4 +; SCALABLE-NEXT: [[TMP14:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 +; SCALABLE-NEXT: br i1 [[TMP14]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP8:![0-9]+]] ; SCALABLE: middle.block: -; SCALABLE-NEXT: [[CMP_N:%.*]] = icmp eq i64 1024, [[N_VEC]] -; SCALABLE-NEXT: br i1 [[CMP_N]], label [[EXIT:%.*]], label [[SCALAR_PH]] +; SCALABLE-NEXT: br i1 true, label [[EXIT:%.*]], label [[SCALAR_PH]] ; SCALABLE: scalar.ph: -; SCALABLE-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ] +; SCALABLE-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ 1024, [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ] ; SCALABLE-NEXT: br label [[LOOP:%.*]] ; SCALABLE: loop: ; SCALABLE-NEXT: [[I:%.*]] = phi i64 [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ], [ [[NEXTI:%.*]], [[LOOP]] ] @@ -823,79 +794,57 @@ exit: define void @load_store_factor8(ptr %p) { ; CHECK-LABEL: @load_store_factor8( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[TMP0:%.*]] = call i64 @llvm.vscale.i64() -; CHECK-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 2 -; CHECK-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 1024, [[TMP1]] -; CHECK-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]] +; CHECK-NEXT: br i1 false, label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]] ; CHECK: vector.ph: -; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.vscale.i64() -; CHECK-NEXT: [[TMP3:%.*]] = mul i64 [[TMP2]], 2 -; CHECK-NEXT: [[N_MOD_VF:%.*]] = urem i64 1024, [[TMP3]] -; CHECK-NEXT: [[N_VEC:%.*]] = sub i64 1024, [[N_MOD_VF]] -; CHECK-NEXT: [[TMP4:%.*]] = call i64 @llvm.vscale.i64() -; CHECK-NEXT: [[TMP5:%.*]] = mul i64 [[TMP4]], 2 -; CHECK-NEXT: [[TMP6:%.*]] = call @llvm.experimental.stepvector.nxv2i64() -; CHECK-NEXT: [[TMP7:%.*]] = add [[TMP6]], zeroinitializer -; CHECK-NEXT: [[TMP8:%.*]] = mul [[TMP7]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[INDUCTION:%.*]] = add zeroinitializer, [[TMP8]] -; CHECK-NEXT: [[TMP9:%.*]] = call i64 @llvm.vscale.i64() -; CHECK-NEXT: [[TMP10:%.*]] = mul i64 [[TMP9]], 2 -; CHECK-NEXT: [[TMP11:%.*]] = mul i64 1, [[TMP10]] -; CHECK-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement poison, i64 [[TMP11]], i64 0 -; CHECK-NEXT: [[DOTSPLAT:%.*]] = shufflevector [[DOTSPLATINSERT]], poison, zeroinitializer ; CHECK-NEXT: br label [[VECTOR_BODY:%.*]] ; CHECK: vector.body: ; CHECK-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ] -; CHECK-NEXT: [[VEC_IND:%.*]] = phi [ [[INDUCTION]], [[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], [[VECTOR_BODY]] ] -; CHECK-NEXT: [[TMP12:%.*]] = shl [[VEC_IND]], shufflevector ( insertelement ( poison, i64 3, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP13:%.*]] = getelementptr i64, ptr [[P:%.*]], [[TMP12]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP13]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP14:%.*]] = add [[WIDE_MASKED_GATHER]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP14]], [[TMP13]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP15:%.*]] = add [[TMP12]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP16:%.*]] = getelementptr i64, ptr [[P]], [[TMP15]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER1:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP16]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP17:%.*]] = add [[WIDE_MASKED_GATHER1]], shufflevector ( insertelement ( poison, i64 2, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP17]], [[TMP16]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP18:%.*]] = add [[TMP15]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP19:%.*]] = getelementptr i64, ptr [[P]], [[TMP18]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER2:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP19]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP20:%.*]] = add [[WIDE_MASKED_GATHER2]], shufflevector ( insertelement ( poison, i64 3, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP20]], [[TMP19]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP21:%.*]] = add [[TMP18]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP22:%.*]] = getelementptr i64, ptr [[P]], [[TMP21]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER3:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP22]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP23:%.*]] = add [[WIDE_MASKED_GATHER3]], shufflevector ( insertelement ( poison, i64 4, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP23]], [[TMP22]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP24:%.*]] = add [[TMP21]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP25:%.*]] = getelementptr i64, ptr [[P]], [[TMP24]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER4:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP25]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP26:%.*]] = add [[WIDE_MASKED_GATHER4]], shufflevector ( insertelement ( poison, i64 5, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP26]], [[TMP25]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP27:%.*]] = add [[TMP24]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP28:%.*]] = getelementptr i64, ptr [[P]], [[TMP27]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER5:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP28]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP29:%.*]] = add [[WIDE_MASKED_GATHER5]], shufflevector ( insertelement ( poison, i64 6, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP29]], [[TMP28]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP30:%.*]] = add [[TMP27]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP31:%.*]] = getelementptr i64, ptr [[P]], [[TMP30]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER6:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP31]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP32:%.*]] = add [[WIDE_MASKED_GATHER6]], shufflevector ( insertelement ( poison, i64 7, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP32]], [[TMP31]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[TMP33:%.*]] = add [[TMP30]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; CHECK-NEXT: [[TMP34:%.*]] = getelementptr i64, ptr [[P]], [[TMP33]] -; CHECK-NEXT: [[WIDE_MASKED_GATHER7:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP34]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; CHECK-NEXT: [[TMP35:%.*]] = add [[WIDE_MASKED_GATHER7]], shufflevector ( insertelement ( poison, i64 8, i64 0), poison, zeroinitializer) -; CHECK-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP35]], [[TMP34]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], [[TMP5]] -; CHECK-NEXT: [[VEC_IND_NEXT]] = add [[VEC_IND]], [[DOTSPLAT]] -; CHECK-NEXT: [[TMP36:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]] -; CHECK-NEXT: br i1 [[TMP36]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP10:![0-9]+]] +; CHECK-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0 +; CHECK-NEXT: [[TMP1:%.*]] = shl i64 [[TMP0]], 3 +; CHECK-NEXT: [[TMP2:%.*]] = getelementptr i64, ptr [[P:%.*]], i64 [[TMP1]] +; CHECK-NEXT: [[TMP3:%.*]] = getelementptr i64, ptr [[TMP2]], i32 0 +; CHECK-NEXT: [[WIDE_VEC:%.*]] = load <16 x i64>, ptr [[TMP3]], align 8 +; CHECK-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; CHECK-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; CHECK-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; CHECK-NEXT: [[STRIDED_VEC3:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; CHECK-NEXT: [[STRIDED_VEC4:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; CHECK-NEXT: [[STRIDED_VEC5:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; CHECK-NEXT: [[STRIDED_VEC6:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; CHECK-NEXT: [[STRIDED_VEC7:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; CHECK-NEXT: [[TMP4:%.*]] = add <2 x i64> [[STRIDED_VEC]], +; CHECK-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 +; CHECK-NEXT: [[TMP6:%.*]] = add <2 x i64> [[STRIDED_VEC1]], +; CHECK-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 +; CHECK-NEXT: [[TMP8:%.*]] = add <2 x i64> [[STRIDED_VEC2]], +; CHECK-NEXT: [[TMP9:%.*]] = add i64 [[TMP7]], 1 +; CHECK-NEXT: [[TMP10:%.*]] = add <2 x i64> [[STRIDED_VEC3]], +; CHECK-NEXT: [[TMP11:%.*]] = add i64 [[TMP9]], 1 +; CHECK-NEXT: [[TMP12:%.*]] = add <2 x i64> [[STRIDED_VEC4]], +; CHECK-NEXT: [[TMP13:%.*]] = add i64 [[TMP11]], 1 +; CHECK-NEXT: [[TMP14:%.*]] = add <2 x i64> [[STRIDED_VEC5]], +; CHECK-NEXT: [[TMP15:%.*]] = add i64 [[TMP13]], 1 +; CHECK-NEXT: [[TMP16:%.*]] = add <2 x i64> [[STRIDED_VEC6]], +; CHECK-NEXT: [[TMP17:%.*]] = add i64 [[TMP15]], 1 +; CHECK-NEXT: [[TMP18:%.*]] = getelementptr i64, ptr [[P]], i64 [[TMP17]] +; CHECK-NEXT: [[TMP19:%.*]] = add <2 x i64> [[STRIDED_VEC7]], +; CHECK-NEXT: [[TMP20:%.*]] = getelementptr i64, ptr [[TMP18]], i32 -7 +; CHECK-NEXT: [[TMP21:%.*]] = shufflevector <2 x i64> [[TMP4]], <2 x i64> [[TMP6]], <4 x i32> +; CHECK-NEXT: [[TMP22:%.*]] = shufflevector <2 x i64> [[TMP8]], <2 x i64> [[TMP10]], <4 x i32> +; CHECK-NEXT: [[TMP23:%.*]] = shufflevector <2 x i64> [[TMP12]], <2 x i64> [[TMP14]], <4 x i32> +; CHECK-NEXT: [[TMP24:%.*]] = shufflevector <2 x i64> [[TMP16]], <2 x i64> [[TMP19]], <4 x i32> +; CHECK-NEXT: [[TMP25:%.*]] = shufflevector <4 x i64> [[TMP21]], <4 x i64> [[TMP22]], <8 x i32> +; CHECK-NEXT: [[TMP26:%.*]] = shufflevector <4 x i64> [[TMP23]], <4 x i64> [[TMP24]], <8 x i32> +; CHECK-NEXT: [[TMP27:%.*]] = shufflevector <8 x i64> [[TMP25]], <8 x i64> [[TMP26]], <16 x i32> +; CHECK-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <16 x i64> [[TMP27]], <16 x i64> poison, <16 x i32> +; CHECK-NEXT: store <16 x i64> [[INTERLEAVED_VEC]], ptr [[TMP20]], align 8 +; CHECK-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2 +; CHECK-NEXT: [[TMP28:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 +; CHECK-NEXT: br i1 [[TMP28]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP10:![0-9]+]] ; CHECK: middle.block: -; CHECK-NEXT: [[CMP_N:%.*]] = icmp eq i64 1024, [[N_VEC]] -; CHECK-NEXT: br i1 [[CMP_N]], label [[EXIT:%.*]], label [[SCALAR_PH]] +; CHECK-NEXT: br i1 true, label [[EXIT:%.*]], label [[SCALAR_PH]] ; CHECK: scalar.ph: -; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ] +; CHECK-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ 1024, [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ] ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[I:%.*]] = phi i64 [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ], [ [[NEXTI:%.*]], [[LOOP]] ] @@ -952,51 +901,48 @@ define void @load_store_factor8(ptr %p) { ; FIXED-NEXT: br label [[VECTOR_BODY:%.*]] ; FIXED: vector.body: ; FIXED-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ] -; FIXED-NEXT: [[VEC_IND:%.*]] = phi <4 x i64> [ , [[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], [[VECTOR_BODY]] ] -; FIXED-NEXT: [[TMP0:%.*]] = shl <4 x i64> [[VEC_IND]], -; FIXED-NEXT: [[TMP1:%.*]] = getelementptr i64, ptr [[P:%.*]], <4 x i64> [[TMP0]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP1]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP2:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP2]], <4 x ptr> [[TMP1]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP3:%.*]] = add <4 x i64> [[TMP0]], -; FIXED-NEXT: [[TMP4:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP3]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER1:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP4]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP5:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER1]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP5]], <4 x ptr> [[TMP4]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP6:%.*]] = add <4 x i64> [[TMP3]], -; FIXED-NEXT: [[TMP7:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP6]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER2:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP7]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP8:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER2]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP8]], <4 x ptr> [[TMP7]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP9:%.*]] = add <4 x i64> [[TMP6]], -; FIXED-NEXT: [[TMP10:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP9]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER3:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP10]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP11:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER3]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP11]], <4 x ptr> [[TMP10]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP12:%.*]] = add <4 x i64> [[TMP9]], -; FIXED-NEXT: [[TMP13:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP12]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER4:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP13]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP14:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER4]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP14]], <4 x ptr> [[TMP13]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP15:%.*]] = add <4 x i64> [[TMP12]], -; FIXED-NEXT: [[TMP16:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP15]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER5:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP16]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP17:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER5]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP17]], <4 x ptr> [[TMP16]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP18:%.*]] = add <4 x i64> [[TMP15]], -; FIXED-NEXT: [[TMP19:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP18]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER6:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP19]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP20:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER6]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP20]], <4 x ptr> [[TMP19]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[TMP21:%.*]] = add <4 x i64> [[TMP18]], -; FIXED-NEXT: [[TMP22:%.*]] = getelementptr i64, ptr [[P]], <4 x i64> [[TMP21]] -; FIXED-NEXT: [[WIDE_MASKED_GATHER7:%.*]] = call <4 x i64> @llvm.masked.gather.v4i64.v4p0(<4 x ptr> [[TMP22]], i32 8, <4 x i1> , <4 x i64> poison) -; FIXED-NEXT: [[TMP23:%.*]] = add <4 x i64> [[WIDE_MASKED_GATHER7]], -; FIXED-NEXT: call void @llvm.masked.scatter.v4i64.v4p0(<4 x i64> [[TMP23]], <4 x ptr> [[TMP22]], i32 8, <4 x i1> ) -; FIXED-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 4 -; FIXED-NEXT: [[VEC_IND_NEXT]] = add <4 x i64> [[VEC_IND]], -; FIXED-NEXT: [[TMP24:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 -; FIXED-NEXT: br i1 [[TMP24]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP10:![0-9]+]] +; FIXED-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0 +; FIXED-NEXT: [[TMP1:%.*]] = shl i64 [[TMP0]], 3 +; FIXED-NEXT: [[TMP2:%.*]] = getelementptr i64, ptr [[P:%.*]], i64 [[TMP1]] +; FIXED-NEXT: [[TMP3:%.*]] = getelementptr i64, ptr [[TMP2]], i32 0 +; FIXED-NEXT: [[WIDE_VEC:%.*]] = load <16 x i64>, ptr [[TMP3]], align 8 +; FIXED-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; FIXED-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; FIXED-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; FIXED-NEXT: [[STRIDED_VEC3:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; FIXED-NEXT: [[STRIDED_VEC4:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; FIXED-NEXT: [[STRIDED_VEC5:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; FIXED-NEXT: [[STRIDED_VEC6:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; FIXED-NEXT: [[STRIDED_VEC7:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; FIXED-NEXT: [[TMP4:%.*]] = add <2 x i64> [[STRIDED_VEC]], +; FIXED-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 +; FIXED-NEXT: [[TMP6:%.*]] = add <2 x i64> [[STRIDED_VEC1]], +; FIXED-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 +; FIXED-NEXT: [[TMP8:%.*]] = add <2 x i64> [[STRIDED_VEC2]], +; FIXED-NEXT: [[TMP9:%.*]] = add i64 [[TMP7]], 1 +; FIXED-NEXT: [[TMP10:%.*]] = add <2 x i64> [[STRIDED_VEC3]], +; FIXED-NEXT: [[TMP11:%.*]] = add i64 [[TMP9]], 1 +; FIXED-NEXT: [[TMP12:%.*]] = add <2 x i64> [[STRIDED_VEC4]], +; FIXED-NEXT: [[TMP13:%.*]] = add i64 [[TMP11]], 1 +; FIXED-NEXT: [[TMP14:%.*]] = add <2 x i64> [[STRIDED_VEC5]], +; FIXED-NEXT: [[TMP15:%.*]] = add i64 [[TMP13]], 1 +; FIXED-NEXT: [[TMP16:%.*]] = add <2 x i64> [[STRIDED_VEC6]], +; FIXED-NEXT: [[TMP17:%.*]] = add i64 [[TMP15]], 1 +; FIXED-NEXT: [[TMP18:%.*]] = getelementptr i64, ptr [[P]], i64 [[TMP17]] +; FIXED-NEXT: [[TMP19:%.*]] = add <2 x i64> [[STRIDED_VEC7]], +; FIXED-NEXT: [[TMP20:%.*]] = getelementptr i64, ptr [[TMP18]], i32 -7 +; FIXED-NEXT: [[TMP21:%.*]] = shufflevector <2 x i64> [[TMP4]], <2 x i64> [[TMP6]], <4 x i32> +; FIXED-NEXT: [[TMP22:%.*]] = shufflevector <2 x i64> [[TMP8]], <2 x i64> [[TMP10]], <4 x i32> +; FIXED-NEXT: [[TMP23:%.*]] = shufflevector <2 x i64> [[TMP12]], <2 x i64> [[TMP14]], <4 x i32> +; FIXED-NEXT: [[TMP24:%.*]] = shufflevector <2 x i64> [[TMP16]], <2 x i64> [[TMP19]], <4 x i32> +; FIXED-NEXT: [[TMP25:%.*]] = shufflevector <4 x i64> [[TMP21]], <4 x i64> [[TMP22]], <8 x i32> +; FIXED-NEXT: [[TMP26:%.*]] = shufflevector <4 x i64> [[TMP23]], <4 x i64> [[TMP24]], <8 x i32> +; FIXED-NEXT: [[TMP27:%.*]] = shufflevector <8 x i64> [[TMP25]], <8 x i64> [[TMP26]], <16 x i32> +; FIXED-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <16 x i64> [[TMP27]], <16 x i64> poison, <16 x i32> +; FIXED-NEXT: store <16 x i64> [[INTERLEAVED_VEC]], ptr [[TMP20]], align 8 +; FIXED-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2 +; FIXED-NEXT: [[TMP28:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 +; FIXED-NEXT: br i1 [[TMP28]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP10:![0-9]+]] ; FIXED: middle.block: ; FIXED-NEXT: br i1 true, label [[EXIT:%.*]], label [[SCALAR_PH]] ; FIXED: scalar.ph: @@ -1052,79 +998,57 @@ define void @load_store_factor8(ptr %p) { ; ; SCALABLE-LABEL: @load_store_factor8( ; SCALABLE-NEXT: entry: -; SCALABLE-NEXT: [[TMP0:%.*]] = call i64 @llvm.vscale.i64() -; SCALABLE-NEXT: [[TMP1:%.*]] = mul i64 [[TMP0]], 2 -; SCALABLE-NEXT: [[MIN_ITERS_CHECK:%.*]] = icmp ult i64 1024, [[TMP1]] -; SCALABLE-NEXT: br i1 [[MIN_ITERS_CHECK]], label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]] +; SCALABLE-NEXT: br i1 false, label [[SCALAR_PH:%.*]], label [[VECTOR_PH:%.*]] ; SCALABLE: vector.ph: -; SCALABLE-NEXT: [[TMP2:%.*]] = call i64 @llvm.vscale.i64() -; SCALABLE-NEXT: [[TMP3:%.*]] = mul i64 [[TMP2]], 2 -; SCALABLE-NEXT: [[N_MOD_VF:%.*]] = urem i64 1024, [[TMP3]] -; SCALABLE-NEXT: [[N_VEC:%.*]] = sub i64 1024, [[N_MOD_VF]] -; SCALABLE-NEXT: [[TMP4:%.*]] = call i64 @llvm.vscale.i64() -; SCALABLE-NEXT: [[TMP5:%.*]] = mul i64 [[TMP4]], 2 -; SCALABLE-NEXT: [[TMP6:%.*]] = call @llvm.experimental.stepvector.nxv2i64() -; SCALABLE-NEXT: [[TMP7:%.*]] = add [[TMP6]], zeroinitializer -; SCALABLE-NEXT: [[TMP8:%.*]] = mul [[TMP7]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[INDUCTION:%.*]] = add zeroinitializer, [[TMP8]] -; SCALABLE-NEXT: [[TMP9:%.*]] = call i64 @llvm.vscale.i64() -; SCALABLE-NEXT: [[TMP10:%.*]] = mul i64 [[TMP9]], 2 -; SCALABLE-NEXT: [[TMP11:%.*]] = mul i64 1, [[TMP10]] -; SCALABLE-NEXT: [[DOTSPLATINSERT:%.*]] = insertelement poison, i64 [[TMP11]], i64 0 -; SCALABLE-NEXT: [[DOTSPLAT:%.*]] = shufflevector [[DOTSPLATINSERT]], poison, zeroinitializer ; SCALABLE-NEXT: br label [[VECTOR_BODY:%.*]] ; SCALABLE: vector.body: ; SCALABLE-NEXT: [[INDEX:%.*]] = phi i64 [ 0, [[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], [[VECTOR_BODY]] ] -; SCALABLE-NEXT: [[VEC_IND:%.*]] = phi [ [[INDUCTION]], [[VECTOR_PH]] ], [ [[VEC_IND_NEXT:%.*]], [[VECTOR_BODY]] ] -; SCALABLE-NEXT: [[TMP12:%.*]] = shl [[VEC_IND]], shufflevector ( insertelement ( poison, i64 3, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP13:%.*]] = getelementptr i64, ptr [[P:%.*]], [[TMP12]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP13]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP14:%.*]] = add [[WIDE_MASKED_GATHER]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP14]], [[TMP13]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP15:%.*]] = add [[TMP12]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP16:%.*]] = getelementptr i64, ptr [[P]], [[TMP15]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER1:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP16]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP17:%.*]] = add [[WIDE_MASKED_GATHER1]], shufflevector ( insertelement ( poison, i64 2, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP17]], [[TMP16]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP18:%.*]] = add [[TMP15]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP19:%.*]] = getelementptr i64, ptr [[P]], [[TMP18]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER2:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP19]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP20:%.*]] = add [[WIDE_MASKED_GATHER2]], shufflevector ( insertelement ( poison, i64 3, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP20]], [[TMP19]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP21:%.*]] = add [[TMP18]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP22:%.*]] = getelementptr i64, ptr [[P]], [[TMP21]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER3:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP22]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP23:%.*]] = add [[WIDE_MASKED_GATHER3]], shufflevector ( insertelement ( poison, i64 4, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP23]], [[TMP22]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP24:%.*]] = add [[TMP21]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP25:%.*]] = getelementptr i64, ptr [[P]], [[TMP24]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER4:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP25]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP26:%.*]] = add [[WIDE_MASKED_GATHER4]], shufflevector ( insertelement ( poison, i64 5, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP26]], [[TMP25]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP27:%.*]] = add [[TMP24]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP28:%.*]] = getelementptr i64, ptr [[P]], [[TMP27]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER5:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP28]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP29:%.*]] = add [[WIDE_MASKED_GATHER5]], shufflevector ( insertelement ( poison, i64 6, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP29]], [[TMP28]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP30:%.*]] = add [[TMP27]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP31:%.*]] = getelementptr i64, ptr [[P]], [[TMP30]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER6:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP31]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP32:%.*]] = add [[WIDE_MASKED_GATHER6]], shufflevector ( insertelement ( poison, i64 7, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP32]], [[TMP31]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[TMP33:%.*]] = add [[TMP30]], shufflevector ( insertelement ( poison, i64 1, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: [[TMP34:%.*]] = getelementptr i64, ptr [[P]], [[TMP33]] -; SCALABLE-NEXT: [[WIDE_MASKED_GATHER7:%.*]] = call @llvm.masked.gather.nxv2i64.nxv2p0( [[TMP34]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer), poison) -; SCALABLE-NEXT: [[TMP35:%.*]] = add [[WIDE_MASKED_GATHER7]], shufflevector ( insertelement ( poison, i64 8, i64 0), poison, zeroinitializer) -; SCALABLE-NEXT: call void @llvm.masked.scatter.nxv2i64.nxv2p0( [[TMP35]], [[TMP34]], i32 8, shufflevector ( insertelement ( poison, i1 true, i64 0), poison, zeroinitializer)) -; SCALABLE-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], [[TMP5]] -; SCALABLE-NEXT: [[VEC_IND_NEXT]] = add [[VEC_IND]], [[DOTSPLAT]] -; SCALABLE-NEXT: [[TMP36:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]] -; SCALABLE-NEXT: br i1 [[TMP36]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP10:![0-9]+]] +; SCALABLE-NEXT: [[TMP0:%.*]] = add i64 [[INDEX]], 0 +; SCALABLE-NEXT: [[TMP1:%.*]] = shl i64 [[TMP0]], 3 +; SCALABLE-NEXT: [[TMP2:%.*]] = getelementptr i64, ptr [[P:%.*]], i64 [[TMP1]] +; SCALABLE-NEXT: [[TMP3:%.*]] = getelementptr i64, ptr [[TMP2]], i32 0 +; SCALABLE-NEXT: [[WIDE_VEC:%.*]] = load <16 x i64>, ptr [[TMP3]], align 8 +; SCALABLE-NEXT: [[STRIDED_VEC:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC1:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC2:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC3:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC4:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC5:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC6:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; SCALABLE-NEXT: [[STRIDED_VEC7:%.*]] = shufflevector <16 x i64> [[WIDE_VEC]], <16 x i64> poison, <2 x i32> +; SCALABLE-NEXT: [[TMP4:%.*]] = add <2 x i64> [[STRIDED_VEC]], +; SCALABLE-NEXT: [[TMP5:%.*]] = add i64 [[TMP1]], 1 +; SCALABLE-NEXT: [[TMP6:%.*]] = add <2 x i64> [[STRIDED_VEC1]], +; SCALABLE-NEXT: [[TMP7:%.*]] = add i64 [[TMP5]], 1 +; SCALABLE-NEXT: [[TMP8:%.*]] = add <2 x i64> [[STRIDED_VEC2]], +; SCALABLE-NEXT: [[TMP9:%.*]] = add i64 [[TMP7]], 1 +; SCALABLE-NEXT: [[TMP10:%.*]] = add <2 x i64> [[STRIDED_VEC3]], +; SCALABLE-NEXT: [[TMP11:%.*]] = add i64 [[TMP9]], 1 +; SCALABLE-NEXT: [[TMP12:%.*]] = add <2 x i64> [[STRIDED_VEC4]], +; SCALABLE-NEXT: [[TMP13:%.*]] = add i64 [[TMP11]], 1 +; SCALABLE-NEXT: [[TMP14:%.*]] = add <2 x i64> [[STRIDED_VEC5]], +; SCALABLE-NEXT: [[TMP15:%.*]] = add i64 [[TMP13]], 1 +; SCALABLE-NEXT: [[TMP16:%.*]] = add <2 x i64> [[STRIDED_VEC6]], +; SCALABLE-NEXT: [[TMP17:%.*]] = add i64 [[TMP15]], 1 +; SCALABLE-NEXT: [[TMP18:%.*]] = getelementptr i64, ptr [[P]], i64 [[TMP17]] +; SCALABLE-NEXT: [[TMP19:%.*]] = add <2 x i64> [[STRIDED_VEC7]], +; SCALABLE-NEXT: [[TMP20:%.*]] = getelementptr i64, ptr [[TMP18]], i32 -7 +; SCALABLE-NEXT: [[TMP21:%.*]] = shufflevector <2 x i64> [[TMP4]], <2 x i64> [[TMP6]], <4 x i32> +; SCALABLE-NEXT: [[TMP22:%.*]] = shufflevector <2 x i64> [[TMP8]], <2 x i64> [[TMP10]], <4 x i32> +; SCALABLE-NEXT: [[TMP23:%.*]] = shufflevector <2 x i64> [[TMP12]], <2 x i64> [[TMP14]], <4 x i32> +; SCALABLE-NEXT: [[TMP24:%.*]] = shufflevector <2 x i64> [[TMP16]], <2 x i64> [[TMP19]], <4 x i32> +; SCALABLE-NEXT: [[TMP25:%.*]] = shufflevector <4 x i64> [[TMP21]], <4 x i64> [[TMP22]], <8 x i32> +; SCALABLE-NEXT: [[TMP26:%.*]] = shufflevector <4 x i64> [[TMP23]], <4 x i64> [[TMP24]], <8 x i32> +; SCALABLE-NEXT: [[TMP27:%.*]] = shufflevector <8 x i64> [[TMP25]], <8 x i64> [[TMP26]], <16 x i32> +; SCALABLE-NEXT: [[INTERLEAVED_VEC:%.*]] = shufflevector <16 x i64> [[TMP27]], <16 x i64> poison, <16 x i32> +; SCALABLE-NEXT: store <16 x i64> [[INTERLEAVED_VEC]], ptr [[TMP20]], align 8 +; SCALABLE-NEXT: [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 2 +; SCALABLE-NEXT: [[TMP28:%.*]] = icmp eq i64 [[INDEX_NEXT]], 1024 +; SCALABLE-NEXT: br i1 [[TMP28]], label [[MIDDLE_BLOCK:%.*]], label [[VECTOR_BODY]], !llvm.loop [[LOOP10:![0-9]+]] ; SCALABLE: middle.block: -; SCALABLE-NEXT: [[CMP_N:%.*]] = icmp eq i64 1024, [[N_VEC]] -; SCALABLE-NEXT: br i1 [[CMP_N]], label [[EXIT:%.*]], label [[SCALAR_PH]] +; SCALABLE-NEXT: br i1 true, label [[EXIT:%.*]], label [[SCALAR_PH]] ; SCALABLE: scalar.ph: -; SCALABLE-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ] +; SCALABLE-NEXT: [[BC_RESUME_VAL:%.*]] = phi i64 [ 1024, [[MIDDLE_BLOCK]] ], [ 0, [[ENTRY:%.*]] ] ; SCALABLE-NEXT: br label [[LOOP:%.*]] ; SCALABLE: loop: ; SCALABLE-NEXT: [[I:%.*]] = phi i64 [ [[BC_RESUME_VAL]], [[SCALAR_PH]] ], [ [[NEXTI:%.*]], [[LOOP]] ] diff --git a/llvm/test/Transforms/LoopVectorize/RISCV/interleaved-cost.ll b/llvm/test/Transforms/LoopVectorize/RISCV/interleaved-cost.ll index a724ef87abb3..7bfd2eaad574 100644 --- a/llvm/test/Transforms/LoopVectorize/RISCV/interleaved-cost.ll +++ b/llvm/test/Transforms/LoopVectorize/RISCV/interleaved-cost.ll @@ -72,12 +72,12 @@ entry: ; VF_8: Found an estimated cost of 0 for VF 8 For instruction: store i8 %a1, ptr %p1, align 1 ; VF_8-NEXT: Found an estimated cost of 3 for VF 8 For instruction: store i8 %a2, ptr %p2, align 1 ; VF_16-LABEL: Checking a loop in 'i8_factor_3' -; VF_16: Found an estimated cost of 48 for VF 16 For instruction: %l0 = load i8, ptr %p0, align 1 +; VF_16: Found an estimated cost of 5 for VF 16 For instruction: %l0 = load i8, ptr %p0, align 1 ; VF_16-NEXT: Found an estimated cost of 0 for VF 16 For instruction: %l1 = load i8, ptr %p1, align 1 ; VF_16-NEXT: Found an estimated cost of 0 for VF 16 For instruction: %l2 = load i8, ptr %p2, align 1 ; VF_16: Found an estimated cost of 0 for VF 16 For instruction: store i8 %a0, ptr %p0, align 1 ; VF_16: Found an estimated cost of 0 for VF 16 For instruction: store i8 %a1, ptr %p1, align 1 -; VF_16-NEXT: Found an estimated cost of 48 for VF 16 For instruction: store i8 %a2, ptr %p2, align 1 +; VF_16-NEXT: Found an estimated cost of 5 for VF 16 For instruction: store i8 %a2, ptr %p2, align 1 for.body: %i = phi i64 [ 0, %entry ], [ %i.next, %for.body ] %p0 = getelementptr inbounds %i8.3, ptr %data, i64 %i, i32 0 -- GitLab From 6bf185920bd6831efc151d7d4158d6390006c50b Mon Sep 17 00:00:00 2001 From: Lang Hames Date: Thu, 16 May 2024 14:44:58 +1000 Subject: [PATCH 071/403] [ORC] Support visionOS in LC_BUILD_VERSIONs for JITDylibs. rdar://127846581 --- llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp b/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp index 2b397b2d48e7..b477a48af290 100644 --- a/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp +++ b/llvm/lib/ExecutionEngine/Orc/MachOPlatform.cpp @@ -277,6 +277,10 @@ MachOPlatform::HeaderOptions::BuildVersionOpts::fromTriple(const Triple &TT, Platform = TT.isSimulatorEnvironment() ? MachO::PLATFORM_WATCHOSSIMULATOR : MachO::PLATFORM_WATCHOS; break; + case Triple::XROS: + Platform = TT.isSimulatorEnvironment() ? MachO::PLATFORM_XROS_SIMULATOR + : MachO::PLATFORM_XROS; + break; default: return std::nullopt; } -- GitLab From 566fbb450092bf8c9f966a6ab1b0381626e3e535 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 16 May 2024 12:57:28 +0800 Subject: [PATCH 072/403] [RISCV] Defer creating RISCVInsertVSETVLI to avoid leak with -stop-after (#92303) As noted in https://github.com/llvm/llvm-project/pull/91440#discussion_r1601976425, if the pass pipeline stops early because of -stop-after any allocated passes added with insertPass will not be freed if they haven't already been added. This was showing up as a failure on the address sanitizer buildbots. We can fix it by instead passing the pass ID instead so that allocation is deferred. --- llvm/lib/Target/RISCV/RISCV.h | 1 + llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp | 1 + llvm/lib/Target/RISCV/RISCVTargetMachine.cpp | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCV.h b/llvm/lib/Target/RISCV/RISCV.h index d405395dcf9e..2b8688c5de61 100644 --- a/llvm/lib/Target/RISCV/RISCV.h +++ b/llvm/lib/Target/RISCV/RISCV.h @@ -60,6 +60,7 @@ void initializeRISCVExpandAtomicPseudoPass(PassRegistry &); FunctionPass *createRISCVInsertVSETVLIPass(); void initializeRISCVInsertVSETVLIPass(PassRegistry &); +extern char &RISCVInsertVSETVLIID; FunctionPass *createRISCVCoalesceVSETVLIPass(); void initializeRISCVCoalesceVSETVLIPass(PassRegistry &); diff --git a/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp b/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp index 363007d7b68b..324ce5cb5ed7 100644 --- a/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp +++ b/llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp @@ -868,6 +868,7 @@ private: } // end anonymous namespace char RISCVInsertVSETVLI::ID = 0; +char &llvm::RISCVInsertVSETVLIID = RISCVInsertVSETVLI::ID; INITIALIZE_PASS(RISCVInsertVSETVLI, DEBUG_TYPE, RISCV_INSERT_VSETVLI_NAME, false, false) diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp index 5d598a275a00..5aab138dae40 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -548,9 +548,9 @@ void RISCVPassConfig::addPreRegAlloc() { // Run RISCVInsertVSETVLI after PHI elimination. On O1 and above do it after // register coalescing so needVSETVLIPHI doesn't need to look through COPYs. if (TM->getOptLevel() == CodeGenOptLevel::None) - insertPass(&PHIEliminationID, createRISCVInsertVSETVLIPass()); + insertPass(&PHIEliminationID, &RISCVInsertVSETVLIID); else - insertPass(&RegisterCoalescerID, createRISCVInsertVSETVLIPass()); + insertPass(&RegisterCoalescerID, &RISCVInsertVSETVLIID); } void RISCVPassConfig::addFastRegAlloc() { -- GitLab From 70a926cfb1d4af326be5afe6419991aeff8f44b2 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov Date: Thu, 16 May 2024 02:39:04 -0300 Subject: [PATCH 073/403] [clang] NFC: Add a few more interesting test cases for CWG2398 --- clang/test/SemaTemplate/cwg2398.cpp | 58 +++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/clang/test/SemaTemplate/cwg2398.cpp b/clang/test/SemaTemplate/cwg2398.cpp index a20155486b12..d163354b2e5f 100644 --- a/clang/test/SemaTemplate/cwg2398.cpp +++ b/clang/test/SemaTemplate/cwg2398.cpp @@ -137,3 +137,61 @@ namespace ttp_defaults { // old-error@-2 {{template template argument has different template parameters}} // old-error@-3 {{explicit instantiation of 'f' does not refer to a function template}} } // namespace ttp_defaults + +namespace ttp_only { + template