[Mlir-commits] [mlir] [MLIR][OpenMP] Fix taskloop charbox privatization (PR #205568)

Tom Eccles llvmlistbot at llvm.org
Tue Jun 30 08:20:14 PDT 2026


https://github.com/tblah updated https://github.com/llvm/llvm-project/pull/205568

>From 7433df7cf99a950456c8e4710e0d703f33416ba6 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Wed, 24 Jun 2026 12:38:54 +0100
Subject: [PATCH 1/6] [MLIR][OpenMP] Fix taskloop charbox privatization

Load by-value privatizer arguments from task context storage
when lowering taskloop duplication and firstprivate copy
regions. This keeps pointer-based privatizers unchanged
while allowing descriptor values such as lowered Fortran
boxchar values to be passed by value.

It is not at all ideal that boxchars get special treatment.
MLIR to LLVM-IR should not depend upon any particular
frontend, but this is a long-standing TODO. The purpose
of this patch is to fix the reported bug.

Fixes: #205479

Assisted-by: Codex
---
 .../OpenMP/OpenMPToLLVMIRTranslation.cpp      | 57 ++++++++++++++-----
 .../LLVMIR/openmp-taskloop-charbox.mlir       | 54 ++++++++++++++++++
 2 files changed, 97 insertions(+), 14 deletions(-)
 create mode 100644 mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir

diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 0f954e384929a..4de38aa505fcc 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -1733,6 +1733,25 @@ findAssociatedValue(Value privateVar, llvm::IRBuilderBase &builder,
   return moduleTranslation.lookupValue(privateVar);
 }
 
+// Privatizer region arguments may be by-value even when the available LLVM
+// value is storage for that value, e.g. lowered Fortran boxchar descriptors in
+// task context structs. Materialize the value expected by the region argument
+// while preserving the existing pointer mapping for pointer arguments.
+static llvm::Value *
+materializeRegionArgValue(llvm::IRBuilderBase &builder,
+                          LLVM::ModuleTranslation &moduleTranslation,
+                          BlockArgument regionArg, llvm::Value *value) {
+  if (!regionArg)
+    return value;
+
+  llvm::Type *regionArgType =
+      moduleTranslation.convertType(regionArg.getType());
+  if (regionArgType->isPointerTy() || !value->getType()->isPointerTy())
+    return value;
+
+  return builder.CreateLoad(regionArgType, value);
+}
+
 /// Initialize a single (first)private variable. You probably want to use
 /// allocateAndInitPrivateVars instead of this.
 /// This returns the private variable which has been initialized. This
@@ -1924,10 +1943,15 @@ static LogicalResult copyFirstPrivateVars(
     // copyRegion implements `lhs = rhs`
     Region &copyRegion = decl.getCopyRegion();
 
-    moduleTranslation.mapValue(decl.getCopyMoldArg(), moldVar);
+    llvm::Value *copyMoldVar = materializeRegionArgValue(
+        builder, moduleTranslation, decl.getCopyMoldArg(), moldVar);
+    llvm::Value *copyPrivateVar = materializeRegionArgValue(
+        builder, moduleTranslation, decl.getCopyPrivateArg(), llvmVar);
+
+    moduleTranslation.mapValue(decl.getCopyMoldArg(), copyMoldVar);
 
     // map copyRegion lhs arg
-    moduleTranslation.mapValue(decl.getCopyPrivateArg(), llvmVar);
+    moduleTranslation.mapValue(decl.getCopyPrivateArg(), copyPrivateVar);
 
     // in-place convert copy region
     if (failed(inlineConvertOmpRegions(copyRegion, "omp.private.copy", builder,
@@ -3079,14 +3103,15 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
     // initialized character box is yielded by value. Here we need to store the
     // yielded value into the private allocation, and load the private
     // allocation to match the type expected by region block arguments.
+    llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
     if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&
         !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
       builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);
       // Load it so we have the value pointed to by the GEP
-      llvmPrivateVarAlloc = builder.CreateLoad(privateVarOrErr.get()->getType(),
-                                               llvmPrivateVarAlloc);
+      llvmPrivateVar = builder.CreateLoad(privateVarOrErr.get()->getType(),
+                                          llvmPrivateVarAlloc);
     }
-    assert(llvmPrivateVarAlloc->getType() ==
+    assert(llvmPrivateVar->getType() ==
            moduleTranslation.convertType(blockArg.getType()));
 
     // Mapping blockArg -> llvmPrivateVarAlloc is done inside the body callback
@@ -3561,14 +3586,15 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
     llvm::IRBuilderBase::InsertPointGuard guard(builder);
     builder.SetInsertPoint(builder.GetInsertBlock()->getTerminator());
 
+    llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
     if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&
         !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
       builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);
       // Load it so we have the value pointed to by the GEP
-      llvmPrivateVarAlloc = builder.CreateLoad(privateVarOrErr.get()->getType(),
-                                               llvmPrivateVarAlloc);
+      llvmPrivateVar = builder.CreateLoad(privateVarOrErr.get()->getType(),
+                                          llvmPrivateVarAlloc);
     }
-    assert(llvmPrivateVarAlloc->getType() ==
+    assert(llvmPrivateVar->getType() ==
            moduleTranslation.convertType(blockArg.getType()));
   }
 
@@ -3842,9 +3868,11 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
       assert(llvmPrivateVarAlloc &&
              "reads from mold so shouldn't have been skipped");
 
-      llvm::Expected<llvm::Value *> privateVarOrErr =
-          initPrivateVar(builder, moduleTranslation, privDecl, mold, blockArg,
-                         llvmPrivateVarAlloc, builder.GetInsertBlock());
+      llvm::Value *moldArg = materializeRegionArgValue(
+          builder, moduleTranslation, privDecl.getInitMoldArg(), mold);
+      llvm::Expected<llvm::Value *> privateVarOrErr = initPrivateVar(
+          builder, moduleTranslation, privDecl, moldArg, blockArg,
+          llvmPrivateVarAlloc, builder.GetInsertBlock());
       if (!privateVarOrErr)
         return privateVarOrErr.takeError();
 
@@ -3855,14 +3883,15 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
       // initialized character box is yielded by value. Here we need to store
       // the yielded value into the private allocation, and load the private
       // allocation to match the type expected by region block arguments.
+      llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
       if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&
           !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
         builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);
         // Load it so we have the value pointed to by the GEP
-        llvmPrivateVarAlloc = builder.CreateLoad(
-            privateVarOrErr.get()->getType(), llvmPrivateVarAlloc);
+        llvmPrivateVar = builder.CreateLoad(privateVarOrErr.get()->getType(),
+                                            llvmPrivateVarAlloc);
       }
