From 87f2433bc16591d7e126fc19c22ea06666d80b9f Mon Sep 17 00:00:00 2001 From: NimishMishra Date: Sun, 14 Dec 2025 11:42:13 +0530 Subject: [PATCH 1/2] [flang][mlir] Add support for implicit linearization in omp.simd --- flang/lib/Lower/OpenMP/OpenMP.cpp | 28 ++++++++++++- flang/lib/Semantics/resolve-directives.cpp | 6 ++- .../Lower/OpenMP/parallel-private-clause.f90 | 2 +- flang/test/Lower/OpenMP/simd-linear.f90 | 36 +++++++++++++++- .../OpenMP/OpenMPToLLVMIRTranslation.cpp | 41 +++++++++++++------ 5 files changed, 96 insertions(+), 17 deletions(-) diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 582e684442dfc..9beaf7ba30ba4 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -3025,9 +3025,35 @@ genStandaloneSimd(lower::AbstractConverter &converter, lower::SymMap &symTable, simdArgs.priv.vars = simdClauseOps.privateVars; simdArgs.reduction.syms = simdReductionSyms; simdArgs.reduction.vars = simdClauseOps.reductionVars; + + std::vector typeAttrs; + // If attributes from explicit `linear(...)` clause are present, + // carry them forward. + if (simdClauseOps.linearVarTypes && !simdClauseOps.linearVarTypes.empty()) + typeAttrs.assign(simdClauseOps.linearVarTypes.begin(), + simdClauseOps.linearVarTypes.end()); + + for (auto [loopVar, loopStep] : llvm::zip(iv, loopNestClauseOps.loopSteps)) { + // TODO: Implicit linearization is skipped if iv is a pointer + // or an allocatable, due to potential mismatch between the linear + // variable type (example !fir.ref>>) + // and the linear step size (example: i64). Handle this type mismatch + // gracefully. + if (loopVar->test(Fortran::semantics::Symbol::Flag::OmpLinear) && + !(Fortran::semantics::IsAllocatableOrPointer(*loopVar) || + Fortran::semantics::IsAllocatableOrPointer(loopVar->GetUltimate()))) { + const mlir::Value variable = converter.getSymbolAddress(*loopVar); + mlir::Type ty = converter.genType(*loopVar); + typeAttrs.push_back(mlir::TypeAttr::get(ty)); + simdClauseOps.linearVars.push_back(variable); + simdClauseOps.linearStepVars.push_back(loopStep); + } + } + simdClauseOps.linearVarTypes = + mlir::ArrayAttr::get(&converter.getMLIRContext(), typeAttrs); + auto simdOp = genWrapperOp(converter, loc, simdClauseOps, simdArgs); - genLoopNestOp(converter, symTable, semaCtx, eval, loc, queue, item, loopNestClauseOps, iv, {{simdOp, simdArgs}}, llvm::omp::Directive::OMPD_simd, dsp); diff --git a/flang/lib/Semantics/resolve-directives.cpp b/flang/lib/Semantics/resolve-directives.cpp index 6211643b08970..faf8bc2ba90f1 100644 --- a/flang/lib/Semantics/resolve-directives.cpp +++ b/flang/lib/Semantics/resolve-directives.cpp @@ -2357,7 +2357,7 @@ void OmpAttributeVisitor::CheckPerfectNestAndRectangularLoop( // parallel do, taskloop, or distribute construct is (are) private. // - The loop iteration variable in the associated do-loop of a simd construct // with just one associated do-loop is linear with a linear-step that is the -// increment of the associated do-loop. +// increment of the associated do-loop (only for OpenMP versions <= 4.5) // - The loop iteration variables in the associated do-loops of a simd // construct with multiple associated do-loops are lastprivate. void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( @@ -2367,10 +2367,12 @@ void OmpAttributeVisitor::PrivatizeAssociatedLoopIndexAndCheckLoopLevel( return; } Symbol::Flag ivDSA; + unsigned version{context_.langOptions().OpenMPVersion}; if (!llvm::omp::allSimdSet.test(GetContext().directive)) { ivDSA = Symbol::Flag::OmpPrivate; } else if (level == 1) { - ivDSA = Symbol::Flag::OmpLinear; + if (version <= 45) + ivDSA = Symbol::Flag::OmpLinear; } else { ivDSA = Symbol::Flag::OmpLastPrivate; } diff --git a/flang/test/Lower/OpenMP/parallel-private-clause.f90 b/flang/test/Lower/OpenMP/parallel-private-clause.f90 index 3a7fc22c0289b..a198ca8d09867 100644 --- a/flang/test/Lower/OpenMP/parallel-private-clause.f90 +++ b/flang/test/Lower/OpenMP/parallel-private-clause.f90 @@ -349,7 +349,7 @@ subroutine simd_loop_1 ! FIRDialect: %[[UB:.*]] = arith.constant 9 : i32 ! FIRDialect: %[[STEP:.*]] = arith.constant 1 : i32 - ! FIRDialect: omp.simd private({{.*}}) { + ! FIRDialect: omp.simd linear({{.*}} = %[[STEP]] : !fir.ref) private({{.*}}) { ! FIRDialect-NEXT: omp.loop_nest (%[[I:.*]]) : i32 = (%[[LB]]) to (%[[UB]]) inclusive step (%[[STEP]]) { !$OMP SIMD PRIVATE(r) do i=1, 9 diff --git a/flang/test/Lower/OpenMP/simd-linear.f90 b/flang/test/Lower/OpenMP/simd-linear.f90 index b6c7668af998b..794c9cfa8324c 100644 --- a/flang/test/Lower/OpenMP/simd-linear.f90 +++ b/flang/test/Lower/OpenMP/simd-linear.f90 @@ -1,15 +1,24 @@ ! This test checks lowering of OpenMP SIMD Directive ! with linear clause -! RUN: %flang_fc1 -fopenmp -emit-hlfir %s -o - 2>&1 | FileCheck %s +! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=50 %s -o - 2>&1 | FileCheck %s +! RUN: %flang_fc1 -fopenmp -emit-hlfir -fopenmp-version=45 %s -o - 2>&1 | FileCheck %s --check-prefix=IMPLICIT !CHECK: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFsimple_linearEx"} !CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFsimple_linearEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) !CHECK: %[[const:.*]] = arith.constant 1 : i32 + +!IMPLICIT: %[[I_ALLOCA:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFsimple_linearEi"} +!IMPLICIT: %[[I:.*]]:2 = hlfir.declare %[[I_ALLOCA]] {{.*}} +!IMPLICIT: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFsimple_linearEx"} +!IMPLICIT: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFsimple_linearEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!IMPLICIT: %[[const:.*]] = arith.constant 1 : i32 subroutine simple_linear implicit none integer :: x, y, i !CHECK: omp.simd linear(%[[X]]#0 = %[[const]] : !fir.ref) {{.*}} + + !IMPLICIT: omp.simd linear(%[[X]]#0 = %[[const]] : !fir.ref, %[[I]]#0 = %{{.*}} : !fir.ref) {{.*}} !$omp simd linear(x) !CHECK: %[[LOAD:.*]] = fir.load %[[X]]#0 : !fir.ref !CHECK: %[[const:.*]] = arith.constant 2 : i32 @@ -18,16 +27,25 @@ subroutine simple_linear y = x + 2 end do !CHECK: } {linear_var_types = [i32]} + !IMPLICIT: } {linear_var_types = [i32, i32]} end subroutine !CHECK: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFlinear_stepEx"} !CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFlinear_stepEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) + +!IMPLICIT: %[[I_ALLOCA:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFlinear_stepEi"} +!IMPLICIT: %[[I:.*]]:2 = hlfir.declare %[[I_ALLOCA]] {{.*}} +!IMPLICIT: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFlinear_stepEx"} +!IMPLICIT: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFlinear_stepEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!IMPLICIT: %[[const:.*]] = arith.constant 4 : i32 subroutine linear_step implicit none integer :: x, y, i !CHECK: %[[const:.*]] = arith.constant 4 : i32 !CHECK: omp.simd linear(%[[X]]#0 = %[[const]] : !fir.ref) {{.*}} + + !IMPLICIT: omp.simd linear(%[[X]]#0 = %[[const]] : !fir.ref, %[[I]]#0 = %{{.*}} : !fir.ref) {{.*}} !$omp simd linear(x:4) !CHECK: %[[LOAD:.*]] = fir.load %[[X]]#0 : !fir.ref !CHECK: %[[const:.*]] = arith.constant 2 : i32 @@ -36,22 +54,38 @@ subroutine linear_step y = x + 2 end do !CHECK: } {linear_var_types = [i32]} + !IMPLICIT: } {linear_var_types = [i32, i32]} end subroutine !CHECK: %[[A_alloca:.*]] = fir.alloca i32 {bindc_name = "a", uniq_name = "_QFlinear_exprEa"} !CHECK: %[[A:.*]]:2 = hlfir.declare %[[A_alloca]] {uniq_name = "_QFlinear_exprEa"} : (!fir.ref) -> (!fir.ref, !fir.ref) !CHECK: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFlinear_exprEx"} !CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFlinear_exprEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) + +!IMPLICIT: %[[A_alloca:.*]] = fir.alloca i32 {bindc_name = "a", uniq_name = "_QFlinear_exprEa"} +!IMPLICIT: %[[A:.*]]:2 = hlfir.declare %[[A_alloca]] {uniq_name = "_QFlinear_exprEa"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!IMPLICIT: %[[I_ALLOCA:.*]] = fir.alloca i32 {bindc_name = "i", uniq_name = "_QFlinear_exprEi"} +!IMPLICIT: %[[I:.*]]:2 = hlfir.declare %[[I_ALLOCA]] {uniq_name = "_QFlinear_exprEi"} : (!fir.ref) -> (!fir.ref, !fir.ref) +!IMPLICIT: %[[X_alloca:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFlinear_exprEx"} +!IMPLICIT: %[[X:.*]]:2 = hlfir.declare %[[X_alloca]] {uniq_name = "_QFlinear_exprEx"} : (!fir.ref) -> (!fir.ref, !fir.ref) subroutine linear_expr implicit none integer :: x, y, i, a !CHECK: %[[LOAD_A:.*]] = fir.load %[[A]]#0 : !fir.ref !CHECK: %[[const:.*]] = arith.constant 4 : i32 !CHECK: %[[LINEAR_EXPR:.*]] = arith.addi %[[LOAD_A]], %[[const]] : i32 + + !IMPLICIT: %[[LOAD_A:.*]] = fir.load %[[A]]#0 : !fir.ref + !IMPLICIT: %[[const:.*]] = arith.constant 4 : i32 + !IMPLICIT: %[[LINEAR_EXPR:.*]] = arith.addi %[[LOAD_A]], %[[const]] : i32 + !CHECK: omp.simd linear(%[[X]]#0 = %[[LINEAR_EXPR]] : !fir.ref) {{.*}} + + !IMPLICIT: omp.simd linear(%[[X]]#0 = %[[LINEAR_EXPR]] : !fir.ref, %[[I]]#0 = {{.*}} : !fir.ref) {{.*}} !$omp simd linear(x:a+4) do i = 1, 10 y = x + 2 end do !CHECK: } {linear_var_types = [i32]} + !IMPLICIT: } {linear_var_types = [i32, i32]} end subroutine diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp index 03d67a52853f6..710bdd2bc5425 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -155,12 +155,12 @@ class LinearClauseProcessor { // Allocate space for linear variabes void createLinearVar(llvm::IRBuilderBase &builder, LLVM::ModuleTranslation &moduleTranslation, - mlir::Value &linearVar, int idx) { + llvm::Value *linearVar, int idx) { linearPreconditionVars.push_back( builder.CreateAlloca(linearVarTypes[idx], nullptr, ".linear_var")); llvm::Value *linearLoopBodyTemp = builder.CreateAlloca(linearVarTypes[idx], nullptr, ".linear_result"); - linearOrigVal.push_back(moduleTranslation.lookupValue(linearVar)); + linearOrigVal.push_back(linearVar); linearLoopBodyTemps.push_back(linearLoopBodyTemp); } @@ -2623,8 +2623,9 @@ convertOmpWsloop(Operation &opInst, llvm::IRBuilderBase &builder, linearClauseProcessor.registerType(moduleTranslation, linearVarType); for (auto [idx, linearVar] : llvm::enumerate(wsloopOp.getLinearVars())) - linearClauseProcessor.createLinearVar(builder, moduleTranslation, - linearVar, idx); + linearClauseProcessor.createLinearVar( + builder, moduleTranslation, moduleTranslation.lookupValue(linearVar), + idx); for (mlir::Value linearStep : wsloopOp.getLinearStepVars()) linearClauseProcessor.initLinearStep(moduleTranslation, linearStep); } @@ -2942,6 +2943,11 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder, llvm::OpenMPIRBuilder::InsertPointTy allocaIP = findAllocaInsertPoint(builder, moduleTranslation); + llvm::Expected afterAllocas = allocatePrivateVars( + builder, moduleTranslation, privateVarsInfo, allocaIP); + if (handleError(afterAllocas, opInst).failed()) + return failure(); + // Initialize linear variables and linear step LinearClauseProcessor linearClauseProcessor; @@ -2949,18 +2955,29 @@ convertOmpSimd(Operation &opInst, llvm::IRBuilderBase &builder, auto linearVarTypes = simdOp.getLinearVarTypes().value(); for (mlir::Attribute linearVarType : linearVarTypes) linearClauseProcessor.registerType(moduleTranslation, linearVarType); - for (auto [idx, linearVar] : llvm::enumerate(simdOp.getLinearVars())) - linearClauseProcessor.createLinearVar(builder, moduleTranslation, - linearVar, idx); + for (auto [idx, linearVar] : llvm::enumerate(simdOp.getLinearVars())) { + bool isImplicit = false; + for (auto [mlirPrivVar, llvmPrivateVar] : llvm::zip_equal( + privateVarsInfo.mlirVars, privateVarsInfo.llvmVars)) { + // If the linear variable is implicit, reuse the already + // existing llvm::Value + if (linearVar == mlirPrivVar) { + isImplicit = true; + linearClauseProcessor.createLinearVar(builder, moduleTranslation, + llvmPrivateVar, idx); + break; + } + } + + if (!isImplicit) + linearClauseProcessor.createLinearVar( + builder, moduleTranslation, + moduleTranslation.lookupValue(linearVar), idx); + } for (mlir::Value linearStep : simdOp.getLinearStepVars()) linearClauseProcessor.initLinearStep(moduleTranslation, linearStep); } - llvm::Expected afterAllocas = allocatePrivateVars( - builder, moduleTranslation, privateVarsInfo, allocaIP); - if (handleError(afterAllocas, opInst).failed()) - return failure(); - if (failed(allocReductionVars(simdOp, reductionArgs, builder, moduleTranslation, allocaIP, reductionDecls, privateReductionVariables, reductionVariableMap, From 46632882b80f9b7157417448856339c1690480ad Mon Sep 17 00:00:00 2001 From: NimishMishra Date: Sun, 14 Dec 2025 11:53:29 +0530 Subject: [PATCH 2/2] Modify docs --- flang/docs/OpenMPSupport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flang/docs/OpenMPSupport.md b/flang/docs/OpenMPSupport.md index 6ef0f2a581771..8e58035b46769 100644 --- a/flang/docs/OpenMPSupport.md +++ b/flang/docs/OpenMPSupport.md @@ -34,7 +34,7 @@ Note : No distinction is made between the support in Parser/Semantics, MLIR, Low | Feature | Status | Comments | |------------------------------------------------------------|--------|---------------------------------------------------------| | proc_bind clause | Y | | -| simd construct | P | linear clause is not supported | +| simd construct | Y | | | declare simd construct | N | | | do simd construct | P | linear clause is not supported | | target data construct | P | device clause not supported |