Commit 6961353d authored by Robert Kimball's avatar Robert Kimball

tests pass

parent 6d43129f
......@@ -106,6 +106,7 @@ if (NGRAPH_CPU_ENABLE AND LLVM_INCLUDE_DIR AND
set(SRC ${SRC}
codegen/code_writer.cpp
codegen/compiler.cpp
codegen/execution_engine.cpp
runtime/cpu/call_frame.cpp
runtime/cpu/cpu_backend.cpp
runtime/cpu/cpu_manager.cpp
......
......@@ -45,9 +45,6 @@
#include <clang/Frontend/TextDiagnosticPrinter.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include "ngraph/codegen/compiler.hpp"
#include "ngraph/file_util.hpp"
#include "ngraph/log.hpp"
......@@ -74,82 +71,10 @@ static std::string GetExecutablePath(const char* Argv0)
return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
}
execution_state::execution_state()
: m_execution_engine{nullptr}
, precompiled_headers_enabled(false)
, debuginfo_enabled(false)
{
}
execution_state::~execution_state()
{
}
bool execution_state::is_version_number(const string& path)
{
bool rc = true;
vector<string> tokens = ngraph::split(path, '.');
for (string s : tokens)
{
for (char c : s)
{
if (!isdigit(c))
{
rc = false;
}
}
}
return rc;
}
void execution_state::add_header_search_path(HeaderSearchOptions& hso, const string& path)
{
static vector<string> valid_ext = {".h", ".hpp", ".tcc", ""};
#ifdef USE_CACHE
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)
{
string ext = file_util::get_file_ext(file);
if (contains(valid_ext, ext))
{
// This is a header file
string relative_name = file.substr(path.size() + 1);
string mapped_name = file_util::path_join(mapped_path, relative_name);
ErrorOr<unique_ptr<MemoryBuffer>> code = MemoryBuffer::getFile(file);
if (error_code ec = code.getError())
{
// throw up
}
s_header_cache.add_file(mapped_name, code.get());
}
}
};
file_util::iterate_files(path, func, true);
#else
hso.AddPath(path, clang::frontend::System, false, false);
#endif
}
void execution_state::use_cached_files(std::unique_ptr<CompilerInstance>& Clang)
{
HeaderSearchOptions& hso = Clang->getInvocation().getHeaderSearchOpts();
for (const string& path : s_header_cache.get_include_paths())
{
hso.AddPath(path, clang::frontend::System, false, false);
}
for (auto& header : s_header_cache.get_header_map())
{
Clang->getPreprocessorOpts().addRemappedFile(header.first, header.second.get());
}
}
std::unique_ptr<llvm::Module> execution_state::compile(const string& source, const string& name)
Compiler::Compiler()
: m_precompiled_headers_enabled(false)
, m_debuginfo_enabled(false)
, m_source_name("code.cpp")
{
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
......@@ -158,7 +83,7 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
// Prepare compilation arguments
vector<const char*> args;
args.push_back(name.c_str());
args.push_back(m_source_name.c_str());
// Prepare DiagnosticEngine
DiagnosticOptions DiagOpts;
......@@ -168,23 +93,23 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
new DiagnosticsEngine(pDiagIDs, &DiagOpts, textDiagPrinter);
// Create and initialize CompilerInstance
std::unique_ptr<CompilerInstance> Clang(new CompilerInstance());
Clang->createDiagnostics();
m_compiler = std::unique_ptr<CompilerInstance>(new CompilerInstance());
m_compiler->createDiagnostics();
// Initialize CompilerInvocation
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.
if (Clang->getHeaderSearchOpts().UseBuiltinIncludes &&
Clang->getHeaderSearchOpts().ResourceDir.empty())
if (m_compiler->getHeaderSearchOpts().UseBuiltinIncludes &&
m_compiler->getHeaderSearchOpts().ResourceDir.empty())
{
void* MainAddr = reinterpret_cast<void*>(GetExecutablePath);
auto path = CompilerInvocation::GetResourcesPath(args[0], MainAddr);
Clang->getHeaderSearchOpts().ResourceDir = path;
m_compiler->getHeaderSearchOpts().ResourceDir = path;
}
HeaderSearchOptions& hso = Clang->getInvocation().getHeaderSearchOpts();
HeaderSearchOptions& hso = m_compiler->getInvocation().getHeaderSearchOpts();
if (s_header_cache.is_valid() == false)
{
// Add base toolchain-supplied header paths
......@@ -232,13 +157,13 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
}
#ifdef USE_CACHE
use_cached_files(Clang);
use_cached_files(m_compiler);
#endif
// Language options
// These are the C++ features needed to compile ngraph headers
// and any dependencies like Eigen
auto LO = Clang->getInvocation().getLangOpts();
auto LO = m_compiler->getInvocation().getLangOpts();
LO->CPlusPlus = 1;
LO->CPlusPlus11 = 1;
LO->Bool = 1;
......@@ -251,7 +176,7 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
LO->OpenMPUseTLS = 1;
// CodeGen options
auto& CGO = Clang->getInvocation().getCodeGenOpts();
auto& CGO = m_compiler->getInvocation().getCodeGenOpts();
CGO.OptimizationLevel = 3;
CGO.RelocationModel = "static";
CGO.ThreadModel = "posix";
......@@ -261,22 +186,22 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
CGO.VectorizeSLP = 1;
CGO.CXAAtExit = 0;
if (debuginfo_enabled)
if (m_debuginfo_enabled)
{
CGO.setDebugInfo(codegenoptions::FullDebugInfo);
}
if (precompiled_headers_enabled)
if (m_precompiled_headers_enabled)
{
// Preprocessor options
auto& PPO = Clang->getInvocation().getPreprocessorOpts();
auto& PPO = m_compiler->getInvocation().getPreprocessorOpts();
PPO.ImplicitPCHInclude = "ngcpu.pch";
PPO.DisablePCHValidation = 1;
}
// Enable various target features
// 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
TO.CPU = "broadwell";
TO.FeaturesAsWritten.emplace_back("+sse");
......@@ -288,62 +213,103 @@ std::unique_ptr<llvm::Module> execution_state::compile(const string& source, con
TO.FeaturesAsWritten.emplace_back("+avx");
TO.FeaturesAsWritten.emplace_back("+avx2");
TO.FeaturesAsWritten.emplace_back("+fma");
}
// Map code filename to a memoryBuffer
StringRef source_ref(source);
unique_ptr<MemoryBuffer> buffer = MemoryBuffer::getMemBufferCopy(source_ref);
Clang->getInvocation().getPreprocessorOpts().addRemappedFile(name, buffer.get());
Compiler::~Compiler()
{
}
// Create and execute action
CodeGenAction* compilerAction = new EmitCodeGenOnlyAction();
std::unique_ptr<llvm::Module> rc;
if (Clang->ExecuteAction(*compilerAction) == true)
bool Compiler::is_version_number(const string& path)
{
bool rc = 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;
}
bool execution_state::add_module(std::unique_ptr<llvm::Module>& module)
void Compiler::add_header_search_path(HeaderSearchOptions& hso, const string& path)
{
if (module)
{
if (!m_execution_engine)
#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)
{
m_execution_engine = llvm::EngineBuilder(move(module))
.setEngineKind(llvm::EngineKind::JIT)
.setOptLevel(llvm::CodeGenOpt::Aggressive)
.setErrorStr(&jit_error)
.create();
if (!m_execution_engine)
string ext = file_util::get_file_ext(file);
if (contains(valid_ext, ext))
{
return false;
// This is a header file
string relative_name = file.substr(path.size() + 1);
string mapped_name = file_util::path_join(mapped_path, relative_name);
ErrorOr<unique_ptr<MemoryBuffer>> code = MemoryBuffer::getFile(file);
if (error_code ec = code.getError())
{
// throw up
}
s_header_cache.add_file(mapped_name, code.get());
}
}
};
file_util::iterate_files(path, func, true);
#else
hso.AddPath(path, clang::frontend::System, false, false);
#endif
}
void Compiler::use_cached_files(std::unique_ptr<CompilerInstance>& ci)
{
HeaderSearchOptions& hso = ci->getInvocation().getHeaderSearchOpts();
for (const string& path : s_header_cache.get_include_paths())
{
hso.AddPath(path, clang::frontend::System, false, false);
}
else
for (auto& header : s_header_cache.get_header_map())
{
return false;
ci->getPreprocessorOpts().addRemappedFile(header.first, header.second.get());
}
return true;
}
void execution_state::finalize()
std::unique_ptr<llvm::Module> Compiler::compile(const string& source)
{
if (m_execution_engine)
// 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)
{
m_execution_engine->finalizeObject();
m_execution_engine->runStaticConstructorsDestructors(false);
rc = compilerAction->takeModule();
}
else
buffer.release();
for (const auto& file : m_compiler->getInvocation().getPreprocessorOpts().RemappedFileBuffers)
{
NGRAPH_INFO << file.first;
}
m_compiler->getInvocation().getPreprocessorOpts().clearRemappedFiles();
for (const auto& file : m_compiler->getInvocation().getPreprocessorOpts().RemappedFileBuffers)
{
throw std::runtime_error(
"Error in finalize: " +
(jit_error.empty() ? "Could not create an execution engine" : jit_error));
NGRAPH_INFO << file.first;
}
return rc;
}
......@@ -27,7 +27,7 @@ namespace ngraph
namespace codegen
{
class module;
class execution_state;
class Compiler;
class HeaderCache;
}
}
......@@ -45,41 +45,24 @@ private:
std::unique_ptr<llvm::Module> m_module;
};
class ngraph::codegen::execution_state : public llvm::SectionMemoryManager
class ngraph::codegen::Compiler : public llvm::SectionMemoryManager
{
public:
execution_state();
~execution_state();
Compiler();
~Compiler();
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();
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);
}
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; }
std::unique_ptr<llvm::Module> compile(const std::string& source);
private:
llvm::ExecutionEngine* m_execution_engine;
std::string jit_error;
bool precompiled_headers_enabled;
bool debuginfo_enabled;
std::unique_ptr<clang::CompilerInstance> m_compiler;
bool m_precompiled_headers_enabled;
bool m_debuginfo_enabled;
std::string m_source_name;
template <typename signature>
std::function<signature> f_cast(void* f)
{
return static_cast<signature*>(reinterpret_cast<signature*>(f));
}
bool is_version_number(const std::string& path);
void add_header_search_path(clang::HeaderSearchOptions& hso, const std::string& path);
void use_cached_files(std::unique_ptr<clang::CompilerInstance>& Clang);
......
// ----------------------------------------------------------------------------
// 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));
}
};
......@@ -16,7 +16,7 @@
#include <memory>
#include "ngraph/codegen/compiler.hpp"
#include "ngraph/codegen/execution_engine.hpp"
#include "ngraph/runtime/manager.hpp"
namespace ngraph
......@@ -33,7 +33,7 @@ namespace ngraph
class CPUManager : public Manager
{
protected:
ngraph::codegen::execution_state exec_state;
ngraph::codegen::ExecutionEngine exec_state;
public:
virtual std::shared_ptr<Backend> allocate_backend() override;
......
......@@ -22,6 +22,7 @@
#include "ngraph/codegen/code_writer.hpp"
#include "ngraph/codegen/compiler.hpp"
#include "ngraph/codegen/execution_engine.hpp"
#include "ngraph/descriptor/input.hpp"
#include "ngraph/descriptor/layout/dense_tensor_view_layout.hpp"
#include "ngraph/descriptor/output.hpp"
......@@ -314,24 +315,25 @@ using namespace ngraph::runtime::cpu::eigen;
out << code;
out.close();
ngraph::codegen::execution_state estate;
codegen::Compiler compiler;
codegen::ExecutionEngine execution_engine;
#if NGCPU_PCH
estate.set_precompiled_headers_enabled(true);
compiler.set_precompiled_headers_enabled(true);
#endif
#if NGCPU_DEBUGINFO
estate.set_debuginfo_enabled(true);
compiler.set_debuginfo_enabled(true);
#endif
auto llvm_module = estate.compile(code, function_name + "_codegen.cpp");
auto llvm_module = compiler.compile(code);
if (llvm_module == nullptr)
{
throw runtime_error("function failed to compile");
}
estate.add_module(llvm_module);
estate.finalize();
m_compiled_function = estate.find_function<EntryPoint_t>(function_name);
execution_engine.add_module(llvm_module);
execution_engine.finalize();
m_compiled_function = execution_engine.find_function<EntryPoint_t>(function_name);
assert(m_compiled_function);
m_is_compiled = true;
......
......@@ -19,23 +19,26 @@
#include "gtest/gtest.h"
#include "ngraph/codegen/compiler.hpp"
#include "ngraph/codegen/execution_engine.hpp"
using namespace std;
using namespace ngraph;
TEST(codegen, simple_return)
{
constexpr auto name = "test.cpp";
constexpr auto source = R"(extern "C" int test() { return 2+5; })";
ngraph::codegen::execution_state estate;
auto module = estate.compile(source, name);
codegen::Compiler compiler;
codegen::ExecutionEngine execution_engine;
auto module = compiler.compile(source);
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);
int result = func();
......@@ -44,18 +47,19 @@ TEST(codegen, simple_return)
TEST(codegen, pass_args)
{
constexpr auto name = "test.cpp";
constexpr auto source = R"(extern "C" int test(int a, int b) { return a+b; })";
ngraph::codegen::execution_state estate;
auto module = estate.compile(source, name);
codegen::Compiler compiler;
codegen::ExecutionEngine execution_engine;
auto module = compiler.compile(source);
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);
int result = func(20, 22);
......@@ -64,7 +68,6 @@ TEST(codegen, pass_args)
TEST(codegen, include)
{
constexpr auto name = "test.cpp";
constexpr auto source =
R"(
#include <cmath>
......@@ -74,15 +77,17 @@ TEST(codegen, include)
}
)";
ngraph::codegen::execution_state estate;
auto module = estate.compile(source, name);
codegen::Compiler compiler;
codegen::ExecutionEngine execution_engine;
auto module = compiler.compile(source);
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);
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