-      assert(llvmPrivateVarAlloc->getType() ==
+      assert(llvmPrivateVar->getType() ==
              moduleTranslation.convertType(blockArg.getType()));
 
       // Mapping blockArg -> llvmPrivateVarAlloc is done inside the body
diff --git a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
new file mode 100644
index 0000000000000..a20cc0149a453
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
@@ -0,0 +1,54 @@
+// RUN: mlir-translate --mlir-to-llvmir %s | FileCheck %s
+
+module {
+  llvm.func @touch(!llvm.ptr, i64)
+
+  omp.private {type = firstprivate} @box_firstprivate : !llvm.struct<(ptr, i64)> init {
+  ^bb0(%arg0: !llvm.struct<(ptr, i64)>, %arg1: !llvm.struct<(ptr, i64)>):
+    %0 = llvm.extractvalue %arg0[0] : !llvm.struct<(ptr, i64)>
+    %1 = llvm.extractvalue %arg0[1] : !llvm.struct<(ptr, i64)>
+    %2 = llvm.mlir.undef : !llvm.struct<(ptr, i64)>
+    %3 = llvm.insertvalue %0, %2[0] : !llvm.struct<(ptr, i64)>
+    %4 = llvm.insertvalue %1, %3[1] : !llvm.struct<(ptr, i64)>
+    omp.yield(%4 : !llvm.struct<(ptr, i64)>)
+  } copy {
+  ^bb0(%arg0: !llvm.struct<(ptr, i64)>, %arg1: !llvm.struct<(ptr, i64)>):
+    %0 = llvm.extractvalue %arg0[0] : !llvm.struct<(ptr, i64)>
+    %1 = llvm.extractvalue %arg0[1] : !llvm.struct<(ptr, i64)>
+    llvm.call @touch(%0, %1) : (!llvm.ptr, i64) -> ()
+    omp.yield(%arg0 : !llvm.struct<(ptr, i64)>)
+  } dealloc {
+  ^bb0(%arg0: !llvm.struct<(ptr, i64)>):
+    %0 = llvm.extractvalue %arg0[0] : !llvm.struct<(ptr, i64)>
+    %1 = llvm.extractvalue %arg0[1] : !llvm.struct<(ptr, i64)>
+    llvm.call @touch(%0, %1) : (!llvm.ptr, i64) -> ()
+    omp.yield
+  }
+
+  llvm.func @test(%arg0: !llvm.ptr, %arg1: i64) {
+    %0 = llvm.mlir.undef : !llvm.struct<(ptr, i64)>
+    %1 = llvm.insertvalue %arg0, %0[0] : !llvm.struct<(ptr, i64)>
+    %2 = llvm.insertvalue %arg1, %1[1] : !llvm.struct<(ptr, i64)>
+    %c1 = llvm.mlir.constant(1 : i32) : i32
+    %c2 = llvm.mlir.constant(2 : i32) : i32
+    omp.taskloop.context private(@box_firstprivate %2 -> %arg2 : !llvm.struct<(ptr, i64)>) {
+      omp.taskloop.wrapper {
+        omp.loop_nest (%arg3) : i32 = (%c1) to (%c2) inclusive step (%c1) {
+          %3 = llvm.extractvalue %arg2[0] : !llvm.struct<(ptr, i64)>
+          %4 = llvm.extractvalue %arg2[1] : !llvm.struct<(ptr, i64)>
+          llvm.call @touch(%3, %4) : (!llvm.ptr, i64) -> ()
+          omp.yield
+        }
+      }
+      omp.terminator
+    }
+    llvm.return
+  }
+}
+
+// CHECK-LABEL: define void @test(
+// CHECK:         call void @__kmpc_taskloop(
+
+// CHECK-LABEL: define internal void @omp_taskloop_dup(
+// CHECK:         load { ptr, i64 }, ptr
+// CHECK:         call void @touch(

>From 626ccf20e8b287a3c4b2954e32d0e8d4981a5632 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Thu, 25 Jun 2026 15:12:07 +0100
Subject: [PATCH 2/6] Avoid NDEBUG task privatizer warning

---
 .../LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp     | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index 4de38aa505fcc..054c04eb745d1 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -3103,7 +3103,7 @@ convertOmpTaskOp(omp::TaskOp taskOp, llvm::IRBuilderBase &builder,
     // initialized character box is yielded by value. Here we need to store the
     // yielded value into the private allocation, and load the private
     // allocation to match the type expected by region block arguments.
-    llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
+    [[maybe_unused]] llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
     if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&
         !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
       builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);
@@ -3586,7 +3586,7 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
     llvm::IRBuilderBase::InsertPointGuard guard(builder);
     builder.SetInsertPoint(builder.GetInsertBlock()->getTerminator());
 
-    llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
+    [[maybe_unused]] llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
     if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&
         !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
       builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);
