#include "llvm/Target/TargetOptions.h" 
#include "llvm/Module.h" 
#include "llvm/Support/IRBuilder.h" 
#include "llvm/ModuleProvider.h" 
#include "llvm/ExecutionEngine/JIT.h" 
#include "llvm/Passmanager.h" 
#include "llvm/Transforms/Scalar.h" 
#include "llvm/Target/TargetData.h" 

using namespace llvm; 

static float global[32][4] = {0};

static Function *createFunction(Module *module, ExecutionEngine *executionEngine) 
{ 
	Function *function = cast<Function>(module->getOrInsertFunction("", Type::VoidTy, (Type *)0)); 
	BasicBlock *BB = BasicBlock::Create("", function); 
	IRBuilder *builder = new IRBuilder(BB); 

	{
		// Create globals 
		GlobalValue *globalPointer[32];
		
		for(int i = 0; i < 32; i++)
		{
			globalPointer[i] = new GlobalVariable(VectorType::get(Type::FloatTy, 4), false, GlobalValue::ExternalLinkage, 0, "", module); 
			globalPointer[i]->setAlignment(1);
			executionEngine->addGlobalMapping(globalPointer[i], global[i]);
		}

		// Do some math 
		Value *vector[32];

		for(int i = 0; i < 32; i++)
		{
			vector[i] = builder->Insert(new LoadInst(globalPointer[i], "", false, 1));   // Unaligned load 
		}

		for(int i = 0; i < 32; i++)
		{
			for(int j = i; j < 32; j++)
			{
				vector[i] = builder->CreateMul(vector[i], vector[j]);
			}
		}

		Value *result = builder->CreateAdd(vector[0], vector[0]);

		for(int i = 0; i < 32; i++)
		{
			result = builder->CreateAdd(result, vector[i]);
		}

		builder->Insert(new StoreInst(result, globalPointer[0], false, 1));
	}

	builder->CreateRetVoid(); 

	return function; 
} 


int main() 
{ 
	Module *module = new Module(""); 
	ExistingModuleProvider *MP = new ExistingModuleProvider(module); 
	ExecutionEngine *executionEngine = ExecutionEngine::create(MP, false); 
	Function *function = createFunction(module, executionEngine); 

	PassManager *passManager = new PassManager(); 
	passManager->add(new TargetData(*executionEngine->getTargetData())); 
	passManager->add(createScalarReplAggregatesPass());
	passManager->add(createInstructionCombiningPass());
	passManager->run(*module); 
	delete passManager; 

	void *f = executionEngine->getPointerToFunction(function); 

	((void(*)())f)(); 
} 
