Index: llvm/lib/Transforms/IPO/OpenMPOpt.cpp =================================================================== --- llvm/lib/Transforms/IPO/OpenMPOpt.cpp +++ llvm/lib/Transforms/IPO/OpenMPOpt.cpp @@ -119,7 +119,7 @@ } private: - /// Try to delete parallel regions if possible + /// Try to delete parallel regions if possible. bool deleteParallelRegions() { const unsigned CallbackCalleeOperand = 2; @@ -385,6 +385,36 @@ return nullptr; } + /// Returns true if the function declaration \p F matches the runtime + /// function types, that is, return type \p RTFRetType, and arguments types + /// \p RTFArgTypes. If the runtime function is variadic, \p IsVarArg, the + /// comparison is made until the runtime function number of arguments. + static bool declMatchesRTFTypes(Function *F, Type *RTFRetType, bool IsVarArg, + SmallVector &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; + if (!IsVarArg && F->arg_size() > RTFArgTypes.size()) + return false; + + auto RTFTyIt = RTFArgTypes.begin(); + for (Argument &Arg : F->args()) { + if (Arg.getType() != *RTFTyIt) + return false; + + if (IsVarArg && ++RTFTyIt == RTFArgTypes.end()) + break; + } + + return true; + } + /// Helper to initialize all runtime function information for those defined in /// OpenMPKinds.def. void initializeRuntimeFunctions() { @@ -415,26 +445,29 @@ #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({__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 ArgsTypes({__VA_ARGS__}); \ + Function *F = M.getFunction(_Name); \ + if (declMatchesRTFTypes(F, _ReturnType, _IsVarArg, 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. } Index: llvm/test/Transforms/OpenMP/rtf_type_checking.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/OpenMP/rtf_type_checking.ll @@ -0,0 +1,64 @@ +; 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