@@ -3883,7 +3883,7 @@ convertOmpTaskloopContextOp(omp::TaskloopContextOp contextOp,
       // initialized character box is yielded by value. Here we need to store
       // the yielded value into the private allocation, and load the private
       // allocation to match the type expected by region block arguments.
-      llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
+      [[maybe_unused]] llvm::Value *llvmPrivateVar = llvmPrivateVarAlloc;
       if ((privateVarOrErr.get() != llvmPrivateVarAlloc) &&
           !mlir::isa<LLVM::LLVMPointerType>(blockArg.getType())) {
         builder.CreateStore(privateVarOrErr.get(), llvmPrivateVarAlloc);

>From 85aee0132adb2f758a2998421714fe8d1c9d9cec Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Thu, 25 Jun 2026 15:22:02 +0100
Subject: [PATCH 3/6] Tighten taskloop charbox test

---
 mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
index a20cc0149a453..dff7cee1811fd 100644
--- a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
@@ -50,5 +50,6 @@ module {
 // CHECK:         call void @__kmpc_taskloop(
 
 // CHECK-LABEL: define internal void @omp_taskloop_dup(
-// CHECK:         load { ptr, i64 }, ptr
+// CHECK:         %[[MOLD:.*]] = load { ptr, i64 }, ptr
+// CHECK:         extractvalue { ptr, i64 } %[[MOLD]], 0
 // CHECK:         call void @touch(

>From 0362db31507fc67a8730ff4c22db01272bbbe904 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Mon, 29 Jun 2026 16:06:58 +0100
Subject: [PATCH 4/6] Fix test

---
 mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
index dff7cee1811fd..243f81bcc4ace 100644
--- a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
@@ -38,7 +38,7 @@ module {
           %4 = llvm.extractvalue %arg2[1] : !llvm.struct<(ptr, i64)>
           llvm.call @touch(%3, %4) : (!llvm.ptr, i64) -> ()
           omp.yield
-        }
+        } {omp.combined}
       }
       omp.terminator
     }

