[Bf-blender-cvs] [50410d1] object_nodes: Completed basic expression tree code generation.
Lukas Tönne
noreply at git.blender.org
Fri Apr 8 17:30:52 CEST 2016
Commit: 50410d1ce56eedcc71070a143dffbab00ae72d3f
Author: Lukas Tönne
Date: Fri Apr 8 17:28:42 2016 +0200
Branches: object_nodes
https://developer.blender.org/rB50410d1ce56eedcc71070a143dffbab00ae72d3f
Completed basic expression tree code generation.
The code uses a pointer-based signature convention for node functions,
which helps to avoid issues with type coercion through clang.
In the future we could either find a more reliable way to produce llvm IR
for the nodes, or use node functions as external functions without inlining.
===================================================================
M source/blender/blenvm/llvm/llvm_codegen.cc
M source/blender/blenvm/modules/mod_value.cc
M source/blender/blenvm/modules/mod_value.h
===================================================================
diff --git a/source/blender/blenvm/llvm/llvm_codegen.cc b/source/blender/blenvm/llvm/llvm_codegen.cc
index 91d62de..5ad1311 100644
--- a/source/blender/blenvm/llvm/llvm_codegen.cc
+++ b/source/blender/blenvm/llvm/llvm_codegen.cc
@@ -39,6 +39,20 @@
#include "llvm_function.h"
#include "llvm_headers.h"
+/* call signature convention in llvm modules:
+ * BYVALUE: Pass struct types directly into (inlined) functions,
+ * Return type is a struct with all outputs, or a single
+ * value if node has just one output.
+ * BYPOINTER: Pass arguments as pointers/references.
+ * First function args are output pointers, followed by
+ * inputs const pointers.
+ * This call style is necessary to avoid type coercion by
+ * the clang compiler! See
+ * http://stackoverflow.com/questions/22776391/why-does-clang-coerce-struct-parameters-to-ints
+ */
+//#define BVM_NODE_CALL_BYVALUE
+#define BVM_NODE_CALL_BYPOINTER
+
/* TypeBuilder specializations for own structs */
namespace llvm {
@@ -104,14 +118,14 @@ namespace blenvm {
* rather than storing full TypeDesc in each socket!
* These functions just provides per-socket type names in the meantime.
*/
-inline string dummy_type_name(const InputKey &input)
+inline string dummy_type_name(ConstInputKey input)
{
size_t hash = std::hash<const NodeInstance *>()(input.node) ^ std::hash<const NodeInput *>()(input.socket);
std::stringstream ss;
ss << "InputType" << (unsigned short)hash;
return ss.str();
}
-inline string dummy_type_name(const OutputKey &output)
+inline string dummy_type_name(ConstOutputKey output)
{
size_t hash = std::hash<const NodeInstance *>()(output.node) ^ std::hash<const NodeOutput *>()(output.socket);
std::stringstream ss;
@@ -298,21 +312,47 @@ llvm::CallInst *LLVMCompiler::codegen_node_call(llvm::BasicBlock *block,
std::vector<Value *> args;
Value *retval = NULL;
+#ifdef BVM_NODE_CALL_BYVALUE
if (evalfunc->hasStructRetAttr()) {
Argument *retarg = &(*evalfunc->getArgumentList().begin());
retval = builder.CreateAlloca(retarg->getType()->getPointerElementType());
args.push_back(retval);
}
+#endif
+#ifdef BVM_NODE_CALL_BYPOINTER
+ for (int i = 0; i < node->num_outputs(); ++i) {
+ ConstOutputKey output = node->output(i);
+ Type *output_type = codegen_type(dummy_type_name(output), &output.socket->typedesc);
+ AllocaInst *outputmem = builder.CreateAlloca(output_type);
+
+ Type *arg_type = evalfunc->getFunctionType()->getParamType(args.size());
+ Value *value = builder.CreatePointerBitCastOrAddrSpaceCast(outputmem, arg_type);
+ args.push_back(value);
+
+ /* use as node output values */
+ bool ok = output_values.insert(OutputValuePair(output, outputmem)).second;
+ BLI_assert(ok && "Value for node output already defined!");
+ }
+#endif
/* set input arguments */
for (int i = 0; i < node->num_inputs(); ++i) {
ConstInputKey input = node->input(i);
switch (input.value_type()) {
- case INPUT_CONSTANT:
- args.push_back(codegen_constant(input.value()));
+ case INPUT_CONSTANT: {
+ /* create storage for the global value */
+ Constant *constval = codegen_constant(input.value());
+ AllocaInst *constmem = builder.CreateAlloca(constval->getType());
+ builder.CreateStore(constval, constmem);
+
+ /* use the pointer as function argument */
+ Type *arg_type = evalfunc->getFunctionType()->getParamType(args.size());
+ Value *value = builder.CreatePointerBitCastOrAddrSpaceCast(constmem, arg_type);
+ args.push_back(value);
break;
+ }
case INPUT_EXPRESSION:
args.push_back(output_values.at(input.link()));
break;
@@ -327,16 +367,32 @@ llvm::CallInst *LLVMCompiler::codegen_node_call(llvm::BasicBlock *block,
if (!retval)
retval = call;
- for (int i = 0; i < node->num_outputs(); ++i) {
- ConstOutputKey output = node->output(i);
- Value *value = builder.CreateStructGEP(retval, i);
- if (!value) {
- printf("Error: no output value defined for '%s':'%s'\n", node->name.c_str(), output.socket->name.c_str());
+ if (node->num_outputs() == 0) {
+ /* nothing to return */
+ }
+ else {
+#ifdef BVM_NODE_CALL_BYVALUE
+ if (evalfunc->hasStructRetAttr()) {
+ for (int i = 0; i < node->num_outputs(); ++i) {
+ ConstOutputKey output = node->output(i);
+ Value *value = builder.CreateStructGEP(retval, i);
+ if (!value) {
+ printf("Error: no output value defined for '%s':'%s'\n", node->name.c_str(), output.socket->name.c_str());
+ }
+
+ /* use as node output values */
+ bool ok = output_values.insert(OutputValuePair(output, value)).second;
+ BLI_assert(ok && "Value for node output already defined!");
+ }
}
-
- /* use as node output values */
- bool ok = output_values.insert(OutputValuePair(output, value)).second;
- BLI_assert(ok && "Value for node output already defined!");
+ else {
+ BLI_assert(node->num_outputs() == 1);
+ ConstOutputKey output = node->output(0);
+ /* use as node output values */
+ bool ok = output_values.insert(OutputValuePair(output, retval)).second;
+ BLI_assert(ok && "Value for node output already defined!");
+ }
+#endif
}
return call;
@@ -375,10 +431,6 @@ llvm::BasicBlock *LLVMCompiler::codegen_function_body_expression(const NodeGraph
for (NodeGraph::NodeInstanceMap::const_iterator it = graph.nodes.begin(); it != graph.nodes.end(); ++it)
nodes.insert(it->second);
- printf("DOING THE NODES: ---\n");
- module()->dump();
- printf("--------------------\n");
-
for (OrderedNodeSet::const_iterator it = nodes.begin(); it != nodes.end(); ++it) {
const NodeInstance &node = **it;
@@ -392,9 +444,9 @@ llvm::BasicBlock *LLVMCompiler::codegen_function_body_expression(const NodeGraph
Value *retptr = builder.CreateStructGEP(retarg, i);
-// Value *value = output_values.at(output.key);
-// Value *retval = builder.CreateLoad(value);
-// builder.CreateStore(retval, retptr);
+ Value *value = output_values.at(output.key);
+ Value *retval = builder.CreateLoad(value);
+ builder.CreateStore(retval, retptr);
}
builder.CreateRetVoid();
@@ -407,7 +459,7 @@ llvm::Function *LLVMCompiler::codegen_node_function(const string &name, const No
using namespace llvm;
FunctionType *functype = codegen_node_function_type(graph);
- Function *func = llvm::cast<Function>(module()->getOrInsertFunction(name, functype));
+ Function *func = Function::Create(functype, Function::ExternalLinkage, name, module());
Argument *retarg = func->getArgumentList().begin();
retarg->addAttr(AttributeSet::get(context(), AttributeSet::ReturnIndex, Attribute::StructRet));
@@ -426,15 +478,20 @@ FunctionLLVM *LLVMCompiler::compile_function(const string &name, const NodeGraph
using namespace llvm;
m_module = new Module(name, context());
- llvm_execution_engine()->addModule(m_module);
llvm_link_module_full(m_module);
Function *func = codegen_node_function(name, graph);
- assert(func != NULL && "codegen_node_function returned NULL!");
+ BLI_assert(m_module->getFunction(name) && "Function not registered in module!");
+ BLI_assert(func != NULL && "codegen_node_function returned NULL!");
printf("=== NODE FUNCTION ===\n");
+ fflush(stdout);
func->dump();
printf("=====================\n");
+ fflush(stdout);
+
+ verifyFunction(*func, &outs());
+ verifyModule(*m_module, &outs());
FunctionPassManager fpm(m_module);
PassManagerBuilder builder;
@@ -444,13 +501,14 @@ FunctionLLVM *LLVMCompiler::compile_function(const string &name, const NodeGraph
fpm.run(*func);
- llvm_execution_engine()->finalizeObject();
-
+ /* Note: Adding module to exec engine before creating the function prevents compilation! */
+ llvm_execution_engine()->addModule(m_module);
uint64_t address = llvm_execution_engine()->getFunctionAddress(name);
BLI_assert(address != 0);
llvm_execution_engine()->removeModule(m_module);
delete m_module;
+ m_module = NULL;
FunctionLLVM *fn = new FunctionLLVM(address);
return fn;
diff --git a/source/blender/blenvm/modules/mod_value.cc b/source/blender/blenvm/modules/mod_value.cc
index 11c43e8..40bb82a 100644
--- a/source/blender/blenvm/modules/mod_value.cc
+++ b/source/blender/blenvm/modules/mod_value.cc
@@ -28,9 +28,9 @@
#include "mod_math.h"
#include "mod_value.h"
-void VALUE_FLOAT(float *result, const float *value)
+void VALUE_FLOAT(float &result, const float &value)
{
- *result = *value;
+ result = value;
}
void VALUE_FLOAT3(float3 &result, const float3 &value)
diff --git a/source/blender/blenvm/modules/mod_value.h b/source/blender/blenvm/modules/mod_value.h
index a0eff6d..0ae585d 100644
--- a/source/blender/blenvm/modules/mod_value.h
+++ b/source/blender/blenvm/modules/mod_value.h
@@ -28,7 +28,7 @@
#ifndef __MOD_VALUE_H__
#define __MOD_VALUE_H__
-__attribute__((annotate("VALUE_FLOAT"))) void VALUE_FLOAT(float *result, const float *value);
+__attribute__((annotate("VALUE_FLOAT"))) void VALUE_FLOAT(float &result, const float &value);
__attribute__((annotate("VALUE_FLOAT3"))) void VALUE_FLOAT3(float3 &result, const float3 &value);
__attribute__((annotate("VALUE_FLOAT4"))) void VALUE_FLOAT4(float4 &result, const float4 &value);
__attribute__((annotate("VALUE_INT"))) void VALUE_INT(int &result, const int &value);
More information about the Bf-blender-cvs
mailing list