diff --git a/llvm/include/llvm/IR/IntrinsicsVISC.td b/llvm/include/llvm/IR/IntrinsicsVISC.td index ab22372d80ac6ae1de89c2da6c3402fd0787e181..e0d4971f3eda620d35a764d648f94f9c84290ef4 100644 --- a/llvm/include/llvm/IR/IntrinsicsVISC.td +++ b/llvm/include/llvm/IR/IntrinsicsVISC.td @@ -324,5 +324,26 @@ let TargetPrefix = "visc" in { llvm_i32_ty, llvm_i32_ty, llvm_i32_ty], []>; + def int_visc_tensor_fft: Intrinsic<[llvm_ptr_ty], [ + llvm_ptr_ty + ], []>; + def int_visc_tensor_cosineT: Intrinsic<[llvm_ptr_ty], [ + llvm_ptr_ty + ], []>; + def int_visc_tensor_reduce: Intrinsic<[llvm_ptr_ty], [ + llvm_ptr_ty, llvm_i32_ty, llvm_ptrptr_ty, llvm_i32_ty + ], []>; + def int_visc_tensor_projectiveT: Intrinsic<[llvm_ptr_ty], [ + llvm_ptr_ty, llvm_ptr_ty + ], []>; + def int_visc_tensor_map1: Intrinsic<[llvm_ptr_ty], [ + llvm_ptr_ty, llvm_ptrptr_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty + ], []>; + def int_visc_tensor_map2: Intrinsic<[llvm_ptr_ty], [ + llvm_ptr_ty, llvm_ptr_ty, llvm_ptrptr_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty + ], []>; + def int_visc_tensor_map3: Intrinsic<[llvm_ptr_ty], [ + llvm_ptr_ty, llvm_ptr_ty, llvm_ptr_ty, llvm_ptrptr_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty + ], []>; } diff --git a/llvm/lib/Transforms/DFG2LLVM_WrapperAPI/DFG2LLVM_WrapperAPI.cpp b/llvm/lib/Transforms/DFG2LLVM_WrapperAPI/DFG2LLVM_WrapperAPI.cpp index 7ea0c1dce23cf94385df3089c499338bec281b64..e8714c128f745d7949ce0897a57475bd070de4a5 100644 --- a/llvm/lib/Transforms/DFG2LLVM_WrapperAPI/DFG2LLVM_WrapperAPI.cpp +++ b/llvm/lib/Transforms/DFG2LLVM_WrapperAPI/DFG2LLVM_WrapperAPI.cpp @@ -707,6 +707,67 @@ void CodeGenStateMachine::transition(IntrinsicInst *II) { current->transition(this, II); } +static Constant *makeFirstArgument(Module *M, const StringRef &strRef) { + // All interfaces will have a string as first argument, which will be + // used to identify the dataflow node at runtime + // Create string for node name, as first argument for wrapper API call + Constant *ConstArray = ConstantDataArray::getString( + M->getContext(), strRef, true + ); + GlobalVariable *GV = new GlobalVariable( + *M, ConstArray->getType(), true, GlobalValue::ExternalLinkage, ConstArray, "" + ); + // Create GEP expression to access it + Constant* Int_0 = ConstantInt::get(Type::getInt32Ty(M->getContext()), 0); + Constant* GEPIndices[] = { Int_0, Int_0 }; + Constant* GEPConst = ConstantExpr::getGetElementPtr( + GV->getType()->getPointerElementType(), GV, GEPIndices + ); + return GEPConst; +} + +static void pushNArgsInOrder( + std::vector<Value*> &args, IntrinsicInst *tensor_ii, + size_t count, size_t start = 0 +) { + for (size_t i = start; i < start + count; i++) + args.push_back(tensor_ii->getOperand(i)); +} + +static void createWrapperAndReplaceAll( + Module *M, Module *RtM, IntrinsicInst *TensorII, std::vector<Value*> &args, + const std::string &name +) { + // Create wrapper API runtime function call + // Appropriately set the name of the function of the runtime that you + // want to call + // Note: the Constant * is what we need to pass to the callInst. + // This name does not have to match, but does so for similarity. + StringRef name_str = StringRef(name); + Constant* wrapper_func = M->getOrInsertFunction( + name_str, + RtM->getFunction(name_str)->getFunctionType() + ); + CallInst* CI = CallInst::Create( + wrapper_func, args, "", TensorII + ); + // We can replace the call to hpvm.tensor.xxx with the runtime call + TensorII->replaceAllUsesWith(CI); +} + +static void codeGenCommonRoutine( + Module *M, Module *RtM, Function *F, IntrinsicInst *TensorII, + std::vector<Value*> &args, const std::string &name, const StringRef &strRef, + size_t n_inorder_args +) { + DEBUG(errs() << F->getName()); + // Create argument list for the runtime call - stored in Args + args.push_back(makeFirstArgument(M, strRef)); + pushNArgsInOrder(args, TensorII, n_inorder_args); + // Done with argument list. + createWrapperAndReplaceAll(M, RtM, TensorII, args, name); +} + void CodeGenStateMachine::codeGen(DFNode *N, Function *F, const StringRef &strRef, InPlaceDFGAnalysis::InPlaceDFGParameter &IPP) { @@ -1122,6 +1183,41 @@ errs() << "TensorII: " << *TensorII << "\n"; TensorII->replaceAllUsesWith(TensorII->getOperand(0)); } break; + case Intrinsic::visc_tensor_fft: + { + codeGenCommonRoutine(M, RtM, F, TensorII, Args, "wrapper_tensorFFT", strRef, 1); + } + break; + case Intrinsic::visc_tensor_cosineT: + { + codeGenCommonRoutine(M, RtM, F, TensorII, Args, "wrapper_tensor_cosineT", strRef, 1); + } + break; + case Intrinsic::visc_tensor_reduce: + { + codeGenCommonRoutine(M, RtM, F, TensorII, Args, "wrapper_tensor_reduce", strRef, 4); + } + break; + case Intrinsic::visc_tensor_projectiveT: + { + codeGenCommonRoutine(M, RtM, F, TensorII, Args, "wrapper_tensor_projectiveT", strRef, 2); + } + break; + case Intrinsic::visc_tensor_map1: + { + codeGenCommonRoutine(M, RtM, F, TensorII, Args, "wrapper_tensor_map1", strRef, 5); + } + break; + case Intrinsic::visc_tensor_map2: + { + codeGenCommonRoutine(M, RtM, F, TensorII, Args, "wrapper_tensor_map2", strRef, 6); + } + break; + case Intrinsic::visc_tensor_map3: + { + codeGenCommonRoutine(M, RtM, F, TensorII, Args, "wrapper_tensor_map3", strRef, 7); + } + break; /* case Intrinsic::visc_image_fft_transform: { // llvm.hpvm.image.fft.transform - Or another image intrinsic diff --git a/llvm/lib/Transforms/GenVISC/GenVISC.cpp b/llvm/lib/Transforms/GenVISC/GenVISC.cpp index faab312087eade9f5d1e622555de270260f3259d..9e41ca494a4eb6495e0fc79a815abebfc2d3bbba 100644 --- a/llvm/lib/Transforms/GenVISC/GenVISC.cpp +++ b/llvm/lib/Transforms/GenVISC/GenVISC.cpp @@ -177,6 +177,14 @@ IS_VISC_CALL(tensor_clipped_relu) IS_VISC_CALL(tensor_tanh) IS_VISC_CALL(tensor_sigmoid) IS_VISC_CALL(tensor_softmax) +// Image processing tensor operators +IS_VISC_CALL(tensor_fft) +IS_VISC_CALL(tensor_cosineT) +IS_VISC_CALL(tensor_reduce) +IS_VISC_CALL(tensor_projectiveT) +IS_VISC_CALL(tensor_map1) +IS_VISC_CALL(tensor_map2) +IS_VISC_CALL(tensor_map3) // Return the constant integer represented by value V static unsigned getNumericValue(Value* V) { @@ -1308,6 +1316,27 @@ bool GenVISC::runOnModule(Module &M) { if (isVISCCall_tensor_softmax(I)) { ReplaceCallWithIntrinsic(I, Intrinsic::visc_tensor_softmax, &toBeErased); } + if (isVISCCall_tensor_fft(I)) { + ReplaceCallWithIntrinsic(I, Intrinsic::visc_tensor_fft, &toBeErased); + } + if (isVISCCall_tensor_cosineT(I)) { + ReplaceCallWithIntrinsic(I, Intrinsic::visc_tensor_cosineT, &toBeErased); + } + if (isVISCCall_tensor_reduce(I)) { + ReplaceCallWithIntrinsic(I, Intrinsic::visc_tensor_reduce, &toBeErased); + } + if (isVISCCall_tensor_projectiveT(I)) { + ReplaceCallWithIntrinsic(I, Intrinsic::visc_tensor_projectiveT, &toBeErased); + } + if (isVISCCall_tensor_map1(I)) { + ReplaceCallWithIntrinsic(I, Intrinsic::visc_tensor_map1, &toBeErased); + } + if (isVISCCall_tensor_map2(I)) { + ReplaceCallWithIntrinsic(I, Intrinsic::visc_tensor_map2, &toBeErased); + } + if (isVISCCall_tensor_map3(I)) { + ReplaceCallWithIntrinsic(I, Intrinsic::visc_tensor_map3, &toBeErased); + } } // Erase the __visc__node calls diff --git a/llvm/test/VISC/DNN_Benchmarks/common/include/visc.h b/llvm/test/VISC/DNN_Benchmarks/common/include/visc.h index ab4787ffa221e225e354e3fed470dbe632a090c4..095b3d6f89a670b16467db4d0e695adb2fe207d6 100644 --- a/llvm/test/VISC/DNN_Benchmarks/common/include/visc.h +++ b/llvm/test/VISC/DNN_Benchmarks/common/include/visc.h @@ -101,6 +101,14 @@ void* __visc__tensor_pool_mean(void*, int, int, int, int, int, int); void* __visc__tensor_relu(void*); void* __visc__tensor_tanh(void*); void* __visc__tensor_softmax(void*); +// Tensor ops for image processing +void* __visc__tensor_fft(void*); +void* __visc__tensor_cosineT(void*); +void* __visc__tensor_reduce(void*, int, void**, int); +void* __visc__tensor_projectiveT(void*, void*); +void* __visc__tensor_map1(void*, void**, int, int, int); +void* __visc__tensor_map2(void*, void*, void**, int, int, int); +void* __visc__tensor_map3(void*, void*, void*, void**, int, int, int); #include <unistd.h>