>From 50490df84b6c23d16722ef6908c6c356ef2166bf Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Mon, 29 Jun 2026 17:30:42 +0100
Subject: [PATCH 5/6] Put new tag in the right place

---
 mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
index 243f81bcc4ace..d0a570e6f6804 100644
--- a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
@@ -38,10 +38,10 @@ module {
           %4 = llvm.extractvalue %arg2[1] : !llvm.struct<(ptr, i64)>
           llvm.call @touch(%3, %4) : (!llvm.ptr, i64) -> ()
           omp.yield
-        } {omp.combined}
+        }
       }
       omp.terminator
-    }
+    } {omp.combined}
     llvm.return
   }
 }

>From 9ee5603d54b1371c0db241635e59ee2e7cea220b Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Tue, 30 Jun 2026 16:15:45 +0100
Subject: [PATCH 6/6] Expand test

---
 .../LLVMIR/openmp-taskloop-charbox.mlir       | 178 +++++++++++++++++-
 1 file changed, 170 insertions(+), 8 deletions(-)

diff --git a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
index d0a570e6f6804..9c051092ebcee 100644
--- a/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
+++ b/mlir/test/Target/LLVMIR/openmp-taskloop-charbox.mlir
@@ -1,7 +1,9 @@
 // RUN: mlir-translate --mlir-to-llvmir %s | FileCheck %s
 
 module {
-  llvm.func @touch(!llvm.ptr, i64)
+  llvm.func @use_body_box(!llvm.ptr, i64)
+  llvm.func @copy_box(!llvm.ptr, i64)
+  llvm.func @dealloc_box(!llvm.ptr, i64)
 
   omp.private {type = firstprivate} @box_firstprivate : !llvm.struct<(ptr, i64)> init {
   ^bb0(%arg0: !llvm.struct<(ptr, i64)>, %arg1: !llvm.struct<(ptr, i64)>):
@@ -15,13 +17,13 @@ module {
   ^bb0(%arg0: !llvm.struct<(ptr, i64)>, %arg1: !llvm.struct<(ptr, i64)>):
     %0 = llvm.extractvalue %arg0[0] : !llvm.struct<(ptr, i64)>
     %1 = llvm.extractvalue %arg0[1] : !llvm.struct<(ptr, i64)>
-    llvm.call @touch(%0, %1) : (!llvm.ptr, i64) -> ()
+    llvm.call @copy_box(%0, %1) : (!llvm.ptr, i64) -> ()
     omp.yield(%arg0 : !llvm.struct<(ptr, i64)>)
   } dealloc {
   ^bb0(%arg0: !llvm.struct<(ptr, i64)>):
     %0 = llvm.extractvalue %arg0[0] : !llvm.struct<(ptr, i64)>
     %1 = llvm.extractvalue %arg0[1] : !llvm.struct<(ptr, i64)>
-    llvm.call @touch(%0, %1) : (!llvm.ptr, i64) -> ()
+    llvm.call @dealloc_box(%0, %1) : (!llvm.ptr, i64) -> ()
     omp.yield
   }
 
@@ -36,7 +38,7 @@ module {
         omp.loop_nest (%arg3) : i32 = (%c1) to (%c2) inclusive step (%c1) {
           %3 = llvm.extractvalue %arg2[0] : !llvm.struct<(ptr, i64)>
           %4 = llvm.extractvalue %arg2[1] : !llvm.struct<(ptr, i64)>
-          llvm.call @touch(%3, %4) : (!llvm.ptr, i64) -> ()
+          llvm.call @use_body_box(%3, %4) : (!llvm.ptr, i64) -> ()
           omp.yield
         }
       }
@@ -47,9 +49,169 @@ module {
 }
 
 // CHECK-LABEL: define void @test(
-// CHECK:         call void @__kmpc_taskloop(
+// CHECK-SAME:     ptr %[[ARG0:.*]], i64 %[[ARG1:.*]]) {
+// CHECK-NEXT:   %[[STRUCT_ARG:.*]] = alloca { i64, i64, i64, ptr }, align 8
+// CHECK-NEXT:   %[[BOX_WITH_PTR:.*]] = insertvalue { ptr, i64 } undef, ptr %[[ARG0]], 0
+// CHECK-NEXT:   %[[ORIG_BOX:.*]] = insertvalue { ptr, i64 } %[[BOX_WITH_PTR]], i64 %[[ARG1]], 1
+// CHECK-NEXT:   br label %entry
+// CHECK-EMPTY:
+// CHECK-NEXT: entry:{{.*}}; preds = %[[ENTRY_PRED:.*]]
+// CHECK-NEXT:   br label %omp.private.init
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.private.init:{{.*}}; preds = %entry
+// CHECK-NEXT:   %[[TASK_CONTEXT:.*]] = tail call ptr @malloc(i64 ptrtoint (ptr getelementptr ({ { ptr, i64 } }, ptr null, i32 1) to i64))
+// CHECK-NEXT:   %[[TASK_CONTEXT_BOX:.*]] = getelementptr { { ptr, i64 } }, ptr %[[TASK_CONTEXT]], i32 0, i32 0
+// CHECK-NEXT:   %[[INIT_PTR:.*]] = extractvalue { ptr, i64 } %[[ORIG_BOX]], 0
+// CHECK-NEXT:   %[[INIT_LEN:.*]] = extractvalue { ptr, i64 } %[[ORIG_BOX]], 1
+// CHECK-NEXT:   %[[INIT_BOX_WITH_PTR:.*]] = insertvalue { ptr, i64 } undef, ptr %[[INIT_PTR]], 0
+// CHECK-NEXT:   %[[INIT_BOX:.*]] = insertvalue { ptr, i64 } %[[INIT_BOX_WITH_PTR]], i64 %[[INIT_LEN]], 1
+// CHECK-NEXT:   store { ptr, i64 } %[[INIT_BOX]], ptr %[[TASK_CONTEXT_BOX]], align 8
+// CHECK-NEXT:   %[[INIT_LOAD:.*]] = load { ptr, i64 }, ptr %[[TASK_CONTEXT_BOX]], align 8
+// CHECK-NEXT:   br label %omp.private.copy
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.private.copy:{{.*}}; preds = %omp.private.init
+// CHECK-NEXT:   br label %omp.private.copy1
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.private.copy1:{{.*}}; preds = %omp.private.copy
+// CHECK-NEXT:   %[[COPY_LOAD:.*]] = load { ptr, i64 }, ptr %[[TASK_CONTEXT_BOX]], align 8
+// CHECK-NEXT:   %[[COPY_PTR:.*]] = extractvalue { ptr, i64 } %[[ORIG_BOX]], 0
+// CHECK-NEXT:   %[[COPY_LEN:.*]] = extractvalue { ptr, i64 } %[[ORIG_BOX]], 1
+// CHECK-NEXT:   call void @copy_box(ptr %[[COPY_PTR]], i64 %[[COPY_LEN]])
+// CHECK-NEXT:   br label %omp.taskloop.wrapper.start
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.taskloop.wrapper.start:{{.*}}; preds = %omp.private.copy1
+// CHECK-NEXT:   br label %codeRepl
+// CHECK-EMPTY:
+// CHECK-NEXT: codeRepl:{{.*}}; preds = %omp.taskloop.wrapper.start
+// CHECK-NEXT:   %[[LB_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[STRUCT_ARG]], i32 0, i32 0
+// CHECK-NEXT:   store i64 1, ptr %[[LB_ADDR]], align 4
+// CHECK-NEXT:   %[[UB_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[STRUCT_ARG]], i32 0, i32 1
+// CHECK-NEXT:   store i64 2, ptr %[[UB_ADDR]], align 4
+// CHECK-NEXT:   %[[STEP_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[STRUCT_ARG]], i32 0, i32 2
+// CHECK-NEXT:   store i64 1, ptr %[[STEP_ADDR]], align 4
+// CHECK-NEXT:   %[[TASK_CONTEXT_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[STRUCT_ARG]], i32 0, i32 3
+// CHECK-NEXT:   store ptr %[[TASK_CONTEXT]], ptr %[[TASK_CONTEXT_ADDR]], align 8
+// CHECK-NEXT:   %[[THREAD_NUM:.*]] = call i32 @__kmpc_global_thread_num(ptr @[[IDENT:[0-9]+]])
+// CHECK-NEXT:   call void @__kmpc_taskgroup(ptr @[[IDENT]], i32 %[[THREAD_NUM]])
+// CHECK-NEXT:   %[[TASK:.*]] = call ptr @__kmpc_omp_task_alloc(ptr @[[IDENT]], i32 %[[THREAD_NUM]], i32 1, i64 40, i64 32, ptr @test..omp_par)
+// CHECK-NEXT:   %[[TASK_DATA:.*]] = load ptr, ptr %[[TASK]], align 8
+// CHECK-NEXT:   call void @llvm.memcpy.p0.p0.i64(ptr align 1 %[[TASK_DATA]], ptr align 1 %[[STRUCT_ARG]], i64 32, i1 false)
+// CHECK-NEXT:   %[[TASK_LB_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[TASK_DATA]], i32 0, i32 0
+// CHECK-NEXT:   %[[TASK_UB_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[TASK_DATA]], i32 0, i32 1
+// CHECK-NEXT:   %[[TASK_STEP_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[TASK_DATA]], i32 0, i32 2
+// CHECK-NEXT:   %[[TASK_STEP:.*]] = load i64, ptr %[[TASK_STEP_ADDR]], align 4
+// CHECK-NEXT:   call void @__kmpc_taskloop(ptr @[[IDENT]], i32 %[[THREAD_NUM]], ptr %[[TASK]], i32 1, ptr %[[TASK_LB_ADDR]], ptr %[[TASK_UB_ADDR]], i64 %[[TASK_STEP]], i32 1, i32 0, i64 0, ptr @omp_taskloop_dup)
+// CHECK-NEXT:   call void @__kmpc_end_taskgroup(ptr @[[IDENT]], i32 %[[THREAD_NUM]])
+// CHECK-NEXT:   br label %taskloop.exit
+// CHECK-EMPTY:
+// CHECK-NEXT: taskloop.exit:{{.*}}; preds = %codeRepl
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
+// CHECK-EMPTY:
+// CHECK-NEXT: define internal void @test..omp_par(i32 %[[GLOBAL_TID:.*]], ptr %[[TASK_ARG:.*]]) {
+// CHECK-NEXT: taskloop.alloca:
+// CHECK-NEXT:   %[[TASK_ARGS:.*]] = load ptr, ptr %[[TASK_ARG]], align 8
+// CHECK-NEXT:   %[[PAR_LB_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[TASK_ARGS]], i32 0, i32 0
+// CHECK-NEXT:   %[[PAR_LB:.*]] = load i64, ptr %[[PAR_LB_ADDR]], align 4
+// CHECK-NEXT:   %[[PAR_UB_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[TASK_ARGS]], i32 0, i32 1
+// CHECK-NEXT:   %[[PAR_UB:.*]] = load i64, ptr %[[PAR_UB_ADDR]], align 4
+// CHECK-NEXT:   %[[PAR_STEP_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[TASK_ARGS]], i32 0, i32 2
+// CHECK-NEXT:   %[[PAR_STEP:.*]] = load i64, ptr %[[PAR_STEP_ADDR]], align 4
+// CHECK-NEXT:   %[[PAR_CONTEXT_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[TASK_ARGS]], i32 0, i32 3
+// CHECK-NEXT:   %[[PAR_CONTEXT:.*]] = load ptr, ptr %[[PAR_CONTEXT_ADDR]], align 8, !align ![[ALIGN:[0-9]+]]
+// CHECK-NEXT:   br label %taskloop.body
+// CHECK-EMPTY:
+// CHECK-NEXT: taskloop.body:{{.*}}; preds = %taskloop.alloca
+// CHECK-NEXT:   %[[PAR_BOX_ADDR:.*]] = getelementptr { { ptr, i64 } }, ptr %[[PAR_CONTEXT]], i32 0, i32 0
+// CHECK-NEXT:   %[[PAR_BOX:.*]] = load { ptr, i64 }, ptr %[[PAR_BOX_ADDR]], align 8
+// CHECK-NEXT:   br label %omp.taskloop.context.region
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.taskloop.context.region:{{.*}}; preds = %taskloop.body
+// CHECK-NEXT:   br label %omp.taskloop.wrapper.region
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.taskloop.wrapper.region:{{.*}}; preds = %omp.taskloop.context.region
+// CHECK-NEXT:   br label %omp_loop.preheader
+// CHECK-EMPTY:
+// CHECK-NEXT: omp_loop.preheader:{{.*}}; preds = %omp.taskloop.wrapper.region
+// CHECK-NEXT:   %[[RANGE:.*]] = sub i64 %[[PAR_UB]], %[[PAR_LB]]
+// CHECK-NEXT:   %[[STEP_COUNT:.*]] = sdiv i64 %[[RANGE]], %[[PAR_STEP]]
+// CHECK-NEXT:   %[[TRIP_COUNT:.*]] = add i64 %[[STEP_COUNT]], 1
+// CHECK-NEXT:   %[[TRIP_COUNT_I32:.*]] = trunc i64 %[[TRIP_COUNT]] to i32
+// CHECK-NEXT:   %[[LB_I32:.*]] = trunc i64 %[[PAR_LB]] to i32
+// CHECK-NEXT:   br label %omp_loop.header
+// CHECK-EMPTY:
+// CHECK-NEXT: omp_loop.header:{{.*}}; preds = %omp_loop.inc, %omp_loop.preheader
+// CHECK-NEXT:   %[[LOOP_IV:.*]] = phi i32 [ 0, %omp_loop.preheader ], [ %[[LOOP_NEXT:.*]], %omp_loop.inc ]
+// CHECK-NEXT:   br label %omp_loop.cond
+// CHECK-EMPTY:
+// CHECK-NEXT: omp_loop.cond:{{.*}}; preds = %omp_loop.header
+// CHECK-NEXT:   %[[LOOP_CMP:.*]] = icmp ult i32 %[[LOOP_IV]], %[[TRIP_COUNT_I32]]
+// CHECK-NEXT:   br i1 %[[LOOP_CMP]], label %omp_loop.body, label %omp_loop.exit
+// CHECK-EMPTY:
+// CHECK-NEXT: omp_loop.exit:{{.*}}; preds = %omp_loop.cond
+// CHECK-NEXT:   br label %omp_loop.after
+// CHECK-EMPTY:
+// CHECK-NEXT: omp_loop.after:{{.*}}; preds = %omp_loop.exit
+// CHECK-NEXT:   br label %omp.region.cont2
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.region.cont2:{{.*}}; preds = %omp_loop.after
+// CHECK-NEXT:   br label %omp.region.cont
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.region.cont:{{.*}}; preds = %omp.region.cont2
+// CHECK-NEXT:   %[[DEALLOC_PTR:.*]] = extractvalue { ptr, i64 } %[[PAR_BOX]], 0
+// CHECK-NEXT:   %[[DEALLOC_LEN:.*]] = extractvalue { ptr, i64 } %[[PAR_BOX]], 1
+// CHECK-NEXT:   call void @dealloc_box(ptr %[[DEALLOC_PTR]], i64 %[[DEALLOC_LEN]])
+// CHECK-NEXT:   tail call void @free(ptr %[[PAR_CONTEXT]])
+// CHECK-NEXT:   br label %taskloop.exit.exitStub
+// CHECK-EMPTY:
+// CHECK-NEXT: omp_loop.body:{{.*}}; preds = %omp_loop.cond
+// CHECK-NEXT:   %[[LOOP_OFFSET:.*]] = mul i32 %[[LOOP_IV]], 1
+// CHECK-NEXT:   %[[LOGICAL_IV:.*]] = add i32 %[[LOOP_OFFSET]], %[[LB_I32]]
+// CHECK-NEXT:   br label %omp.loop_nest.region
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.loop_nest.region:{{.*}}; preds = %omp_loop.body
+// CHECK-NEXT:   %[[BODY_PTR:.*]] = extractvalue { ptr, i64 } %[[PAR_BOX]], 0
+// CHECK-NEXT:   %[[BODY_LEN:.*]] = extractvalue { ptr, i64 } %[[PAR_BOX]], 1
+// CHECK-NEXT:   call void @use_body_box(ptr %[[BODY_PTR]], i64 %[[BODY_LEN]])
+// CHECK-NEXT:   br label %omp.region.cont3
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.region.cont3:{{.*}}; preds = %omp.loop_nest.region
+// CHECK-NEXT:   br label %omp_loop.inc
+// CHECK-EMPTY:
+// CHECK-NEXT: omp_loop.inc:{{.*}}; preds = %omp.region.cont3
+// CHECK-NEXT:   %[[LOOP_NEXT]] = add nuw i32 %[[LOOP_IV]], 1
+// CHECK-NEXT:   br label %omp_loop.header
+// CHECK-EMPTY:
+// CHECK-NEXT: taskloop.exit.exitStub:{{.*}}; preds = %omp.region.cont
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }
 
 // CHECK-LABEL: define internal void @omp_taskloop_dup(
-// CHECK:         %[[MOLD:.*]] = load { ptr, i64 }, ptr
-// CHECK:         extractvalue { ptr, i64 } %[[MOLD]], 0
-// CHECK:         call void @touch(
+// CHECK-SAME:     ptr %[[DEST_TASK:.*]], ptr %[[SRC_TASK:.*]], i32 %[[LASTPRIVATE_FLAG:.*]]) {
+// CHECK-NEXT: entry:
+// CHECK-NEXT:   %[[DEST_TASK_DATA:.*]] = getelementptr { %struct.kmp_task_ompbuilder_t, { i64, i64, i64, ptr } }, ptr %[[DEST_TASK]], i32 0, i32 1
+// CHECK-NEXT:   %[[DEST_CONTEXT_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[DEST_TASK_DATA]], i32 0, i32 3
+// CHECK-NEXT:   %[[SRC_TASK_DATA:.*]] = getelementptr { %struct.kmp_task_ompbuilder_t, { i64, i64, i64, ptr } }, ptr %[[SRC_TASK]], i32 0, i32 1
+// CHECK-NEXT:   %[[SRC_CONTEXT_ADDR:.*]] = getelementptr { i64, i64, i64, ptr }, ptr %[[SRC_TASK_DATA]], i32 0, i32 3
+// CHECK-NEXT:   %[[SRC_CONTEXT:.*]] = load ptr, ptr %[[SRC_CONTEXT_ADDR]], align 8
+// CHECK-NEXT:   %[[DEST_CONTEXT:.*]] = tail call ptr @malloc(i64 ptrtoint (ptr getelementptr ({ { ptr, i64 } }, ptr null, i32 1) to i64))
+// CHECK-NEXT:   store ptr %[[DEST_CONTEXT]], ptr %[[DEST_CONTEXT_ADDR]], align 8
+// CHECK-NEXT:   %[[SRC_BOX_ADDR:.*]] = getelementptr { { ptr, i64 } }, ptr %[[SRC_CONTEXT]], i32 0, i32 0
+// CHECK-NEXT:   %[[DEST_BOX_ADDR:.*]] = getelementptr { { ptr, i64 } }, ptr %[[DEST_CONTEXT]], i32 0, i32 0
+// CHECK-NEXT:   %[[DUP_MOLD:.*]] = load { ptr, i64 }, ptr %[[SRC_BOX_ADDR]], align 8
+// CHECK-NEXT:   %[[DUP_PTR:.*]] = extractvalue { ptr, i64 } %[[DUP_MOLD]], 0
+// CHECK-NEXT:   %[[DUP_LEN:.*]] = extractvalue { ptr, i64 } %[[DUP_MOLD]], 1
+// CHECK-NEXT:   %[[DUP_BOX_WITH_PTR:.*]] = insertvalue { ptr, i64 } undef, ptr %[[DUP_PTR]], 0
+// CHECK-NEXT:   %[[DUP_BOX:.*]] = insertvalue { ptr, i64 } %[[DUP_BOX_WITH_PTR]], i64 %[[DUP_LEN]], 1
+// CHECK-NEXT:   store { ptr, i64 } %[[DUP_BOX]], ptr %[[DEST_BOX_ADDR]], align 8
+// CHECK-NEXT:   %[[DUP_PRIVATE:.*]] = load { ptr, i64 }, ptr %[[DEST_BOX_ADDR]], align 8
+// CHECK-NEXT:   br label %omp.private.copy
+// CHECK-EMPTY:
+// CHECK-NEXT: omp.private.copy:{{.*}}; preds = %entry
+// CHECK-NEXT:   %[[COPY_MOLD:.*]] = load { ptr, i64 }, ptr %[[SRC_BOX_ADDR]], align 8
+// CHECK-NEXT:   %[[COPY_PRIVATE:.*]] = load { ptr, i64 }, ptr %[[DEST_BOX_ADDR]], align 8
+// CHECK-NEXT:   %[[COPY_MOLD_PTR:.*]] = extractvalue { ptr, i64 } %[[COPY_MOLD]], 0
+// CHECK-NEXT:   %[[COPY_MOLD_LEN:.*]] = extractvalue { ptr, i64 } %[[COPY_MOLD]], 1
+// CHECK-NEXT:   call void @copy_box(ptr %[[COPY_MOLD_PTR]], i64 %[[COPY_MOLD_LEN]])
+// CHECK-NEXT:   ret void
+// CHECK-NEXT: }



More information about the Mlir-commits mailing list