Unverified Commit 0ef1c2f9 authored by Jai Menon's avatar Jai Menon Committed by GitHub

Merge pull request #243 from NervanaSystems/bob/static_compiler

Bob/static compiler
parents 7b9b35d3 1ac8d820
...@@ -106,12 +106,14 @@ if (NGRAPH_CPU_ENABLE AND LLVM_INCLUDE_DIR AND ...@@ -106,12 +106,14 @@ if (NGRAPH_CPU_ENABLE AND LLVM_INCLUDE_DIR AND
set(SRC ${SRC} set(SRC ${SRC}
codegen/code_writer.cpp codegen/code_writer.cpp
codegen/compiler.cpp codegen/compiler.cpp
codegen/execution_engine.cpp
runtime/cpu/call_frame.cpp runtime/cpu/call_frame.cpp
runtime/cpu/cpu_backend.cpp runtime/cpu/cpu_backend.cpp
runtime/cpu/cpu_manager.cpp runtime/cpu/cpu_manager.cpp
runtime/cpu/cpu_kernels.cpp runtime/cpu/cpu_kernels.cpp
runtime/cpu/emitter.cpp runtime/cpu/emitter.cpp
runtime/cpu/external_function.cpp runtime/cpu/external_function.cpp
runtime/cpu/memory_handler.cpp
runtime/cpu/tensor_view.cpp runtime/cpu/tensor_view.cpp
) )
# LLVM binary builds are typically built without RTTI # LLVM binary builds are typically built without RTTI
......
...@@ -45,13 +45,15 @@ ...@@ -45,13 +45,15 @@
#include <clang/Frontend/TextDiagnosticPrinter.h> #include <clang/Frontend/TextDiagnosticPrinter.h>
#include <llvm/Support/TargetSelect.h> #include <llvm/Support/TargetSelect.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include "ngraph/codegen/compiler.hpp" #include "ngraph/codegen/compiler.hpp"
#include "ngraph/file_util.hpp"
#include "ngraph/log.hpp"
#include "ngraph/util.hpp"
// TODO: Fix leaks // TODO: Fix leaks
// #define USE_CACHE
using namespace clang; using namespace clang;
using namespace llvm; using namespace llvm;
using namespace llvm::opt; using namespace llvm::opt;
...@@ -59,26 +61,36 @@ using namespace std; ...@@ -59,26 +61,36 @@ using namespace std;
using namespace ngraph::codegen; using namespace ngraph::codegen;
static std::string GetExecutablePath(const char* Argv0) static HeaderCache s_header_cache;
static StaticCompiler s_static_compiler;
static std::mutex m_mutex;
Compiler::Compiler()
{ {
// This just needs to be some symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
void* MainAddr = reinterpret_cast<void*>(GetExecutablePath);
return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
} }
execution_state::execution_state() Compiler::~Compiler()
: m_execution_engine{nullptr}
, precompiled_headers_enabled(false)
, debuginfo_enabled(false)
{ {
} }
execution_state::~execution_state() std::unique_ptr<llvm::Module> Compiler::compile(const std::string& source)
{ {
lock_guard<mutex> lock(m_mutex);
return s_static_compiler.compile(source);
} }
std::unique_ptr<llvm::Module> execution_state::compile(const string& source, const string& name) static std::string GetExecutablePath(const char* Argv0)
{
// This just needs to be some symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
void* MainAddr = reinterpret_cast<void*>(GetExecutablePath);
return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
}
StaticCompiler::StaticCompiler()
: m_precompiled_headers_enabled(false)
, m_debuginfo_enabled(false)
, m_source_name("code.cpp")
{ {
llvm::InitializeAllTargets(); llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs(); llvm::InitializeAllTargetMCs();
...@@ -87,7 +99,7 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con ...@@ -87,7 +99,7 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
// Prepare compilation arguments // Prepare compilation arguments
vector<const char*> args; vector<const char*> args;
args.push_back(name.c_str()); args.push_back(m_source_name.c_str());
// Prepare DiagnosticEngine // Prepare DiagnosticEngine
DiagnosticOptions DiagOpts; DiagnosticOptions DiagOpts;
...@@ -97,45 +109,76 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con ...@@ -97,45 +109,76 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
new DiagnosticsEngine(pDiagIDs, &DiagOpts, textDiagPrinter); new DiagnosticsEngine(pDiagIDs, &DiagOpts, textDiagPrinter);
// Create and initialize CompilerInstance // Create and initialize CompilerInstance
std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); m_compiler = std::unique_ptr<CompilerInstance>(new CompilerInstance());
Clang->createDiagnostics(); m_compiler->createDiagnostics();
// Initialize CompilerInvocation // Initialize CompilerInvocation
CompilerInvocation::CreateFromArgs( CompilerInvocation::CreateFromArgs(
Clang->getInvocation(), &args[0], &args[0] + args.size(), *pDiagnosticsEngine); m_compiler->getInvocation(), &args[0], &args[0] + args.size(), *pDiagnosticsEngine);
// Infer the builtin include path if unspecified. // Infer the builtin include path if unspecified.
if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && if (m_compiler->getHeaderSearchOpts().UseBuiltinIncludes &&
Clang->getHeaderSearchOpts().ResourceDir.empty()) m_compiler->getHeaderSearchOpts().ResourceDir.empty())
{ {
void* MainAddr = reinterpret_cast<void*>(GetExecutablePath); void* MainAddr = reinterpret_cast<void*>(GetExecutablePath);
auto path = CompilerInvocation::GetResourcesPath(args[0], MainAddr); auto path = CompilerInvocation::GetResourcesPath(args[0], MainAddr);
Clang->getHeaderSearchOpts().ResourceDir = path; m_compiler->getHeaderSearchOpts().ResourceDir = path;
} }
auto& HSO = Clang->getInvocation().getHeaderSearchOpts(); if (s_header_cache.is_valid() == false)
{
// Add base toolchain-supplied header paths // Add base toolchain-supplied header paths
// Ideally one would use the Linux toolchain definition in clang/lib/Driver/ToolChains.h // Ideally one would use the Linux toolchain definition in clang/lib/Driver/ToolChains.h
// But that's a private header and isn't part of the public libclang API // But that's a private header and isn't part of the public libclang API
// Instead of re-implementing all of that functionality in a custom toolchain // Instead of re-implementing all of that functionality in a custom toolchain
// just hardcode the paths relevant to frequently used build/test machines for now // just hardcode the paths relevant to frequently used build/test machines for now
HSO.AddPath(CLANG_BUILTIN_HEADERS_PATH, clang::frontend::System, false, false); add_header_search_path(CLANG_BUILTIN_HEADERS_PATH);
HSO.AddPath("/usr/include/x86_64-linux-gnu", clang::frontend::System, false, false); add_header_search_path("/usr/include/x86_64-linux-gnu");
HSO.AddPath("/usr/include", clang::frontend::System, false, false); add_header_search_path("/usr/include");
// Add C++ standard library headers
// Debian-like + GCC 4.8 libstdc++ // Search for headers in
HSO.AddPath("/usr/include/x86_64-linux-gnu/c++/4.8", clang::frontend::System, false, false); // /usr/include/x86_64-linux-gnu/c++/N.N
HSO.AddPath("/usr/include/c++/4.8", clang::frontend::System, false, false); // /usr/include/c++/N.N
// Debian-like + GCC 5 libstdc++ // and add them to the header search path
HSO.AddPath("/usr/include/x86_64-linux-gnu/c++/5", clang::frontend::System, false, false);
HSO.AddPath("/usr/include/c++/5", clang::frontend::System, false, false); file_util::iterate_files("/usr/include/x86_64-linux-gnu/c++/",
HSO.AddPath(EIGEN_HEADERS_PATH, clang::frontend::System, false, false); [&](const std::string& file, bool is_dir) {
HSO.AddPath(NGRAPH_HEADERS_PATH, clang::frontend::System, false, false); if (is_dir)
{
string dir_name = file_util::get_file_name(file);
if (is_version_number(dir_name))
{
add_header_search_path(file);
}
}
});
file_util::iterate_files("/usr/include/c++/", [&](const std::string& file, bool is_dir) {
if (is_dir)
{
string dir_name = file_util::get_file_name(file);
if (is_version_number(dir_name))
{
add_header_search_path(file);
}
}
});
add_header_search_path(EIGEN_HEADERS_PATH);
add_header_search_path(NGRAPH_HEADERS_PATH);
#ifdef USE_CACHE
s_header_cache.set_valid();
#endif
}
#ifdef USE_CACHE
use_cached_files(m_compiler);
#endif
// Language options // Language options
// These are the C++ features needed to compile ngraph headers // These are the C++ features needed to compile ngraph headers
// and any dependencies like Eigen // and any dependencies like Eigen
auto LO = Clang->getInvocation().getLangOpts(); auto LO = m_compiler->getInvocation().getLangOpts();
LO->CPlusPlus = 1; LO->CPlusPlus = 1;
LO->CPlusPlus11 = 1; LO->CPlusPlus11 = 1;
LO->Bool = 1; LO->Bool = 1;
...@@ -148,7 +191,7 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con ...@@ -148,7 +191,7 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
LO->OpenMPUseTLS = 1; LO->OpenMPUseTLS = 1;
// CodeGen options // CodeGen options
auto& CGO = Clang->getInvocation().getCodeGenOpts(); auto& CGO = m_compiler->getInvocation().getCodeGenOpts();
CGO.OptimizationLevel = 3; CGO.OptimizationLevel = 3;
CGO.RelocationModel = "static"; CGO.RelocationModel = "static";
CGO.ThreadModel = "posix"; CGO.ThreadModel = "posix";
...@@ -158,22 +201,22 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con ...@@ -158,22 +201,22 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
CGO.VectorizeSLP = 1; CGO.VectorizeSLP = 1;
CGO.CXAAtExit = 0; CGO.CXAAtExit = 0;
if (debuginfo_enabled) if (m_debuginfo_enabled)
{ {
CGO.setDebugInfo(codegenoptions::FullDebugInfo); CGO.setDebugInfo(codegenoptions::FullDebugInfo);
} }
if (precompiled_headers_enabled) if (m_precompiled_headers_enabled)
{ {
// Preprocessor options // Preprocessor options
auto& PPO = Clang->getInvocation().getPreprocessorOpts(); auto& PPO = m_compiler->getInvocation().getPreprocessorOpts();
PPO.ImplicitPCHInclude = "ngcpu.pch"; PPO.ImplicitPCHInclude = "ngcpu.pch";
PPO.DisablePCHValidation = 1; PPO.DisablePCHValidation = 1;
} }
// Enable various target features // Enable various target features
// Most of these are for Eigen // Most of these are for Eigen
auto& TO = Clang->getInvocation().getTargetOpts(); auto& TO = m_compiler->getInvocation().getTargetOpts();
// TODO: This needs to be configurable and selected carefully // TODO: This needs to be configurable and selected carefully
TO.CPU = "broadwell"; TO.CPU = "broadwell";
TO.FeaturesAsWritten.emplace_back("+sse"); TO.FeaturesAsWritten.emplace_back("+sse");
...@@ -185,62 +228,121 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con ...@@ -185,62 +228,121 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
TO.FeaturesAsWritten.emplace_back("+avx"); TO.FeaturesAsWritten.emplace_back("+avx");
TO.FeaturesAsWritten.emplace_back("+avx2"); TO.FeaturesAsWritten.emplace_back("+avx2");
TO.FeaturesAsWritten.emplace_back("+fma"); TO.FeaturesAsWritten.emplace_back("+fma");
}
// Map code filename to a memoryBuffer StaticCompiler::~StaticCompiler()
StringRef source_ref(source); {
unique_ptr<MemoryBuffer> buffer = MemoryBuffer::getMemBufferCopy(source_ref); }
Clang->getInvocation().getPreprocessorOpts().addRemappedFile(name, buffer.get());
// Create and execute action bool StaticCompiler::is_version_number(const string& path)
CodeGenAction* compilerAction = new EmitCodeGenOnlyAction(); {
std::unique_ptr<llvm::Module> rc; bool rc = true;
if (Clang->ExecuteAction(*compilerAction) == true) vector<string> tokens = ngraph::split(path, '.');
for (string s : tokens)
{ {
rc = compilerAction->takeModule(); for (char c : s)
{
if (!isdigit(c))
{
rc = false;
}
}
} }
buffer.release();
return rc; return rc;
} }
bool execution_state::add_module(std::unique_ptr<llvm::Module>& module) void StaticCompiler::add_header_search_path(const string& path)
{ {
if (module) if (!contains(m_extra_search_path_list, path))
{
m_extra_search_path_list.push_back(path);
HeaderSearchOptions& hso = m_compiler->getInvocation().getHeaderSearchOpts();
#ifdef USE_CACHE
static vector<string> valid_ext = {".h", ".hpp", ".tcc", ""};
string mapped_path = file_util::path_join("/$BUILTIN", path);
mapped_path = path;
s_header_cache.add_path(mapped_path);
auto func = [&](const std::string& file, bool is_dir) {
if (!is_dir)
{ {
if (!m_execution_engine) string ext = file_util::get_file_ext(file);
if (contains(valid_ext, ext))
{ {
m_execution_engine = llvm::EngineBuilder(move(module)) // This is a header file
.setEngineKind(llvm::EngineKind::JIT) string relative_name = file.substr(path.size() + 1);
.setOptLevel(llvm::CodeGenOpt::Aggressive) string mapped_name = file_util::path_join(mapped_path, relative_name);
.setErrorStr(&jit_error)
.create();
if (!m_execution_engine) ErrorOr<unique_ptr<MemoryBuffer>> code = MemoryBuffer::getFile(file);
if (error_code ec = code.getError())
{ {
return false; // throw up
throw runtime_error("could not find file '" + file + "'");
} }
s_header_cache.add_file(mapped_name, code.get());
} }
} }
else };
{ file_util::iterate_files(path, func, true);
return false; #else
hso.AddPath(path, clang::frontend::System, false, false);
#endif
} }
return true;
} }
void execution_state::finalize() void StaticCompiler::use_cached_files()
{ {
if (m_execution_engine) HeaderSearchOptions& hso = m_compiler->getInvocation().getHeaderSearchOpts();
for (const string& path : s_header_cache.get_include_paths())
{ {
m_execution_engine->finalizeObject(); hso.AddPath(path, clang::frontend::System, false, false);
m_execution_engine->runStaticConstructorsDestructors(false);
} }
else for (auto& header : s_header_cache.get_header_map())
{
m_compiler->getPreprocessorOpts().addRemappedFile(header.first, header.second.get());
}
}
std::unique_ptr<llvm::Module> StaticCompiler::compile(const string& source)
{
// Map code filename to a memoryBuffer
StringRef source_ref(source);
unique_ptr<MemoryBuffer> buffer = MemoryBuffer::getMemBufferCopy(source_ref);
m_compiler->getInvocation().getPreprocessorOpts().addRemappedFile(m_source_name, buffer.get());
// Create and execute action
CodeGenAction* compilerAction = new EmitCodeGenOnlyAction();
std::unique_ptr<llvm::Module> rc;
if (m_compiler->ExecuteAction(*compilerAction) == true)
{ {
throw std::runtime_error( rc = compilerAction->takeModule();
"Error in finalize: " +
(jit_error.empty() ? "Could not create an execution engine" : jit_error));
} }
buffer.release();
m_compiler->getInvocation().getPreprocessorOpts().clearRemappedFiles();
return rc;
} }
// std::unique_ptr<llvm::Module> StaticCompiler::generate_pch(const string& source)
// {
// // Map code filename to a memoryBuffer
// StringRef source_ref(source);
// unique_ptr<MemoryBuffer> buffer = MemoryBuffer::getMemBufferCopy(source_ref);
// m_compiler->getInvocation().getPreprocessorOpts().addRemappedFile(m_source_name, buffer.get());
// // Create and execute action
// CodeGenAction* compilerAction = new GeneratePCHAction();
// std::unique_ptr<llvm::Module> rc;
// if (m_compiler->ExecuteAction(*compilerAction) == true)
// {
// rc = compilerAction->takeModule();
// }
// buffer.release();
// m_compiler->getInvocation().getPreprocessorOpts().clearRemappedFiles();
// return rc;
// }
...@@ -27,10 +27,18 @@ namespace ngraph ...@@ -27,10 +27,18 @@ namespace ngraph
namespace codegen namespace codegen
{ {
class module; class module;
class execution_state; class Compiler;
class StaticCompiler;
class HeaderCache;
} }
} }
namespace clang
{
class HeaderSearchOptions;
class CompilerInstance;
}
class ngraph::codegen::module class ngraph::codegen::module
{ {
public: public:
...@@ -38,39 +46,57 @@ private: ...@@ -38,39 +46,57 @@ private:
std::unique_ptr<llvm::Module> m_module; std::unique_ptr<llvm::Module> m_module;
}; };
class ngraph::codegen::execution_state : public llvm::SectionMemoryManager class ngraph::codegen::Compiler
{ {
public: public:
execution_state(); Compiler();
~execution_state(); ~Compiler();
std::unique_ptr<llvm::Module> compile(const std::string& source);
void set_precompiled_headers_enabled(bool state) { precompiled_headers_enabled = state; }
bool is_precompiled_headers_enabled() { return precompiled_headers_enabled; }
void set_debuginfo_enabled(bool state) { debuginfo_enabled = state; }
bool is_debuginfo_enabled() { return debuginfo_enabled; }
std::unique_ptr<llvm::Module> compile(const std::string& source, const std::string& name = "");
bool add_module(std::unique_ptr<llvm::Module>&);
void finalize(); private:
};
template <typename ftype> class ngraph::codegen::StaticCompiler : public llvm::SectionMemoryManager
std::function<ftype> find_function(const std::string& func_name) {
{ public:
auto f = m_execution_engine->getPointerToNamedFunction(func_name); StaticCompiler();
~StaticCompiler();
return f_cast<ftype>(f); void set_precompiled_headers_enabled(bool state) { m_precompiled_headers_enabled = state; }
} bool is_precompiled_headers_enabled() { return m_precompiled_headers_enabled; }
void set_debuginfo_enabled(bool state) { m_debuginfo_enabled = state; }
bool is_debuginfo_enabled() { return m_debuginfo_enabled; }
void add_header_search_path(const std::string& path);
std::unique_ptr<llvm::Module> compile(const std::string& source);
private: private:
llvm::ExecutionEngine* m_execution_engine; std::unique_ptr<clang::CompilerInstance> m_compiler;
std::string jit_error; bool m_precompiled_headers_enabled;
bool precompiled_headers_enabled; bool m_debuginfo_enabled;
bool debuginfo_enabled; std::string m_source_name;
std::vector<std::string> m_extra_search_path_list;
template <typename signature> bool is_version_number(const std::string& path);
std::function<signature> f_cast(void* f) void use_cached_files();
};
class ngraph::codegen::HeaderCache
{
public:
bool is_valid() const { return m_headers_valid; }
bool set_valid() { return m_headers_valid = true; }
void add_path(const std::string& path) { m_include_paths.push_back(path); }
void add_file(const std::string& path, std::unique_ptr<llvm::MemoryBuffer>& code)
{
m_headers.insert(std::make_pair(path, std::move(code)));
}
const std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>>& get_header_map() const
{ {
return static_cast<signature*>(reinterpret_cast<signature*>(f)); return m_headers;
} }
const std::vector<std::string>& get_include_paths() const { return m_include_paths; }
private:
std::map<std::string, std::unique_ptr<llvm::MemoryBuffer>> m_headers;
std::vector<std::string> m_include_paths;
bool m_headers_valid;
}; };
// ----------------------------------------------------------------------------
// Copyright 2017 Nervana Systems Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include "ngraph/codegen/execution_engine.hpp"
using namespace ngraph;
codegen::ExecutionEngine::ExecutionEngine()
: m_execution_engine{nullptr}
{
}
codegen::ExecutionEngine::~ExecutionEngine()
{
}
bool codegen::ExecutionEngine::add_module(std::unique_ptr<llvm::Module>& module)
{
if (module)
{
if (!m_execution_engine)
{
m_execution_engine = llvm::EngineBuilder(move(module))
.setEngineKind(llvm::EngineKind::JIT)
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setErrorStr(&m_jit_error)
.create();
if (!m_execution_engine)
{
return false;
}
}
}
else
{
return false;
}
return true;
}
void codegen::ExecutionEngine::finalize()
{
if (m_execution_engine)
{
m_execution_engine->finalizeObject();
m_execution_engine->runStaticConstructorsDestructors(false);
}
else
{
throw std::runtime_error(
"Error in finalize: " +
(m_jit_error.empty() ? "Could not create an execution engine" : m_jit_error));
}
}
// ----------------------------------------------------------------------------
// Copyright 2017 Nervana Systems Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#pragma once
#include <memory>
#include <llvm/ExecutionEngine/MCJIT.h> // forces JIT to link in
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/Option/Arg.h>
namespace ngraph
{
namespace codegen
{
class ExecutionEngine;
}
}
class ngraph::codegen::ExecutionEngine
{
public:
ExecutionEngine();
~ExecutionEngine();
bool add_module(std::unique_ptr<llvm::Module>& module);
void finalize();
template <typename ftype>
std::function<ftype> find_function(const std::string& func_name)
{
auto f = m_execution_engine->getPointerToNamedFunction(func_name);
return f_cast<ftype>(f);
}
private:
llvm::ExecutionEngine* m_execution_engine;
std::string m_jit_error;
template <typename signature>
std::function<signature> f_cast(void* f)
{
return static_cast<signature*>(reinterpret_cast<signature*>(f));
}
};
/* // ----------------------------------------------------------------------------
Copyright 2016 Nervana Systems Inc. // Copyright 2017 Nervana Systems Inc.
Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
You may obtain a copy of the License at // You may obtain a copy of the License at
//
http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
//
Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
limitations under the License. // ----------------------------------------------------------------------------
*/
#include <cassert> #include <cassert>
#include <dirent.h> #include <dirent.h>
...@@ -29,10 +28,37 @@ ...@@ -29,10 +28,37 @@
#include <unistd.h> #include <unistd.h>
#include <vector> #include <vector>
#include "file_util.hpp" #include "ngraph/file_util.hpp"
#include "ngraph/log.hpp"
using namespace std; using namespace std;
std::string ngraph::file_util::get_file_name(const std::string& s)
{
string rc = s;
auto pos = s.find_last_of('/');
if (pos != string::npos)
{
rc = s.substr(pos + 1);
}
return rc;
}
std::string ngraph::file_util::get_file_ext(const std::string& s)
{
string rc = get_file_name(s);
auto pos = rc.find_last_of('.');
if (pos != string::npos)
{
rc = rc.substr(pos);
}
else
{
rc = "";
}
return rc;
}
string ngraph::file_util::path_join(const string& s1, const string& s2) string ngraph::file_util::path_join(const string& s1, const string& s2)
{ {
string rc; string rc;
...@@ -79,20 +105,22 @@ size_t ngraph::file_util::get_file_size(const string& filename) ...@@ -79,20 +105,22 @@ size_t ngraph::file_util::get_file_size(const string& filename)
void ngraph::file_util::remove_directory(const string& dir) void ngraph::file_util::remove_directory(const string& dir)
{ {
struct stat status; struct stat status;
if (stat(dir.c_str(), &status) == -1) if (stat(dir.c_str(), &status) != -1)
{ {
return; iterate_files(dir,
}
file_util::iterate_files(dir,
[](const string& file, bool is_dir) { [](const string& file, bool is_dir) {
if (is_dir) if (is_dir)
{
rmdir(file.c_str()); rmdir(file.c_str());
}
else else
{
remove(file.c_str()); remove(file.c_str());
}
}, },
true); true);
rmdir(dir.c_str()); rmdir(dir.c_str());
}
} }
void ngraph::file_util::remove_file(const string& file) void ngraph::file_util::remove_file(const string& file)
...@@ -117,7 +145,7 @@ bool ngraph::file_util::make_directory(const string& dir) ...@@ -117,7 +145,7 @@ bool ngraph::file_util::make_directory(const string& dir)
string ngraph::file_util::make_temp_directory(const string& path) string ngraph::file_util::make_temp_directory(const string& path)
{ {
string fname = path.empty() ? file_util::get_temp_directory() : path; string fname = path.empty() ? file_util::get_temp_directory() : path;
string tmp_template = file_util::path_join(fname, "aeonXXXXXX"); string tmp_template = file_util::path_join(fname, "ngraph_XXXXXX");
char* tmpname = strdup(tmp_template.c_str()); char* tmpname = strdup(tmp_template.c_str());
mkdtemp(tmpname); mkdtemp(tmpname);
...@@ -129,7 +157,7 @@ string ngraph::file_util::make_temp_directory(const string& path) ...@@ -129,7 +157,7 @@ string ngraph::file_util::make_temp_directory(const string& path)
std::string ngraph::file_util::get_temp_directory() std::string ngraph::file_util::get_temp_directory()
{ {
const vector<string> potential_tmps = {"NERVANA_AEON_TMP", "TMPDIR", "TMP", "TEMP", "TEMPDIR"}; const vector<string> potential_tmps = {"NGRAPH_TMP", "TMPDIR", "TMP", "TEMP", "TEMPDIR"};
const char* path = nullptr; const char* path = nullptr;
for (const string& var : potential_tmps) for (const string& var : potential_tmps)
...@@ -161,7 +189,7 @@ vector<char> ngraph::file_util::read_file_contents(const string& path) ...@@ -161,7 +189,7 @@ vector<char> ngraph::file_util::read_file_contents(const string& path)
char* p = data.data(); char* p = data.data();
size_t remainder = file_size; size_t remainder = file_size;
size_t offset = 0; size_t offset = 0;
while (remainder > 0) while (f && remainder > 0)
{ {
size_t rc = fread(&p[offset], 1, remainder, f); size_t rc = fread(&p[offset], 1, remainder, f);
offset += rc; offset += rc;
...@@ -197,12 +225,16 @@ void ngraph::file_util::iterate_files(const string& path, ...@@ -197,12 +225,16 @@ void ngraph::file_util::iterate_files(const string& path,
else else
files.push_back(file); files.push_back(file);
}, },
true); recurse);
for (auto f : files) for (auto f : files)
{
func(f, false); func(f, false);
}
for (auto f : dirs) for (auto f : dirs)
{
func(f, true); func(f, true);
}
} }
void ngraph::file_util::iterate_files_worker( void ngraph::file_util::iterate_files_worker(
...@@ -218,18 +250,23 @@ void ngraph::file_util::iterate_files_worker( ...@@ -218,18 +250,23 @@ void ngraph::file_util::iterate_files_worker(
switch (ent->d_type) switch (ent->d_type)
{ {
case DT_DIR: case DT_DIR:
if (recurse && name != "." && name != "..") if (name != "." && name != "..")
{ {
string dir_path = file_util::path_join(path, name); string dir_path = file_util::path_join(path, name);
if (recurse)
{
iterate_files(dir_path, func, recurse); iterate_files(dir_path, func, recurse);
}
func(dir_path, true); func(dir_path, true);
} }
break; break;
case DT_LNK: break; case DT_LNK: break;
case DT_REG: case DT_REG:
name = file_util::path_join(path, name); {
func(name, false); string file_name = file_util::path_join(path, name);
func(file_name, false);
break; break;
}
default: break; default: break;
} }
} }
......
/* // ----------------------------------------------------------------------------
Copyright 2016 Nervana Systems Inc. // Copyright 2017 Nervana Systems Inc.
Licensed under the Apache License, Version 2.0 (the "License"); // Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
You may obtain a copy of the License at // You may obtain a copy of the License at
//
http://www.apache.org/licenses/LICENSE-2.0 // http://www.apache.org/licenses/LICENSE-2.0
//
Unless required by applicable law or agreed to in writing, software // Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, // distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
limitations under the License. // ----------------------------------------------------------------------------
*/
#pragma once #pragma once
...@@ -27,6 +26,8 @@ namespace ngraph ...@@ -27,6 +26,8 @@ namespace ngraph
class ngraph::file_util class ngraph::file_util
{ {
public: public:
static std::string get_file_name(const std::string&);
static std::string get_file_ext(const std::string&);
static std::string path_join(const std::string& s1, const std::string& s2); static std::string path_join(const std::string& s1, const std::string& s2);
static size_t get_file_size(const std::string& filename); static size_t get_file_size(const std::string& filename);
static void remove_directory(const std::string& dir); static void remove_directory(const std::string& dir);
......
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
#pragma once #pragma once
#include <cinttypes>
#include <cstddef> #include <cstddef>
#include <cstdint>
// CBLAS types and wrappers // CBLAS types and wrappers
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
#include <memory> #include <memory>
#include "ngraph/codegen/compiler.hpp" #include "ngraph/codegen/execution_engine.hpp"
#include "ngraph/runtime/manager.hpp" #include "ngraph/runtime/manager.hpp"
namespace ngraph namespace ngraph
...@@ -33,7 +33,7 @@ namespace ngraph ...@@ -33,7 +33,7 @@ namespace ngraph
class CPUManager : public Manager class CPUManager : public Manager
{ {
protected: protected:
ngraph::codegen::execution_state exec_state; ngraph::codegen::ExecutionEngine exec_state;
public: public:
virtual std::shared_ptr<Backend> allocate_backend() override; virtual std::shared_ptr<Backend> allocate_backend() override;
......
...@@ -89,7 +89,7 @@ namespace ngraph ...@@ -89,7 +89,7 @@ namespace ngraph
}; };
} }
// ET element type // T element type
// FMT array format (fmt::V for vector, etc.) // FMT array format (fmt::V for vector, etc.)
// BASE select array/matrix // BASE select array/matrix
template <typename T, template <typename T,
......
...@@ -66,20 +66,12 @@ void Emitter::EmitAdd(const ngraph::Node* n, ...@@ -66,20 +66,12 @@ void Emitter::EmitAdd(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
string type = et.c_type_string();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << type << ">(" << outputs[0].get_tensor().get_name() << ", " TU << emit_array1d(outputs[0]) << " = \n";
<< eigen_vector_format(outputs[0]) << ") =\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << type << ">(" << inputs[0].get_tensor().get_name() << ", " TU << emit_array1d(inputs[0]) << " +\n ";
<< eigen_vector_format(inputs[0]) << ") +\n"; TU << emit_array1d(inputs[1]) << ";\n";
TU << "EigenArray1d<" << type << ">(" << inputs[1].get_tensor().get_name() << ", "
<< eigen_vector_format(inputs[1]) << ");\n";
TU.indent -= 2; TU.indent -= 2;
TU << "}\n"; TU << "}\n";
} }
...@@ -111,11 +103,8 @@ void Emitter::EmitDot(const ngraph::Node* n, ...@@ -111,11 +103,8 @@ void Emitter::EmitDot(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenVector<" << arg0_element_type.c_type_string() << ">(" TU << "" << emit_vector(outputs[0]) << "\n = ";
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) TU << first.get_tensor().get_name() << "[0]\n * " << emit_vector(second) << ";\n";
<< ")\n = " << first.get_tensor().get_name() << "[0]\n * EigenVector<"
<< arg0_element_type.c_type_string() << ">(" << second.get_tensor().get_name() << ", "
<< eigen_vector_format(second) << ");\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -123,15 +112,9 @@ void Emitter::EmitDot(const ngraph::Node* n, ...@@ -123,15 +112,9 @@ void Emitter::EmitDot(const ngraph::Node* n,
{ {
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenVector<" << arg0_element_type.c_type_string() << ">(" TU << "" << emit_vector(outputs[0]) << " << \n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_vector(inputs[0]) << ".dot("
<< ") << \n" << "" << emit_vector(inputs[1]) << ");\n";
<< " EigenVector<" << arg0_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0])
<< ").dot("
<< "EigenVector<" << arg0_element_type.c_type_string() << ">("
<< inputs[1].get_tensor().get_name() << ", " << eigen_vector_format(inputs[1])
<< "));\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -141,14 +124,9 @@ void Emitter::EmitDot(const ngraph::Node* n, ...@@ -141,14 +124,9 @@ void Emitter::EmitDot(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenVector<" << arg0_element_type.c_type_string() << ">(" TU << "" << emit_vector(outputs[0]) << " = \n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_matrix(inputs[0]) << " * "
<< ") = \n" << "" << emit_vector(inputs[1]) << ";\n";
<< " EigenMatrix<" << arg0_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg0_layout->get_shape(), arg0_layout->get_strides()) << ") * "
<< "EigenVector<" << arg0_element_type.c_type_string() << ">("
<< inputs[1].get_tensor().get_name() << ", " << eigen_vector_format(inputs[1]) << ");\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -179,18 +157,9 @@ void Emitter::EmitDot(const ngraph::Node* n, ...@@ -179,18 +157,9 @@ void Emitter::EmitDot(const ngraph::Node* n,
{ {
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenMatrix<" << arg0_element_type.c_type_string() << ">(" TU << "" << emit_matrix(outputs[0]) << " = \n"
<< outputs[0].get_tensor().get_name() << ", " << " " << emit_matrix(inputs[0]) << " * "
<< eigen_matrix_format(out_layout->get_shape(), out_layout->get_strides()) << "" << emit_matrix(inputs[1]) << ";\n";
<< ") = \n"
<< " EigenMatrix<" << arg0_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg0_layout->get_shape(), arg0_layout->get_strides())
<< ") * "
<< "EigenMatrix<" << arg0_element_type.c_type_string() << ">("
<< inputs[1].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg1_layout->get_shape(), arg1_layout->get_strides())
<< ");\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -213,12 +182,9 @@ void Emitter::EmitMultiply(const ngraph::Node* n, ...@@ -213,12 +182,9 @@ void Emitter::EmitMultiply(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << type << ">(" << outputs[0].get_tensor().get_name() << ", " TU << emit_array1d(outputs[0]) << " =\n"
<< eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << " *\n"
<< " EigenArray1d<" << type << ">(" << inputs[0].get_tensor().get_name() << ", " << " " << emit_array1d(inputs[1]) << ";\n";
<< eigen_vector_format(inputs[0]) << ") *\n"
<< " EigenArray1d<" << type << ">(" << inputs[1].get_tensor().get_name() << ", "
<< eigen_vector_format(inputs[1]) << ");\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -276,16 +242,10 @@ void Emitter::EmitAbs(const ngraph::Node* n, ...@@ -276,16 +242,10 @@ void Emitter::EmitAbs(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n";
<< ", " << eigen_vector_format(outputs[0]) << ") =\n"; TU << "Eigen::abs(" << emit_array1d(inputs[0]) << ");\n";
TU << "Eigen::abs(EigenArray1d<" << et.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0]) << "));\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -299,24 +259,19 @@ void Emitter::EmitConcat(const ngraph::Node* n, ...@@ -299,24 +259,19 @@ void Emitter::EmitConcat(const ngraph::Node* n,
assert(result_tensor_type); assert(result_tensor_type);
auto result_shape = result_tensor_type->get_shape(); auto result_shape = result_tensor_type->get_shape();
auto& result_element_type = result_tensor_type->get_element_type();
if (result_shape.size() == 1) if (result_shape.size() == 1)
{ {
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenVector<" << result_element_type.c_type_string() << "> out_vector(" TU << "" << emit_vector(outputs[0], "out_vector") << ";\n";
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0])
<< ");\n";
size_t concat_pos = 0; size_t concat_pos = 0;
for (size_t i = 0; i < inputs.size(); i++) for (size_t i = 0; i < inputs.size(); i++)
{ {
TU << "out_vector.segment(" << concat_pos << ", " TU << "out_vector.segment(" << concat_pos << ", "
<< inputs[i].get_tensor_view_layout()->get_shape().at(0) << ") << " << inputs[i].get_tensor_view_layout()->get_shape().at(0) << ") << "
<< "EigenVector<" << result_element_type.c_type_string() << ">(" << "" << emit_vector(inputs[i]) << ";\n";
<< inputs[i].get_tensor().get_name() << ", " << eigen_vector_format(inputs[i])
<< ");\n";
concat_pos += inputs[i].get_tensor_view_layout()->get_shape().at(0); concat_pos += inputs[i].get_tensor_view_layout()->get_shape().at(0);
} }
TU.indent--; TU.indent--;
...@@ -329,9 +284,7 @@ void Emitter::EmitConcat(const ngraph::Node* n, ...@@ -329,9 +284,7 @@ void Emitter::EmitConcat(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenMatrix<" << result_element_type.c_type_string() << "> out_matrix(" TU << "" << emit_matrix(outputs[0], "out_matrix") << ";\n";
<< outputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(out_layout->get_shape(), out_layout->get_strides()) << ");\n";
size_t concat_pos[2]{0, 0}; size_t concat_pos[2]{0, 0};
for (size_t i = 0; i < inputs.size(); i++) for (size_t i = 0; i < inputs.size(); i++)
...@@ -341,9 +294,7 @@ void Emitter::EmitConcat(const ngraph::Node* n, ...@@ -341,9 +294,7 @@ void Emitter::EmitConcat(const ngraph::Node* n,
TU << "out_matrix.block(" << concat_pos[0] << ", " << concat_pos[1] << ", " TU << "out_matrix.block(" << concat_pos[0] << ", " << concat_pos[1] << ", "
<< arg_shape.at(0) << ", " << arg_shape.at(1) << ") << " << arg_shape.at(0) << ", " << arg_shape.at(1) << ") << "
<< "EigenMatrix<" << result_element_type.c_type_string() << ">(" << "" << emit_matrix(inputs[i]) << ";\n";
<< inputs[i].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg_layout->get_shape(), arg_layout->get_strides()) << ");\n";
concat_pos[axis] += arg_shape.at(axis); concat_pos[axis] += arg_shape.at(axis);
} }
...@@ -358,18 +309,11 @@ void Emitter::EmitDivide(const ngraph::Node* n, ...@@ -358,18 +309,11 @@ void Emitter::EmitDivide(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << " /\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ";\n";
<< ", " << eigen_vector_format(inputs[0]) << ") /\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << ");\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -379,18 +323,11 @@ void Emitter::EmitEqual(const ngraph::Node* n, ...@@ -379,18 +323,11 @@ void Emitter::EmitEqual(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << element::Bool::element_type().c_type_string() << ">(" TU << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << ") =\n" << " (" << emit_array1d(inputs[0]) << " ==\n"
<< " (EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ").template cast<char>();\n";
<< ", " << eigen_vector_format(inputs[0]) << ") ==\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << ")).template cast<char>();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -400,18 +337,11 @@ void Emitter::EmitGreater(const ngraph::Node* n, ...@@ -400,18 +337,11 @@ void Emitter::EmitGreater(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et = TU << "{ // " << n->get_name() << " xxx\n";
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << element::Bool::element_type().c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << ") =\n" << " (" << emit_array1d(inputs[0]) << " >\n"
<< " (EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ").template cast<char>();\n";
<< ", " << eigen_vector_format(inputs[0]) << ") >\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << ")).template cast<char>();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -421,18 +351,11 @@ void Emitter::EmitGreaterEq(const ngraph::Node* n, ...@@ -421,18 +351,11 @@ void Emitter::EmitGreaterEq(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << element::Bool::element_type().c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << ") =\n" << " (" << emit_array1d(inputs[0]) << " >=\n"
<< " (EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ").template cast<char>();\n";
<< ", " << eigen_vector_format(inputs[0]) << ") >=\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << ")).template cast<char>();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -442,18 +365,11 @@ void Emitter::EmitLess(const ngraph::Node* n, ...@@ -442,18 +365,11 @@ void Emitter::EmitLess(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << element::Bool::element_type().c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << ") =\n" << " (" << emit_array1d(inputs[0]) << " <\n"
<< " (EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ").template cast<char>();\n";
<< ", " << eigen_vector_format(inputs[0]) << ") <\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << ")).template cast<char>();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -463,18 +379,11 @@ void Emitter::EmitLessEq(const ngraph::Node* n, ...@@ -463,18 +379,11 @@ void Emitter::EmitLessEq(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << element::Bool::element_type().c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << ") =\n" << " (" << emit_array1d(inputs[0]) << " <=\n"
<< " (EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ").template cast<char>();\n";
<< ", " << eigen_vector_format(inputs[0]) << ") <=\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << ")).template cast<char>();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -484,16 +393,10 @@ void Emitter::EmitLog(const ngraph::Node* n, ...@@ -484,16 +393,10 @@ void Emitter::EmitLog(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " Eigen::log(" << emit_array1d(inputs[0]) << ");\n";
<< " Eigen::log(EigenArray1d<" << et.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0]) << "));\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -503,18 +406,11 @@ void Emitter::EmitMaximum(const ngraph::Node* n, ...@@ -503,18 +406,11 @@ void Emitter::EmitMaximum(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".max(\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ");\n";
<< ", " << eigen_vector_format(inputs[0]) << ").max(\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << "));\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -524,18 +420,11 @@ void Emitter::EmitMinimum(const ngraph::Node* n, ...@@ -524,18 +420,11 @@ void Emitter::EmitMinimum(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".min(\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ");\n";
<< ", " << eigen_vector_format(inputs[0]) << ").min(\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << "));\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -545,16 +434,10 @@ void Emitter::EmitNegative(const ngraph::Node* n, ...@@ -545,16 +434,10 @@ void Emitter::EmitNegative(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " -" << emit_array1d(inputs[0]) << ";\n";
<< " -EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ");\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -564,18 +447,11 @@ void Emitter::EmitNotEqual(const ngraph::Node* n, ...@@ -564,18 +447,11 @@ void Emitter::EmitNotEqual(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << element::Bool::element_type().c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << ") =\n" << " (" << emit_array1d(inputs[0]) << " !=\n"
<< " (EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ").template cast<char>();\n";
<< ", " << eigen_vector_format(inputs[0]) << ") !=\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << ")).template cast<char>();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -585,20 +461,12 @@ void Emitter::EmitSelect(const ngraph::Node* n, ...@@ -585,20 +461,12 @@ void Emitter::EmitSelect(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(1)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << "\n"
<< " EigenArray1d<" << element::Bool::element_type().c_type_string() << ">(" << " .select(" << emit_array1d(inputs[1]) << ",\n"
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0]) << ")\n" << " " << emit_array1d(inputs[2]) << ");\n";
<< " .select(EigenArray1d<" << et.c_type_string() << ">("
<< inputs[1].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0]) << "),\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[2].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << "));\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -608,18 +476,11 @@ void Emitter::EmitSubtract(const ngraph::Node* n, ...@@ -608,18 +476,11 @@ void Emitter::EmitSubtract(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << " -\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name() << " " << emit_array1d(inputs[1]) << ";\n";
<< ", " << eigen_vector_format(inputs[0]) << ") -\n"
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[1].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[1]) << ");\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -808,7 +669,6 @@ void Emitter::EmitBroadcast(const ngraph::Node* n, ...@@ -808,7 +669,6 @@ void Emitter::EmitBroadcast(const ngraph::Node* n,
auto arg_shape = arg_tensor_type->get_shape(); auto arg_shape = arg_tensor_type->get_shape();
auto result_shape = result_tensor_type->get_shape(); auto result_shape = result_tensor_type->get_shape();
auto& result_element_type = result_tensor_type->get_element_type();
if (broadcast->get_broadcast_axes().empty()) if (broadcast->get_broadcast_axes().empty())
{ {
...@@ -826,12 +686,8 @@ void Emitter::EmitBroadcast(const ngraph::Node* n, ...@@ -826,12 +686,8 @@ void Emitter::EmitBroadcast(const ngraph::Node* n,
{ {
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << result_element_type.c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_array1d(inputs[0]) << "(0, 0);\n";
<< ") =\n"
<< " EigenArray1d<" << result_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0])
<< ")(0, 0);\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -843,13 +699,8 @@ void Emitter::EmitBroadcast(const ngraph::Node* n, ...@@ -843,13 +699,8 @@ void Emitter::EmitBroadcast(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenMatrix<" << result_element_type.c_type_string() << ">(" TU << "" << emit_matrix(outputs[0]) << ".colwise() =\n"
<< outputs[0].get_tensor().get_name() << ", " << " " << emit_vector(inputs[0]) << ";\n";
<< eigen_matrix_format(out_layout->get_shape(), out_layout->get_strides())
<< ").colwise() =\n"
<< " EigenVector<" << result_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0])
<< ");\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -859,13 +710,8 @@ void Emitter::EmitBroadcast(const ngraph::Node* n, ...@@ -859,13 +710,8 @@ void Emitter::EmitBroadcast(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenMatrix<" << result_element_type.c_type_string() << ">(" TU << "" << emit_matrix(outputs[0]) << ".rowwise() =\n"
<< outputs[0].get_tensor().get_name() << ", " << " " << emit_vector(inputs[0]) << ".transpose();\n";
<< eigen_matrix_format(out_layout->get_shape(), out_layout->get_strides())
<< ").rowwise() =\n"
<< " EigenVector<" << result_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0])
<< ").transpose();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -892,8 +738,6 @@ void Emitter::EmitConvert(const ngraph::Node* n, ...@@ -892,8 +738,6 @@ void Emitter::EmitConvert(const ngraph::Node* n,
auto arg_tensor_type = dynamic_pointer_cast<const TensorViewType>(arg->get_value_type()); auto arg_tensor_type = dynamic_pointer_cast<const TensorViewType>(arg->get_value_type());
assert(arg_tensor_type); assert(arg_tensor_type);
auto& arg_element_type = arg_tensor_type->get_element_type();
auto result_tensor_type = dynamic_pointer_cast<const TensorViewType>(n->get_value_type()); auto result_tensor_type = dynamic_pointer_cast<const TensorViewType>(n->get_value_type());
assert(result_tensor_type); assert(result_tensor_type);
...@@ -901,10 +745,8 @@ void Emitter::EmitConvert(const ngraph::Node* n, ...@@ -901,10 +745,8 @@ void Emitter::EmitConvert(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << result_element_type.c_type_string() << ">(" TU << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << "\n"
<< " EigenArray1d<" << arg_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0]) << ")\n"
<< " .template cast<" << result_element_type.c_type_string() << ">();\n"; << " .template cast<" << result_element_type.c_type_string() << ">();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
...@@ -961,7 +803,8 @@ void Emitter::EmitReshape(const ngraph::Node* n, ...@@ -961,7 +803,8 @@ void Emitter::EmitReshape(const ngraph::Node* n,
result_shape_product *= i; result_shape_product *= i;
} }
// If there is no layout change or we are just going from 1^n to 1^m or a zero-size tensor, we can just copy. // If there is no layout change or we are just going from 1^n to 1^m or a zero-size tensor,
// we can just copy.
if (same_layout || result_shape_product < 2) if (same_layout || result_shape_product < 2)
{ {
TU << "{ // " << n->get_name() << " 1\n"; TU << "{ // " << n->get_name() << " 1\n";
...@@ -1000,13 +843,8 @@ void Emitter::EmitReshape(const ngraph::Node* n, ...@@ -1000,13 +843,8 @@ void Emitter::EmitReshape(const ngraph::Node* n,
{ {
TU << "{ // " << n->get_name() << " 3\n"; TU << "{ // " << n->get_name() << " 3\n";
TU.indent++; TU.indent++;
TU << "EigenMatrix<" << result_element_type.c_type_string() << ">(" TU << "" << emit_matrix(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << " " << emit_matrix(inputs[0]) << ".transpose();\n";
<< eigen_matrix_format(out_layout->get_shape(), out_layout->get_strides()) << ") =\n"
<< " EigenMatrix<" << result_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg0_layout->get_shape(), arg0_layout->get_strides())
<< ").transpose();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1029,19 +867,7 @@ void Emitter::EmitFunctionCall(const ngraph::Node* n, ...@@ -1029,19 +867,7 @@ void Emitter::EmitFunctionCall(const ngraph::Node* n,
TU << "{ // Call " << function->get_name() << "\n"; TU << "{ // Call " << function->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "std::vector<void*> inputs;\n"; generate_call(inputs, outputs, function);
for (const TensorViewInfo& input : inputs)
{
TU << "inputs.push_back(" << input.get_tensor().get_name() << ");\n";
}
TU << "\n";
TU << "std::vector<void*> outputs;\n";
for (const TensorViewInfo& output : outputs)
{
TU << "outputs.push_back(" << output.get_tensor().get_name() << ");\n";
}
TU << "\n";
TU << function->get_name() << "(inputs, outputs);\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1137,24 +963,18 @@ void Emitter::EmitReduce(const ngraph::Node* n, ...@@ -1137,24 +963,18 @@ void Emitter::EmitReduce(const ngraph::Node* n,
TU << "{ // " << n->get_name() << " 3\n"; TU << "{ // " << n->get_name() << " 3\n";
TU.indent++; TU.indent++;
string type = f_result_element_type.c_type_string(); string type = f_result_element_type.c_type_string();
TU << "auto f = [](" << type << " x, " << type << " y) -> " << type << " {\n"; TU << "auto f = [](" << type << " x, " << type << " y) -> " << type << "\n{";
TU.indent++; TU.indent++;
TU << "std::vector<void*> inputs;\n"; TU << "\n";
TU << "inputs.push_back(&x);\n";
TU << "inputs.push_back(&y);\n\n";
TU << type << " result;\n"; TU << type << " result;\n";
TU << "std::vector<void*> outputs;\n"; TU << "std::vector<void*> inputs = {&x, &y};\n";
TU << "outputs.push_back(&result);\n"; TU << "std::vector<void*> outputs = {&result};\n";
TU << reduction_function->get_name() << "(inputs, outputs);\n"; TU << reduction_function->get_name() << "(inputs, outputs);\n";
TU << "return result;\n"; TU << "return result;\n";
TU.indent--; TU.indent--;
TU << "};\n"; TU << "};\n";
TU << "EigenArray1d<" << f_result_element_type.c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_array1d(inputs[0]) << ".redux(f);\n";
<< ") =\n"
<< " EigenArray1d<" << f_result_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0])
<< ").redux(f);\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1165,12 +985,8 @@ void Emitter::EmitReduce(const ngraph::Node* n, ...@@ -1165,12 +985,8 @@ void Emitter::EmitReduce(const ngraph::Node* n,
{ {
TU << "{ // " << n->get_name() << " 4\n"; TU << "{ // " << n->get_name() << " 4\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << f_result_element_type.c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_array1d(inputs[1]) << "(0, 0);\n";
<< ") =\n"
<< " EigenArray1d<" << f_result_element_type.c_type_string() << ">("
<< inputs[1].get_tensor().get_name() << ", " << eigen_vector_format(inputs[1])
<< ")(0, 0);\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1183,25 +999,18 @@ void Emitter::EmitReduce(const ngraph::Node* n, ...@@ -1183,25 +999,18 @@ void Emitter::EmitReduce(const ngraph::Node* n,
TU << "{ // " << n->get_name() << " 5\n"; TU << "{ // " << n->get_name() << " 5\n";
TU.indent++; TU.indent++;
string type = f_result_element_type.c_type_string(); string type = f_result_element_type.c_type_string();
TU << "auto f = [](" << type << " x, " << type << " y) -> " << type << " {\n"; TU << "auto f = [](" << type << " x, " << type << " y) -> " << type << "\n{";
TU.indent++; TU.indent++;
TU << "std::vector<void*> inputs;\n"; TU << "\n";
TU << "inputs.push_back(&x);\n";
TU << "inputs.push_back(&y);\n\n";
TU << type << " result;\n"; TU << type << " result;\n";
TU << "std::vector<void*> outputs;\n"; TU << "std::vector<void*> inputs = {&x, &y};\n";
TU << "outputs.push_back(&result);\n"; TU << "std::vector<void*> outputs = {&result};\n";
TU << reduction_function->get_name() << "(inputs, outputs);\n"; TU << reduction_function->get_name() << "(inputs, outputs);\n";
TU << "return result;\n"; TU << "return result;\n";
TU.indent--; TU.indent--;
TU << "};\n"; TU << "};\n";
TU << "EigenVector<" << f_result_element_type.c_type_string() << ">(" TU << "" << emit_vector(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_matrix(inputs[0]) << ".rowwise().redux(f);\n";
<< ") =\n"
<< " EigenMatrix<" << f_result_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg0_layout->get_shape(), arg0_layout->get_strides())
<< ").rowwise().redux(f);\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1212,12 +1021,8 @@ void Emitter::EmitReduce(const ngraph::Node* n, ...@@ -1212,12 +1021,8 @@ void Emitter::EmitReduce(const ngraph::Node* n,
{ {
TU << "{ // " << n->get_name() << " 6\n"; TU << "{ // " << n->get_name() << " 6\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << f_result_element_type.c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_array1d(inputs[1]) << "(0, 0);\n";
<< ") =\n"
<< " EigenArray1d<" << f_result_element_type.c_type_string() << ">("
<< inputs[1].get_tensor().get_name() << ", " << eigen_vector_format(inputs[1])
<< ")(0, 0);\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1226,25 +1031,18 @@ void Emitter::EmitReduce(const ngraph::Node* n, ...@@ -1226,25 +1031,18 @@ void Emitter::EmitReduce(const ngraph::Node* n,
TU << "{ // " << n->get_name() << " 7\n"; TU << "{ // " << n->get_name() << " 7\n";
TU.indent++; TU.indent++;
string type = f_result_element_type.c_type_string(); string type = f_result_element_type.c_type_string();
TU << "auto f = [](" << type << " x, " << type << " y) -> " << type << " {\n"; TU << "auto f = [](" << type << " x, " << type << " y) -> " << type << "\n{";
TU.indent++; TU.indent++;
TU << "std::vector<void*> inputs;\n"; TU << "\n";
TU << "inputs.push_back(&x);\n";
TU << "inputs.push_back(&y);\n\n";
TU << type << " result;\n"; TU << type << " result;\n";
TU << "std::vector<void*> outputs;\n"; TU << "std::vector<void*> inputs = {&x, &y};\n";
TU << "outputs.push_back(&result);\n"; TU << "std::vector<void*> outputs = {&result};\n";
TU << reduction_function->get_name() << "(inputs, outputs);\n"; TU << reduction_function->get_name() << "(inputs, outputs);\n";
TU << "return result;\n"; TU << "return result;\n";
TU.indent--; TU.indent--;
TU << "};\n"; TU << "};\n";
TU << "EigenVector<" << f_result_element_type.c_type_string() << ">(" TU << "" << emit_vector(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_matrix(inputs[0]) << ".colwise().redux(f);\n";
<< ") =\n"
<< " EigenMatrix<" << f_result_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg0_layout->get_shape(), arg0_layout->get_strides())
<< ").colwise().redux(f);\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1260,16 +1058,10 @@ void Emitter::EmitSign(const ngraph::Node* n, ...@@ -1260,16 +1058,10 @@ void Emitter::EmitSign(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".sign();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").sign();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1294,7 +1086,6 @@ void Emitter::EmitSlice(const ngraph::Node* n, ...@@ -1294,7 +1086,6 @@ void Emitter::EmitSlice(const ngraph::Node* n,
assert(arg_tensor_view_type); assert(arg_tensor_view_type);
auto arg_shape = arg_tensor_view_type->get_shape(); auto arg_shape = arg_tensor_view_type->get_shape();
auto arg_rank = arg_shape.size(); auto arg_rank = arg_shape.size();
auto& arg_element_type = arg_tensor_view_type->get_element_type();
auto& lower_bounds = slice->get_lower_bounds(); auto& lower_bounds = slice->get_lower_bounds();
auto& upper_bounds = slice->get_upper_bounds(); auto& upper_bounds = slice->get_upper_bounds();
...@@ -1316,12 +1107,8 @@ void Emitter::EmitSlice(const ngraph::Node* n, ...@@ -1316,12 +1107,8 @@ void Emitter::EmitSlice(const ngraph::Node* n,
{ {
TU << "{ // " << n->get_name() << " 2\n"; TU << "{ // " << n->get_name() << " 2\n";
TU.indent++; TU.indent++;
TU << "EigenVector<" << arg_element_type.c_type_string() << ">(" TU << "" << emit_vector(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_vector(inputs[0]) << ".segment(\n"
<< ") =\n"
<< " EigenVector<" << arg_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0])
<< ").segment(\n"
<< " " << to_string(lower_bounds[0]) << ", " << " " << to_string(lower_bounds[0]) << ", "
<< to_string(upper_bounds[0] - lower_bounds[0]) << ");\n"; << to_string(upper_bounds[0] - lower_bounds[0]) << ");\n";
TU.indent--; TU.indent--;
...@@ -1334,14 +1121,9 @@ void Emitter::EmitSlice(const ngraph::Node* n, ...@@ -1334,14 +1121,9 @@ void Emitter::EmitSlice(const ngraph::Node* n,
TU << "{ // " << n->get_name() << " 3\n"; TU << "{ // " << n->get_name() << " 3\n";
TU.indent++; TU.indent++;
TU << "EigenMatrix<" << arg_element_type.c_type_string() << ">(" TU << "" << emit_matrix(outputs[0]) << " = \n"
<< outputs[0].get_tensor().get_name() << ", " << " " << emit_matrix(inputs[0]) << ".block(" << to_string(lower_bounds[0])
<< eigen_matrix_format(out_layout->get_shape(), out_layout->get_strides()) << ") = \n" << ", " << to_string(lower_bounds[1]) << ",\n"
<< " EigenMatrix<" << arg_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg0_layout->get_shape(), arg0_layout->get_strides())
<< ").block(" << to_string(lower_bounds[0]) << ", " << to_string(lower_bounds[1])
<< ",\n"
<< " " << to_string(upper_bounds[0] - lower_bounds[0]) << ",\n" << " " << to_string(upper_bounds[0] - lower_bounds[0]) << ",\n"
<< " " << to_string(upper_bounds[1] - lower_bounds[1]) << ");\n"; << " " << to_string(upper_bounds[1] - lower_bounds[1]) << ");\n";
TU.indent--; TU.indent--;
...@@ -1362,7 +1144,6 @@ void Emitter::EmitSum(const ngraph::Node* n, ...@@ -1362,7 +1144,6 @@ void Emitter::EmitSum(const ngraph::Node* n,
auto s = static_cast<const op::Sum*>(n); auto s = static_cast<const op::Sum*>(n);
auto s_tensor_view_type = dynamic_pointer_cast<const TensorViewType>(s->get_value_type()); auto s_tensor_view_type = dynamic_pointer_cast<const TensorViewType>(s->get_value_type());
assert(s_tensor_view_type); assert(s_tensor_view_type);
auto& s_element_type = s_tensor_view_type->get_element_type();
auto s_shape = s_tensor_view_type->get_shape(); auto s_shape = s_tensor_view_type->get_shape();
auto arg = s->get_arguments().at(0); auto arg = s->get_arguments().at(0);
...@@ -1393,12 +1174,8 @@ void Emitter::EmitSum(const ngraph::Node* n, ...@@ -1393,12 +1174,8 @@ void Emitter::EmitSum(const ngraph::Node* n,
{ {
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << s_element_type.c_type_string() << ">(" TU << "" << emit_array1d(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_array1d(inputs[0]) << ".sum();\n";
<< ") =\n"
<< " EigenArray1d<" << s_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", " << eigen_vector_format(inputs[0])
<< ").sum();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1408,13 +1185,8 @@ void Emitter::EmitSum(const ngraph::Node* n, ...@@ -1408,13 +1185,8 @@ void Emitter::EmitSum(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenVector<" << s_element_type.c_type_string() << ">(" TU << "" << emit_vector(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_matrix(inputs[0]) << ".rowwise().sum();\n";
<< ") =\n"
<< " EigenMatrix<" << s_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg0_layout->get_shape(), arg0_layout->get_strides())
<< ").rowwise().sum();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1424,13 +1196,8 @@ void Emitter::EmitSum(const ngraph::Node* n, ...@@ -1424,13 +1196,8 @@ void Emitter::EmitSum(const ngraph::Node* n,
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenVector<" << s_element_type.c_type_string() << ">(" TU << "" << emit_vector(outputs[0]) << " =\n"
<< outputs[0].get_tensor().get_name() << ", " << eigen_vector_format(outputs[0]) << " " << emit_matrix(inputs[0]) << ".colwise().sum();\n";
<< ") =\n"
<< " EigenMatrix<" << s_element_type.c_type_string() << ">("
<< inputs[0].get_tensor().get_name() << ", "
<< eigen_matrix_format(arg0_layout->get_shape(), arg0_layout->get_strides())
<< ").colwise().sum();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1445,16 +1212,10 @@ void Emitter::EmitExp(const ngraph::Node* n, ...@@ -1445,16 +1212,10 @@ void Emitter::EmitExp(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".exp();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").exp();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1464,16 +1225,10 @@ void Emitter::EmitSin(const ngraph::Node* n, ...@@ -1464,16 +1225,10 @@ void Emitter::EmitSin(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".sin();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").sin();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1483,16 +1238,10 @@ void Emitter::EmitSinh(const ngraph::Node* n, ...@@ -1483,16 +1238,10 @@ void Emitter::EmitSinh(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".sinh();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").sinh();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1502,16 +1251,10 @@ void Emitter::EmitCos(const ngraph::Node* n, ...@@ -1502,16 +1251,10 @@ void Emitter::EmitCos(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".cos();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").cos();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1521,16 +1264,10 @@ void Emitter::EmitCosh(const ngraph::Node* n, ...@@ -1521,16 +1264,10 @@ void Emitter::EmitCosh(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".cosh();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").cosh();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1540,16 +1277,10 @@ void Emitter::EmitTan(const ngraph::Node* n, ...@@ -1540,16 +1277,10 @@ void Emitter::EmitTan(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".tan();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").tan();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1579,16 +1310,10 @@ void Emitter::EmitAsin(const ngraph::Node* n, ...@@ -1579,16 +1310,10 @@ void Emitter::EmitAsin(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".asin();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").asin();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1598,16 +1323,10 @@ void Emitter::EmitAcos(const ngraph::Node* n, ...@@ -1598,16 +1323,10 @@ void Emitter::EmitAcos(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".acos();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").acos();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
...@@ -1617,16 +1336,89 @@ void Emitter::EmitAtan(const ngraph::Node* n, ...@@ -1617,16 +1336,89 @@ void Emitter::EmitAtan(const ngraph::Node* n,
const std::vector<TensorViewInfo>& inputs, const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs) const std::vector<TensorViewInfo>& outputs)
{ {
const element::Type& et =
(dynamic_pointer_cast<const TensorViewType>(n->get_arguments().at(0)->get_value_type()))
->get_element_type();
TU << "{ // " << n->get_name() << "\n"; TU << "{ // " << n->get_name() << "\n";
TU.indent++; TU.indent++;
TU << "EigenArray1d<" << et.c_type_string() << ">(" << outputs[0].get_tensor().get_name() TU << emit_array1d(outputs[0]) << " =\n"
<< ", " << eigen_vector_format(outputs[0]) << ") =\n" << " " << emit_array1d(inputs[0]) << ".atan();\n";
<< " EigenArray1d<" << et.c_type_string() << ">(" << inputs[0].get_tensor().get_name()
<< ", " << eigen_vector_format(inputs[0]) << ").atan();\n";
TU.indent--; TU.indent--;
TU << "}\n"; TU << "}\n";
} }
//------------------------------------------------------------------------------------------------
// Utility methods
//------------------------------------------------------------------------------------------------
void Emitter::generate_call(const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs,
shared_ptr<Function> function)
{
vector<string> input_names;
vector<string> output_names;
for (const TensorViewInfo& input : inputs)
{
input_names.push_back(input.get_tensor().get_name());
}
for (const TensorViewInfo& output : outputs)
{
output_names.push_back(output.get_tensor().get_name());
}
TU << "std::vector<void*> inputs =\n{";
TU.indent++;
TU << "\n" << join(input_names, ",\n");
TU.indent--;
TU << "\n};\n";
TU << "std::vector<void*> outputs =\n{";
TU.indent++;
TU << "\n" << join(output_names, ",\n");
TU.indent--;
TU << "\n};\n";
TU << "\n";
TU << function->get_name() << "(inputs, outputs);\n";
}
static string format_name(const string& name)
{
string rc;
if (name.empty())
{
rc = " " + name;
}
return rc;
}
string Emitter::emit_vector(const TensorViewInfo& tvi, const string& name)
{
stringstream ss;
const element::Type& et = tvi.get_tensor_view()->get_value_type()->get_element_type();
ss << "EigenVector<" << et.c_type_string() << ">" << format_name(name) << "("
<< tvi.get_tensor().get_name() << ", " << eigen_vector_format(tvi) << ")";
return ss.str();
}
string Emitter::emit_array1d(const TensorViewInfo& tvi, const string& name)
{
stringstream ss;
const element::Type& et = tvi.get_tensor_view()->get_value_type()->get_element_type();
ss << "EigenArray1d<" << et.c_type_string() << ">" << format_name(name) << "("
<< tvi.get_tensor().get_name() << ", " << eigen_vector_format(tvi) << ")";
return ss.str();
}
string Emitter::emit_matrix(const TensorViewInfo& tvi, const string& name)
{
stringstream ss;
auto layout = tvi.get_layout<DenseTensorViewLayout>();
const element::Type& et = tvi.get_tensor_view()->get_value_type()->get_element_type();
ss << "EigenMatrix<" << et.c_type_string() << ">" << format_name(name) << "("
<< tvi.get_tensor().get_name() << ", "
<< eigen_matrix_format(layout->get_shape(), layout->get_strides()) << ")";
return ss.str();
}
...@@ -94,6 +94,15 @@ namespace ngraph ...@@ -94,6 +94,15 @@ namespace ngraph
void EMITTER_DECL(EmitAsin); void EMITTER_DECL(EmitAsin);
void EMITTER_DECL(EmitAcos); void EMITTER_DECL(EmitAcos);
void EMITTER_DECL(EmitAtan); void EMITTER_DECL(EmitAtan);
private:
void generate_call(const std::vector<TensorViewInfo>& inputs,
const std::vector<TensorViewInfo>& outputs,
std::shared_ptr<Function> function);
std::string emit_vector(const TensorViewInfo&, const std::string& name = "");
std::string emit_array1d(const TensorViewInfo&, const std::string& name = "");
std::string emit_matrix(const TensorViewInfo&, const std::string& name = "");
}; };
} }
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "ngraph/codegen/code_writer.hpp" #include "ngraph/codegen/code_writer.hpp"
#include "ngraph/codegen/compiler.hpp" #include "ngraph/codegen/compiler.hpp"
#include "ngraph/codegen/execution_engine.hpp"
#include "ngraph/descriptor/input.hpp" #include "ngraph/descriptor/input.hpp"
#include "ngraph/descriptor/layout/dense_tensor_view_layout.hpp" #include "ngraph/descriptor/layout/dense_tensor_view_layout.hpp"
#include "ngraph/descriptor/output.hpp" #include "ngraph/descriptor/output.hpp"
...@@ -74,6 +75,7 @@ ...@@ -74,6 +75,7 @@
#include "ngraph/pass/memory_layout.hpp" #include "ngraph/pass/memory_layout.hpp"
#include "ngraph/pass/topological_sort.hpp" #include "ngraph/pass/topological_sort.hpp"
#include "ngraph/runtime/cpu/call_frame.hpp" #include "ngraph/runtime/cpu/call_frame.hpp"
#include "ngraph/runtime/cpu/cpu_backend.hpp"
#include "ngraph/runtime/cpu/emitter.hpp" #include "ngraph/runtime/cpu/emitter.hpp"
#include "ngraph/runtime/cpu/external_function.hpp" #include "ngraph/runtime/cpu/external_function.hpp"
#include "ngraph/runtime/utils.hpp" #include "ngraph/runtime/utils.hpp"
...@@ -81,26 +83,17 @@ ...@@ -81,26 +83,17 @@
using namespace std; using namespace std;
using namespace ngraph::runtime::cpu; using namespace ngraph::runtime::cpu;
using ngraph::descriptor::layout::DenseTensorViewLayout; static const std::string s_output_dir = "cpu_codegen";
extern "C" void class StaticInitializers
allocate_aligned_buffer(size_t size, size_t alignment, char** allocated, char** aligned_ptr)
{ {
size_t allocation_size = size + alignment; public:
*allocated = static_cast<char*>(malloc(allocation_size)); StaticInitializers() { ngraph::file_util::remove_directory(s_output_dir); }
*aligned_ptr = *allocated; };
size_t mod = size_t(*aligned_ptr) % alignment;
if (mod != 0) static StaticInitializers s_static_initializers;
{
(*aligned_ptr) += (alignment - mod);
}
}
extern "C" void free_aligned_buffer(void* allocated) using ngraph::descriptor::layout::DenseTensorViewLayout;
{
free(allocated);
}
#define TI(x) type_index(typeid(x)) #define TI(x) type_index(typeid(x))
...@@ -192,38 +185,26 @@ void ExternalFunction::compile() ...@@ -192,38 +185,26 @@ void ExternalFunction::compile()
TU += TU +=
R"(// Generated by the NGraph CPU backend R"(// Generated by the NGraph CPU backend
#include <algorithm>
#include <cmath> #include <cmath>
#include <memory>
#include <vector> #include <vector>
#include <Eigen/Dense> #include <Eigen/Dense>
#include "ngraph/runtime/cpu/cpu_kernels.hpp" #include "ngraph/runtime/cpu/cpu_kernels.hpp"
#include "ngraph/runtime/cpu/eigen_utils.hpp" #include "ngraph/runtime/cpu/eigen_utils.hpp"
#include "ngraph/runtime/cpu/memory_handler.hpp"
using namespace ngraph::runtime::cpu::eigen; using namespace ngraph::runtime::cpu::eigen;
extern "C" void allocate_aligned_buffer(
size_t size,
size_t alignment,
char** allocated,
char** aligned_ptr);
extern "C" void free_aligned_buffer(void* allocated);
)"; )";
TU << "// Declare any functions that are not main\n"; TU << "// Declare all functions\n";
for (shared_ptr<Function> f : pass_manager.get_state().get_functions()) for (shared_ptr<Function> f : pass_manager.get_state().get_functions())
{
if (f != m_function)
{ {
TU << "extern \"C\" void " << f->get_name() << "(\n"; TU << "extern \"C\" void " << f->get_name() << "(\n";
TU << " const std::vector<void*>& inputs,\n"; TU << " const std::vector<void*>& inputs,\n";
TU << " const std::vector<void*>& outputs);\n"; TU << " const std::vector<void*>& outputs);\n";
} }
}
TU << "\n"; TU << "\n";
for (shared_ptr<Function> current_function : pass_manager.get_state().get_functions()) for (shared_ptr<Function> current_function : pass_manager.get_state().get_functions())
...@@ -232,14 +213,23 @@ extern "C" void free_aligned_buffer(void* allocated); ...@@ -232,14 +213,23 @@ extern "C" void free_aligned_buffer(void* allocated);
TU << " const std::vector<void*>& inputs,\n"; TU << " const std::vector<void*>& inputs,\n";
TU << " const std::vector<void*>& outputs)\n"; TU << " const std::vector<void*>& outputs)\n";
TU << "{\n"; TU << "{\n";
TU.indent++; TU.indent++;
TU << "// Allocate the memory pool\n";
bool temporaries_used = false;
for (shared_ptr<Node> node : current_function->get_ordered_ops())
{
if (node->liveness_new_list.size() > 0)
{
temporaries_used = true;
break;
}
}
if (temporaries_used)
{
size_t temp_pool_size = pass_manager.get_state().get_temporary_pool_size(); size_t temp_pool_size = pass_manager.get_state().get_temporary_pool_size();
TU << "char* allocated_buffer_pool;\n"; TU << "// Allocate the memory pool\n";
TU << "char* aligned_buffer_pool;\n"; TU << "ngraph::runtime::cpu::MemoryHandler memory_handler(" << temp_pool_size << ", "
TU << "allocate_aligned_buffer(" << temp_pool_size << ", 64" << ngraph::runtime::cpu::alignment << ");\n";
<< ", &allocated_buffer_pool, &aligned_buffer_pool);\n";
TU << "\n"; TU << "\n";
TU << "// Define temporary tensors\n"; TU << "// Define temporary tensors\n";
...@@ -248,11 +238,12 @@ extern "C" void free_aligned_buffer(void* allocated); ...@@ -248,11 +238,12 @@ extern "C" void free_aligned_buffer(void* allocated);
for (descriptor::Tensor* tensor : node->liveness_new_list) for (descriptor::Tensor* tensor : node->liveness_new_list)
{ {
TU << tensor->get_element_type() << "* " << tensor->get_name() << " = (" TU << tensor->get_element_type() << "* " << tensor->get_name() << " = ("
<< tensor->get_element_type() << "*)(aligned_buffer_pool + " << tensor->get_element_type() << "*)(memory_handler.get_ptr("
<< tensor->get_pool_offset() << ");\n"; << tensor->get_pool_offset() << "));\n";
} }
} }
TU << "\n"; TU << "\n";
}
TU << "// Define inputs\n"; TU << "// Define inputs\n";
size_t arg_index = 0; size_t arg_index = 0;
...@@ -283,9 +274,6 @@ extern "C" void free_aligned_buffer(void* allocated); ...@@ -283,9 +274,6 @@ extern "C" void free_aligned_buffer(void* allocated);
} }
TU << "\n"; TU << "\n";
TU << "// Define tensor views\n";
TU << "\n";
for (shared_ptr<Node> node : current_function->get_ordered_ops()) for (shared_ptr<Node> node : current_function->get_ordered_ops())
{ {
auto& n = *node; // Work around a compiler warning (*node inside typeid may have effects auto& n = *node; // Work around a compiler warning (*node inside typeid may have effects
...@@ -311,8 +299,6 @@ extern "C" void free_aligned_buffer(void* allocated); ...@@ -311,8 +299,6 @@ extern "C" void free_aligned_buffer(void* allocated);
handler->second(&emitter, node.get(), this, in, out); handler->second(&emitter, node.get(), this, in, out);
} }
TU << "\nfree_aligned_buffer(allocated_buffer_pool);\n";
TU.indent--; TU.indent--;
// End TU // End TU
...@@ -321,34 +307,33 @@ extern "C" void free_aligned_buffer(void* allocated); ...@@ -321,34 +307,33 @@ extern "C" void free_aligned_buffer(void* allocated);
// TODO: Cleanup and make this a utility function // TODO: Cleanup and make this a utility function
string output_dir = "cpu_codegen";
string function_name = m_function->get_name(); string function_name = m_function->get_name();
file_util::remove_directory(output_dir); file_util::make_directory(s_output_dir);
file_util::make_directory(output_dir); string filename = file_util::path_join(s_output_dir, function_name + "_codegen.cpp");
string filename = file_util::path_join(output_dir, function_name + "_codegen.cpp");
ofstream out(filename); ofstream out(filename);
string code = TU.get_code(); string code = TU.get_code();
out << code; out << code;
out.close(); out.close();
ngraph::codegen::execution_state estate; codegen::Compiler compiler;
codegen::ExecutionEngine execution_engine;
#if NGCPU_PCH #if NGCPU_PCH
estate.set_precompiled_headers_enabled(true); compiler.set_precompiled_headers_enabled(true);
#endif #endif
#if NGCPU_DEBUGINFO #if NGCPU_DEBUGINFO
estate.set_debuginfo_enabled(true); compiler.set_debuginfo_enabled(true);
#endif #endif
auto llvm_module = estate.compile(code, function_name + "_codegen.cpp"); auto llvm_module = compiler.compile(code);
if (llvm_module == nullptr) if (llvm_module == nullptr)
{ {
throw runtime_error("function failed to compile"); throw runtime_error("function failed to compile");
} }
estate.add_module(llvm_module); execution_engine.add_module(llvm_module);
estate.finalize(); execution_engine.finalize();
m_compiled_function = estate.find_function<EntryPoint_t>(function_name); m_compiled_function = execution_engine.find_function<EntryPoint_t>(function_name);
assert(m_compiled_function); assert(m_compiled_function);
m_is_compiled = true; m_is_compiled = true;
......
// ----------------------------------------------------------------------------
// Copyright 2017 Nervana Systems Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#include "ngraph/runtime/cpu/memory_handler.hpp"
using namespace ngraph;
runtime::cpu::MemoryHandler::MemoryHandler(size_t byte_size, size_t alignment)
: m_allocated_buffer_pool(nullptr)
, m_aligned_buffer_pool(nullptr)
{
if (byte_size > 0)
{
size_t allocation_size = byte_size + alignment;
m_allocated_buffer_pool = static_cast<char*>(malloc(allocation_size));
m_aligned_buffer_pool = m_allocated_buffer_pool;
size_t mod = size_t(m_aligned_buffer_pool) % alignment;
if (mod != 0)
{
m_aligned_buffer_pool += (alignment - mod);
}
}
}
runtime::cpu::MemoryHandler::~MemoryHandler()
{
if (m_allocated_buffer_pool != nullptr)
{
free(m_allocated_buffer_pool);
}
}
// ----------------------------------------------------------------------------
// Copyright 2017 Nervana Systems Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// ----------------------------------------------------------------------------
#pragma once
#include <cstddef>
#include <memory>
namespace ngraph
{
namespace runtime
{
namespace cpu
{
class MemoryHandler;
}
}
}
class ngraph::runtime::cpu::MemoryHandler
{
public:
MemoryHandler(size_t pool_size, size_t alignment);
~MemoryHandler();
void* get_ptr(size_t offset) const { return m_aligned_buffer_pool + offset; }
private:
char* m_allocated_buffer_pool;
char* m_aligned_buffer_pool;
};
...@@ -20,35 +20,47 @@ ...@@ -20,35 +20,47 @@
using namespace ngraph; using namespace ngraph;
using namespace std; using namespace std;
extern "C" void
allocate_aligned_buffer(size_t size, size_t alignment, char** allocated, char** aligned_ptr);
extern "C" void free_aligned_buffer(void* allocated);
runtime::cpu::CPUTensorView::CPUTensorView(const ngraph::element::Type& element_type, runtime::cpu::CPUTensorView::CPUTensorView(const ngraph::element::Type& element_type,
const Shape& shape) const Shape& shape)
: runtime::TensorView(std::make_shared<ngraph::descriptor::PrimaryTensorView>( : runtime::TensorView(std::make_shared<ngraph::descriptor::PrimaryTensorView>(
std::make_shared<ngraph::TensorViewType>(element_type, shape), "external", true, true)) std::make_shared<ngraph::TensorViewType>(element_type, shape), "external", true, true))
, m_allocated_buffer_pool(nullptr)
, m_aligned_buffer_pool(nullptr)
{ {
m_descriptor->set_tensor_view_layout( m_descriptor->set_tensor_view_layout(
std::make_shared<ngraph::descriptor::layout::DenseTensorViewLayout>(*m_descriptor)); std::make_shared<ngraph::descriptor::layout::DenseTensorViewLayout>(*m_descriptor));
m_buffer_size = m_descriptor->get_tensor_view_layout()->get_size() * element_type.size(); m_buffer_size = m_descriptor->get_tensor_view_layout()->get_size() * element_type.size();
allocate_aligned_buffer(m_buffer_size, runtime::cpu::alignment, &m_allocated, &m_buffer); if (m_buffer_size > 0)
{
size_t allocation_size = m_buffer_size + runtime::cpu::alignment;
m_allocated_buffer_pool = static_cast<char*>(malloc(allocation_size));
m_aligned_buffer_pool = m_allocated_buffer_pool;
size_t mod = size_t(m_aligned_buffer_pool) % alignment;
if (mod != 0)
{
m_aligned_buffer_pool += (alignment - mod);
}
}
} }
runtime::cpu::CPUTensorView::~CPUTensorView() runtime::cpu::CPUTensorView::~CPUTensorView()
{ {
free_aligned_buffer(m_allocated); if (m_allocated_buffer_pool != nullptr)
{
free(m_allocated_buffer_pool);
}
} }
char* runtime::cpu::CPUTensorView::get_data_ptr() char* runtime::cpu::CPUTensorView::get_data_ptr()
{ {
return m_buffer; return m_aligned_buffer_pool;
} }
const char* runtime::cpu::CPUTensorView::get_data_ptr() const const char* runtime::cpu::CPUTensorView::get_data_ptr() const
{ {
return m_buffer; return m_aligned_buffer_pool;
} }
void runtime::cpu::CPUTensorView::write(const void* source, size_t tensor_offset, size_t n) void runtime::cpu::CPUTensorView::write(const void* source, size_t tensor_offset, size_t n)
......
...@@ -52,7 +52,7 @@ public: ...@@ -52,7 +52,7 @@ public:
void read(void* p, size_t tensor_offset, size_t n) const override; void read(void* p, size_t tensor_offset, size_t n) const override;
private: private:
char* m_allocated; char* m_allocated_buffer_pool;
char* m_buffer; char* m_aligned_buffer_pool;
size_t m_buffer_size; size_t m_buffer_size;
}; };
...@@ -19,23 +19,26 @@ ...@@ -19,23 +19,26 @@
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "ngraph/codegen/compiler.hpp" #include "ngraph/codegen/compiler.hpp"
#include "ngraph/codegen/execution_engine.hpp"
using namespace std; using namespace std;
using namespace ngraph;
TEST(codegen, simple_return) TEST(codegen, simple_return)
{ {
constexpr auto name = "test.cpp";
constexpr auto source = R"(extern "C" int test() { return 2+5; })"; constexpr auto source = R"(extern "C" int test() { return 2+5; })";
ngraph::codegen::execution_state estate; codegen::Compiler compiler;
auto module = estate.compile(source, name); codegen::ExecutionEngine execution_engine;
auto module = compiler.compile(source);
ASSERT_NE(nullptr, module); ASSERT_NE(nullptr, module);
estate.add_module(module); execution_engine.add_module(module);
estate.finalize(); execution_engine.finalize();
auto func = estate.find_function<int()>("test"); auto func = execution_engine.find_function<int()>("test");
ASSERT_NE(nullptr, func); ASSERT_NE(nullptr, func);
int result = func(); int result = func();
...@@ -44,18 +47,19 @@ TEST(codegen, simple_return) ...@@ -44,18 +47,19 @@ TEST(codegen, simple_return)
TEST(codegen, pass_args) TEST(codegen, pass_args)
{ {
constexpr auto name = "test.cpp";
constexpr auto source = R"(extern "C" int test(int a, int b) { return a+b; })"; constexpr auto source = R"(extern "C" int test(int a, int b) { return a+b; })";
ngraph::codegen::execution_state estate; codegen::Compiler compiler;
auto module = estate.compile(source, name); codegen::ExecutionEngine execution_engine;
auto module = compiler.compile(source);
ASSERT_NE(nullptr, module); ASSERT_NE(nullptr, module);
estate.add_module(module); execution_engine.add_module(module);
estate.finalize(); execution_engine.finalize();
auto func = estate.find_function<int(int, int)>("test"); auto func = execution_engine.find_function<int(int, int)>("test");
ASSERT_NE(nullptr, func); ASSERT_NE(nullptr, func);
int result = func(20, 22); int result = func(20, 22);
...@@ -64,7 +68,6 @@ TEST(codegen, pass_args) ...@@ -64,7 +68,6 @@ TEST(codegen, pass_args)
TEST(codegen, include) TEST(codegen, include)
{ {
constexpr auto name = "test.cpp";
constexpr auto source = constexpr auto source =
R"( R"(
#include <cmath> #include <cmath>
...@@ -74,15 +77,17 @@ TEST(codegen, include) ...@@ -74,15 +77,17 @@ TEST(codegen, include)
} }
)"; )";
ngraph::codegen::execution_state estate; codegen::Compiler compiler;
auto module = estate.compile(source, name); codegen::ExecutionEngine execution_engine;
auto module = compiler.compile(source);
ASSERT_NE(nullptr, module); ASSERT_NE(nullptr, module);
estate.add_module(module); execution_engine.add_module(module);
estate.finalize(); execution_engine.finalize();
auto func = estate.find_function<int(int, int)>("test"); auto func = execution_engine.find_function<int(int, int)>("test");
ASSERT_NE(nullptr, func); ASSERT_NE(nullptr, func);
int result = func(20, 2); int result = func(20, 2);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment