[OpenMPOpt] Validate declaration types against the expected types
authorJohannes Doerfert <johannes@jdoerfert.de>
Mon, 23 Mar 2020 15:47:06 +0000 (10:47 -0500)
committerJohannes Doerfert <johannes@jdoerfert.de>
Mon, 23 Mar 2020 16:43:36 +0000 (11:43 -0500)
Validation of the found runtime library functions declarations types
(return and argument types) with the expected types.

Reviewed By: jdoerfert

Differential Revision: https://reviews.llvm.org/D76058

llvm/lib/Transforms/IPO/OpenMPOpt.cpp
llvm/test/Transforms/OpenMP/rtf_type_checking.ll [new file with mode: 0644]

index a766513..ca52a61 100644 (file)
@@ -119,7 +119,7 @@ struct OpenMPOpt {
   }
 
 private:
-  /// Try to delete parallel regions if possible
+  /// Try to delete parallel regions if possible.
   bool deleteParallelRegions() {
     const unsigned CallbackCalleeOperand = 2;
 
@@ -385,6 +385,32 @@ private:
     return nullptr;
   }
 
+  /// Returns true if the function declaration \p F matches the runtime
+  /// function types, that is, return type \p RTFRetType, and argument types
+  /// \p RTFArgTypes.
+  static bool declMatchesRTFTypes(Function *F, Type *RTFRetType,
+                                  SmallVector<Type *, 8> &RTFArgTypes) {
+    // TODO: We should output information to the user (under debug output
+    //       and via remarks).
+
+    if (!F)
+      return false;
+    if (F->getReturnType() != RTFRetType)
+      return false;
+    if (F->arg_size() != RTFArgTypes.size())
+      return false;
+
+    auto RTFTyIt = RTFArgTypes.begin();
+    for (Argument &Arg : F->args()) {
+      if (Arg.getType() != *RTFTyIt)
+        return false;
+
+      ++RTFTyIt;
+    }
+
+    return true;
+  }
+
   /// Helper to initialize all runtime function information for those defined in
   /// OpenMPKinds.def.
   void initializeRuntimeFunctions() {
@@ -415,26 +441,29 @@ private:
 
 #define OMP_RTL(_Enum, _Name, _IsVarArg, _ReturnType, ...)                     \
   {                                                                            \
-    auto &RFI = RFIs[_Enum];                                                   \
-    RFI.Kind = _Enum;                                                          \
-    RFI.Name = _Name;                                                          \
-    RFI.IsVarArg = _IsVarArg;                                                  \
-    RFI.ReturnType = _ReturnType;                                              \
-    RFI.ArgumentTypes = SmallVector<Type *, 8>({__VA_ARGS__});                 \
-    RFI.Declaration = M.getFunction(_Name);                                    \
-    unsigned NumUses = CollectUses(RFI);                                       \
-    (void)NumUses;                                                             \
-    LLVM_DEBUG({                                                               \
-      dbgs() << TAG << RFI.Name << (RFI.Declaration ? "" : " not")             \
-             << " found\n";                                                    \
-      if (RFI.Declaration)                                                     \
-        dbgs() << TAG << "-> got " << NumUses << " uses in "                   \
-               << RFI.UsesMap.size() << " different functions.\n";             \
-    });                                                                        \
+    SmallVector<Type *, 8> ArgsTypes({__VA_ARGS__});                           \
+    Function *F = M.getFunction(_Name);                                        \
+    if (declMatchesRTFTypes(F, _ReturnType , ArgsTypes)) {                     \
+      auto &RFI = RFIs[_Enum];                                                 \
+      RFI.Kind = _Enum;                                                        \
+      RFI.Name = _Name;                                                        \
+      RFI.IsVarArg = _IsVarArg;                                                \
+      RFI.ReturnType = _ReturnType;                                            \
+      RFI.ArgumentTypes = std::move(ArgsTypes);                                \
+      RFI.Declaration = F;                                                     \
+      unsigned NumUses = CollectUses(RFI);                                     \
+      (void)NumUses;                                                           \
+      LLVM_DEBUG({                                                             \
+        dbgs() << TAG << RFI.Name << (RFI.Declaration ? "" : " not")           \
+              << " found\n";                                                   \
+        if (RFI.Declaration)                                                   \
+          dbgs() << TAG << "-> got " << NumUses << " uses in "                 \
+                << RFI.UsesMap.size() << " different functions.\n";            \
+      });                                                                      \
+    }                                                                          \
   }
 #include "llvm/Frontend/OpenMP/OMPKinds.def"
 
-    // TODO: We should validate the declaration agains the types we expect.
     // TODO: We should attach the attributes defined in OMPKinds.def.
   }
 
