Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Static vs jit #666

Draft
wants to merge 83 commits into
base: llvm
Choose a base branch
from
Draft
Changes from 1 commit
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
16d3cab
Disable python bindings for faster build
pramodk Nov 27, 2020
b1cfda6
Integrate LLVM into CMake build system
pramodk Nov 28, 2020
46d4779
Code infrastructure for LLVM code generation backend
pramodk Nov 28, 2020
51987dc
Azure CI fixes for LLVM build and README update
pramodk Nov 28, 2020
ae07ce4
Print build status after cmake configure stage
pramodk Nov 29, 2020
c540fb1
Adding test template for LLVM codegen
pramodk Nov 29, 2020
724605c
Initial LLVM codegen vistor routines (#457)
georgemitenkov Dec 22, 2020
b621d4e
FunctionBlock code generation and terminator checks (#470)
georgemitenkov Dec 25, 2020
917a7da
Add option to run LLVM optimisation passes (#471)
pramodk Dec 28, 2020
b261ba9
Add function call LLVM code generation (#477)
georgemitenkov Dec 30, 2020
7884de8
Support for IndexedName codegen (#478)
georgemitenkov Dec 30, 2020
dbda271
Improvements for code generation specific transformations (#483)
pramodk Jan 6, 2021
83abf60
nrn_state function generation in NMODL AST to help LLVM codegen (#484)
pramodk Jan 6, 2021
bcc091b
Running functions from MOD files via LLVM JIT (#482)
georgemitenkov Jan 8, 2021
838ed6f
Extended support for binary ops and refactoring (#489)
georgemitenkov Jan 12, 2021
eaeb7aa
Avoid converting LOCAL statement in all StatementBlocks (#492)
pramodk Jan 12, 2021
6d60ca9
Handle CodegenVarType type in JSON printer (#494)
pramodk Jan 13, 2021
5b32b31
Integrating LLVM helper into LLVM visitor (#497)
georgemitenkov Jan 25, 2021
bc305ba
LLVM code generation for if/else statements (#499)
georgemitenkov Jan 25, 2021
c8ea994
Added error handling for values not in scope (#502)
georgemitenkov Jan 26, 2021
a32f76b
Added support for WHILE statement (#501)
georgemitenkov Jan 26, 2021
bfaff72
Create mechanism instance struct in LLVM IR (#507)
iomaganaris Feb 1, 2021
b690961
Printf support in LLVM IR codegen (#510)
georgemitenkov Feb 3, 2021
a561c97
Fix issue error: ‘runtime_error’ is not a member of ‘std’ (#512)
iomaganaris Feb 15, 2021
5c80684
Move code gen specific InstanceStruct node to codegen.yaml (#526)
pramodk Mar 5, 2021
5b72bc3
* Improvements to codegen helper (Part I)
pramodk Feb 27, 2021
4b3e2fc
Addressing TODOs for Instance struct (#533) Part II
georgemitenkov Mar 6, 2021
c08eb22
Unit test for scalar state kernel generation in LLVM (#547)
georgemitenkov Mar 9, 2021
ec91271
Indexed name codegen improvements (#550)
georgemitenkov Mar 12, 2021
559d152
Add InstanceStruct test data generation helper and unit test (#546)
iomaganaris Mar 13, 2021
b5d152f
Add the remainder loop for vectorization of DERIVATIVE block (#534)
Mar 17, 2021
c7e5e28
Always initialize return variable in function block (#554)
Mar 19, 2021
20dc785
Running a kernel with NMODL-LLVM JIT (#549)
georgemitenkov Apr 9, 2021
f684466
Loop epilogue fix for LLVM visitor helper (#567)
georgemitenkov Apr 9, 2021
cc92fc2
Gather support and vectorisation fixes for LLVM code generation (#568)
georgemitenkov Apr 10, 2021
3803ae1
Verification and file utilities for LLVM IR codegen (#582)
georgemitenkov Apr 13, 2021
60e68c9
Add gather execution test (#591)
georgemitenkov Apr 16, 2021
8f1fbae
Fixed loop allocations (#590)
georgemitenkov Apr 17, 2021
53cd1cc
Benchmarking LLVM code generation (#583)
georgemitenkov Apr 17, 2021
db80372
Minor benchmarking improvement (#593)
pramodk Apr 18, 2021
2a699a8
Bug fix in codegen helper: delete LOCAL statement (#595)
pramodk Apr 19, 2021
630033c
LLVM 13 compatibility and fixing void* type (#603)
georgemitenkov Apr 20, 2021
fe3d856
Allow LOCAL variable inside StatementBlock for LLVM IR generation (#599)
pramodk Apr 20, 2021
dddffed
Update CI with LLVM v13 (trunk) (#605)
pramodk Apr 22, 2021
068ba5d
Integrating vector maths library into LLVM codegen (#604)
georgemitenkov Apr 22, 2021
98a88d5
Using shared libraries in LLVM JIT (#609)
georgemitenkov Apr 22, 2021
16504c7
Avoid local std::ofstream object causing segfault (#614)
pramodk Apr 24, 2021
dd2889d
Refactoring of runners' infrastructure and dumping object files (#620)
georgemitenkov Apr 30, 2021
93732e0
Optimisation levels for benchmarking (#623)
georgemitenkov May 7, 2021
150943c
Adding function debug information (#628)
georgemitenkov May 8, 2021
1802b74
Fixed using benchmarking_info in TestRunner (#631)
georgemitenkov May 8, 2021
3359ea3
Fixes to run CI with NVHPC/PGI compiler
pramodk May 8, 2021
524b292
Fixed addition of SOLVE block to kernel's FOR loop (#636)
georgemitenkov May 11, 2021
68639a7
IR builder redesign for LLVM IR code generation pipeline (#634)
georgemitenkov May 13, 2021
454a18f
Fixed initialisation of `CodegenAtomicStatement` (#642)
georgemitenkov May 13, 2021
53727df
Fix instance struct data generation for testing/benchmarking (#641)
pramodk May 13, 2021
23100e9
Basic scatter support (#643)
georgemitenkov May 13, 2021
bd05479
Benchmarking code re-organisation and minor improvements (#647)
pramodk May 16, 2021
5d126aa
Added attributes and metadata to LLVM IR compute kernels (#648)
georgemitenkov May 17, 2021
ee8bbdb
Added loaded value to the stack (#655)
georgemitenkov May 18, 2021
5ee761b
Basic predication support for LLVM backend (#652)
georgemitenkov May 20, 2021
8bee7de
Improvements for LLVM code generation and benchmarking (#661)
georgemitenkov May 20, 2021
1461d8f
Fixed `alloca`s insertion point for LLVM backend (#663)
georgemitenkov May 20, 2021
908779e
Fast math flags for LLVM backend (#662)
georgemitenkov May 21, 2021
2ca85e5
Avoid generating LLVM IR for Functions and Procedures if inlined (#664)
iomaganaris May 21, 2021
7fdbb4f
Fixed typo in benchmarking metrics (#665)
georgemitenkov May 21, 2021
4c585f3
Remove only inlined blocks from AST based on symtab properties (#668)
iomaganaris May 21, 2021
f0a3afc
Use VarName on the RHS of assignment expression (#669)
pramodk May 25, 2021
2609f87
[LLVM] SLEEF and libsystem_m vector libraries support (#674)
georgemitenkov May 30, 2021
da1ed52
hacky support for timing JIT vs statically compiled kernels
May 18, 2021
6ca88c2
renamed folder
May 19, 2021
bf8c9d5
slightly better stub for ext kernel, init data at every iteration
May 20, 2021
57d785c
added script and simple kernels
May 20, 2021
a714903
updated benchmark stats for hacky ext kernel
May 20, 2021
2853d23
fixed typo, cleaned up stub
May 20, 2021
6b5e022
added hh kernel
May 21, 2021
7b7b33b
add fast math for JIT in script
May 21, 2021
a5da1d1
added precise division flag to icpc
May 25, 2021
b1b851c
added pragmas to cpp kernel, add ext kernel sse2 and avx2
May 26, 2021
7620651
pragm/flag to vec hh with clang, addded gcc
May 27, 2021
80e85bf
flag typo
May 28, 2021
102210e
rebased and updated llvm path
May 31, 2021
e3a25be
fixed llvm path
May 31, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Support for IndexedName codegen (#478)
LLVM code generation for `IndexedName`s.

- Added code generation for initialising arrays in LOCAL blocks (with both integer constants and macros).
- Added support for indexing arrays.

fixes #467
georgemitenkov authored and pramodk committed May 8, 2021
commit 7884de81fb25ac7a94b256bb6230d5e55be450c1
88 changes: 80 additions & 8 deletions src/codegen/llvm/codegen_llvm_visitor.cpp
Original file line number Diff line number Diff line change
@@ -25,6 +25,44 @@ namespace codegen {
/* Helper routines */
/****************************************************************************************/

bool CodegenLLVMVisitor::check_array_bounds(const ast::IndexedName& node, unsigned index) {
llvm::Type* array_type =
local_named_values->lookup(node.get_node_name())->getType()->getPointerElementType();
unsigned length = array_type->getArrayNumElements();
return 0 <= index && index < length;
}

llvm::Value* CodegenLLVMVisitor::create_gep(const std::string& name, unsigned index) {
llvm::Type* index_type = llvm::Type::getInt32Ty(*context);
std::vector<llvm::Value*> indices;
indices.push_back(llvm::ConstantInt::get(index_type, 0));
indices.push_back(llvm::ConstantInt::get(index_type, index));

return builder.CreateInBoundsGEP(local_named_values->lookup(name), indices);
}

llvm::Value* CodegenLLVMVisitor::codegen_indexed_name(const ast::IndexedName& node) {
unsigned index = get_array_index_or_length(node);

// Check if index is within array bounds.
if (!check_array_bounds(node, index))
throw std::runtime_error("Error: Index is out of bounds");

return create_gep(node.get_node_name(), index);
}

unsigned CodegenLLVMVisitor::get_array_index_or_length(const ast::IndexedName& indexed_name) {
auto integer = std::dynamic_pointer_cast<ast::Integer>(indexed_name.get_length());
if (!integer)
throw std::runtime_error("Error: expecting integer index or length");

// Check if integer value is taken from a macro.
if (!integer->get_macro())
return integer->get_value();
const auto& macro = sym_tab->lookup(integer->get_macro()->get_node_name());
return static_cast<unsigned>(*macro->get_value());
}

void CodegenLLVMVisitor::run_llvm_opt_passes() {
/// run some common optimisation passes that are commonly suggested
fpm.add(llvm::createInstructionCombiningPass());
@@ -43,7 +81,6 @@ void CodegenLLVMVisitor::run_llvm_opt_passes() {
}
}


void CodegenLLVMVisitor::create_external_method_call(const std::string& name,
const ast::ExpressionVector& arguments) {
std::vector<llvm::Value*> argument_values;
@@ -187,8 +224,17 @@ void CodegenLLVMVisitor::visit_binary_expression(const ast::BinaryExpression& no
if (!var) {
throw std::runtime_error("Error: only VarName assignment is currently supported.\n");
}
llvm::Value* alloca = local_named_values->lookup(var->get_node_name());
builder.CreateStore(rhs, alloca);

const auto& identifier = var->get_name();
if (identifier->is_name()) {
llvm::Value* alloca = local_named_values->lookup(var->get_node_name());
builder.CreateStore(rhs, alloca);
} else if (identifier->is_indexed_name()) {
auto indexed_name = std::dynamic_pointer_cast<ast::IndexedName>(identifier);
builder.CreateStore(rhs, codegen_indexed_name(*indexed_name));
} else {
throw std::runtime_error("Error: Unsupported variable type");
}
return;
}

@@ -254,10 +300,22 @@ void CodegenLLVMVisitor::visit_integer(const ast::Integer& node) {

void CodegenLLVMVisitor::visit_local_list_statement(const ast::LocalListStatement& node) {
for (const auto& variable: node.get_variables()) {
// LocalVar always stores a Name.
auto name = variable->get_node_name();
llvm::Type* var_type = llvm::Type::getDoubleTy(*context);
llvm::Value* alloca = builder.CreateAlloca(var_type, /*ArraySize=*/nullptr, name);
std::string name = variable->get_node_name();
const auto& identifier = variable->get_name();
// Local variable can be a scalar (Node AST class) or an array (IndexedName AST class). For
// each case, create memory allocations with the corresponding LLVM type.
llvm::Type* var_type;
if (identifier->is_indexed_name()) {
auto indexed_name = std::dynamic_pointer_cast<ast::IndexedName>(identifier);
unsigned length = get_array_index_or_length(*indexed_name);
var_type = llvm::ArrayType::get(llvm::Type::getDoubleTy(*context), length);
} else if (identifier->is_name()) {
// This case corresponds to a scalar local variable. Its type is double by default.
var_type = llvm::Type::getDoubleTy(*context);
} else {
throw std::runtime_error("Error: Unsupported local variable type");
}
builder.CreateAlloca(var_type, /*ArraySize=*/nullptr, name);
}
}

@@ -310,7 +368,21 @@ void CodegenLLVMVisitor::visit_unary_expression(const ast::UnaryExpression& node
}

void CodegenLLVMVisitor::visit_var_name(const ast::VarName& node) {
llvm::Value* var = builder.CreateLoad(local_named_values->lookup(node.get_node_name()));
const auto& identifier = node.get_name();
if (!identifier->is_name() && !identifier->is_indexed_name())
throw std::runtime_error("Error: Unsupported variable type");

llvm::Value* ptr;
if (identifier->is_name())
ptr = local_named_values->lookup(node.get_node_name());

if (identifier->is_indexed_name()) {
auto indexed_name = std::dynamic_pointer_cast<ast::IndexedName>(identifier);
ptr = codegen_indexed_name(*indexed_name);
}

// Finally, load the variable from the pointer value.
llvm::Value* var = builder.CreateLoad(ptr);
values.push_back(var);
}

29 changes: 29 additions & 0 deletions src/codegen/llvm/codegen_llvm_visitor.hpp
Original file line number Diff line number Diff line change
@@ -100,6 +100,35 @@ class CodegenLLVMVisitor: public visitor::ConstAstVisitor {
, builder(*context)
, fpm(module.get()) {}

/**
* Checks if array index specified by the given IndexedName is within bounds
* \param node IndexedName representing array
* \return \c true if the index is within bounds
*/
bool check_array_bounds(const ast::IndexedName& node, unsigned index);

/**
* Generates LLVM code for the given IndexedName
* \param node IndexedName NMODL AST node
* \return LLVM code generated for this AST node
*/
llvm::Value* codegen_indexed_name(const ast::IndexedName& node);

/**
* Returns GEP instruction to 1D array
* \param name 1D array name
* \param index element index
* \return GEP instruction value
*/
llvm::Value* create_gep(const std::string& name, unsigned index);

/**
* Returns array index or length from given IndexedName
* \param node IndexedName representing array
* \return array index or length
*/
unsigned get_array_index_or_length(const ast::IndexedName& node);

/**
* Create a function call to an external method
* \param name external method name
111 changes: 111 additions & 0 deletions test/unit/codegen/llvm.cpp
Original file line number Diff line number Diff line change
@@ -116,6 +116,31 @@ SCENARIO("Binary expression", "[visitor][llvm]") {
}
}

//=============================================================================
// Define
//=============================================================================

SCENARIO("Define", "[visitor][llvm]") {
GIVEN("Procedure with array variable of length specified by DEFINE") {
std::string nmodl_text = R"(
DEFINE N 100

PROCEDURE foo() {
LOCAL x[N]
}
)";

THEN("macro is expanded and array is allocated") {
std::string module_string = run_llvm_visitor(nmodl_text);
std::smatch m;

// Check stack allocations for i and j
std::regex array(R"(%x = alloca \[100 x double\])");
REQUIRE(std::regex_search(module_string, m, array));
}
}
}

//=============================================================================
// FunctionBlock
//=============================================================================
@@ -256,6 +281,92 @@ SCENARIO("Function call", "[visitor][llvm]") {
}
}

//=============================================================================
// IndexedName
//=============================================================================

SCENARIO("Indexed name", "[visitor][llvm]") {
GIVEN("Procedure with a local array variable") {
std::string nmodl_text = R"(
PROCEDURE foo() {
LOCAL x[2]
}
)";

THEN("array is allocated") {
std::string module_string = run_llvm_visitor(nmodl_text);
std::smatch m;

std::regex array(R"(%x = alloca \[2 x double\])");
REQUIRE(std::regex_search(module_string, m, array));
}
}

GIVEN("Procedure with a local array assignment") {
std::string nmodl_text = R"(
PROCEDURE foo() {
LOCAL x[2]
x[1] = 3
}
)";

THEN("element is stored to the array") {
std::string module_string = run_llvm_visitor(nmodl_text);
std::smatch m;

// Check GEP is created correctly to pint at array element.
std::regex GEP(
R"(%1 = getelementptr inbounds \[2 x double\], \[2 x double\]\* %x, i32 0, i32 1)");
REQUIRE(std::regex_search(module_string, m, GEP));

// Check the value is stored to the pointer.
std::regex store(R"(store double 3.000000e\+00, double\* %1)");
REQUIRE(std::regex_search(module_string, m, store));
}
}

GIVEN("Procedure with a assignment of array element") {
std::string nmodl_text = R"(
PROCEDURE foo() {
LOCAL x[2], y
x[1] = 3
y = x[1]
}
)";

THEN("array element is stored to the variable") {
std::string module_string = run_llvm_visitor(nmodl_text);
std::smatch m;

// Check GEP is created correctly to pint at array element.
std::regex GEP(
R"(%2 = getelementptr inbounds \[2 x double\], \[2 x double\]\* %x, i32 0, i32 1)");
REQUIRE(std::regex_search(module_string, m, GEP));

// Check the value is loaded from the pointer.
std::regex load(R"(%3 = load double, double\* %2)");
REQUIRE(std::regex_search(module_string, m, load));

// Check the value is stored to the the variable.
std::regex store(R"(store double %3, double\* %y)");
REQUIRE(std::regex_search(module_string, m, store));
}
}

GIVEN("Array with out of bounds access") {
std::string nmodl_text = R"(
PROCEDURE foo() {
LOCAL x[2]
x[5] = 3
}
)";

THEN("error is thrown") {
REQUIRE_THROWS_AS(run_llvm_visitor(nmodl_text), std::runtime_error);
}
}
}

//=============================================================================
// LocalList and LocalVar
//=============================================================================