#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[5];

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 *globalPointer1 = new GlobalVariable(VectorType::get(Type::FloatTy, 4), true, GlobalValue::ExternalLinkage, 0, "", module);
		GlobalValue *globalPointer2 = new GlobalVariable(VectorType::get(Type::FloatTy, 4), true, GlobalValue::ExternalLinkage, 0, "", module);
		executionEngine->addGlobalMapping(globalPointer1, (char*)global + 1);   // Guaranteed to be unaligned
		executionEngine->addGlobalMapping(globalPointer2, (char*)global + 2);   // Guaranteed to be unaligned

		// Do some math
		Value *vector1 = builder->Insert(new LoadInst(globalPointer1, "", false, 1));   // Unaligned load
		Value *vector2 = builder->Insert(new LoadInst(globalPointer2, "", false, 1));   // Unaligned load
		Value *result = builder->CreateMul(vector1, vector2);
		builder->Insert(new StoreInst(result, globalPointer1, false, 1));   // Unaligned store
	}

	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());   // Culprit
	passManager->run(*module);
	delete passManager;

	((void(*)())executionEngine->getPointerToFunction(function))();
}