diff --git a/llvm/test/Transforms/OpenMP/rtf_type_checking.ll b/llvm/test/Transforms/OpenMP/rtf_type_checking.ll
new file mode 100644 (file)
index 0000000..57c09bc
--- /dev/null
@@ -0,0 +1,63 @@
+; RUN: opt -S -openmpopt -stats < %s 2>&1 | FileCheck %s
+; REQUIRES: asserts
+
+target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
+
+%struct.ident_t = type { i32, i32, i32, i32, i8* }
+
+@.str = private unnamed_addr constant [23 x i8] c";unknown;unknown;0;0;;\00", align 1
+@0 = private unnamed_addr global %struct.ident_t { i32 0, i32 2, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8
+@1 = private unnamed_addr global %struct.ident_t { i32 0, i32 322, i32 0, i32 0, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @.str, i32 0, i32 0) }, align 8
+
+define i32 @main() {
+entry:
+
+  call void (%struct.ident_t*, void (i32*, i32*, ...)*, ...) @__kmpc_fork_call(%struct.ident_t* nonnull @0, void (i32*, i32*, ...)* bitcast (void (i32*, i32*)* @.omp_outlined. to void (i32*, i32*, ...)*))
+  ret i32 0
+}
+
+; Only the last runtime call will be matched due that the rest of the "runtime function" calls
+; have some type mismatch compared to the real runtime function. See the check at bottom.
+define internal void @.omp_outlined.(i32* noalias %.global_tid., i32* noalias %.bound_tid.) {
+entry:
+
+  call void @__kmpc_master(%struct.ident_t* nonnull @0)
+  call void @__kmpc_end_master(%struct.ident_t* nonnull @0, i32 0, i32 0)
+  call void @__kmpc_barrier(%struct.ident_t* nonnull @1, float 0.0)
+  call void @omp_get_thread_num()
+  call void @__kmpc_flush(%struct.ident_t* nonnull @0)
+  ret void
+}
+; Fewer arguments than expected in variadic function.
+declare !callback !2 void @__kmpc_fork_call(%struct.ident_t*, void (i32*, i32*, ...)*, ...)
+
+; Fewer number of arguments in non variadic function.
+declare void @__kmpc_master(%struct.ident_t*)
+
+; Bigger number of arguments in non variadic function.
+declare void @__kmpc_end_master(%struct.ident_t*, i32, i32)
+
+; Different argument type than the expected.
+declare void @__kmpc_barrier(%struct.ident_t*, float)
+
+; Proper use of runtime function.
+declare void @__kmpc_flush(%struct.ident_t*)
+
+; Different return type.
+declare void @omp_get_thread_num()
+
+!llvm.module.flags = !{!0}
+!llvm.ident = !{!1}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{!"clang"}
+!2 = !{!3}
+!3 = !{i64 2, i64 -1, i64 -1, i1 true}
+; ===-------------------------------------------------------------------------===
+;                         ... Statistics Collected ...
+; ===-------------------------------------------------------------------------===
+;
+; CHECK: 1 cgscc-passmgr - Maximum CGSCCPassMgr iterations on one SCC
+; CHECK: 2 openmp-opt{{.*}}Number of OpenMP runtime functions identified
+;
+; There are two matches since the pass is run once per function.
\ No newline at end of file