//g++ `llvm-config --cxxflags` `llvm-config --ldflags` `llvm-config --libs` main.cpp -o test
#include <llvm/Module.h>
#include <llvm/ModuleProvider.h>
#include <llvm/DerivedTypes.h>
#include <llvm/Constants.h>
#include <llvm/GlobalVariable.h>
#include <llvm/Function.h>
#include <llvm/CallingConv.h>
#include <llvm/BasicBlock.h>
#include <llvm/Instructions.h>
#include <llvm/InlineAsm.h>
#include <llvm/ParameterAttributes.h>
#include <llvm/Support/MathExtras.h>
#include <llvm/Pass.h>
#include <llvm/PassManager.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Assembly/PrintModulePass.h>

#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/ExecutionEngine/Interpreter.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include <algorithm>
#include <iostream>
using namespace llvm;

/*
The underneath generated with clang with:
clang --emit-llvm file.c |llvm-as|opt -std-compile-opts|llvm2cpp
where file.c contains:
void * malloc(int);
float4 *array_to_vector(float **array, int num)
{
    float4 *vector = (float4*)malloc(sizeof(float)*4*num);
    for (int i = 0; i < num; ++i) {
        float4 vec;
        vec.x = array[i][0];
        vec.y = array[i][1];
        vec.w = array[i][2];
        vec.z = array[i][3];
        vector[i] = vec;
    }
    return vector;
}
*/
Module* makeLLVMModule() {
  // Module Construction
  Module* mod = new Module("xopt.bc");

  // Type Definitions
  VectorType* VectorTy_1 = VectorType::get(Type::FloatTy, 4);

  PointerType* PointerTy_0 = PointerType::get(VectorTy_1);

  std::vector<const Type*>FuncTy_2_args;
  PointerType* PointerTy_4 = PointerType::get(Type::FloatTy);

  PointerType* PointerTy_3 = PointerType::get(PointerTy_4);

  FuncTy_2_args.push_back(PointerTy_3);
  FuncTy_2_args.push_back(IntegerType::get(32));
  ParamAttrsList *FuncTy_2_PAL = 0;
  FunctionType* FuncTy_2 = FunctionType::get(
    /*Result=*/PointerTy_0,
    /*Params=*/FuncTy_2_args,
    /*isVarArg=*/false,
    /*ParamAttrs=*/FuncTy_2_PAL);


  // Function Declarations

  Function* func_array_to_vector = new Function(
    /*Type=*/FuncTy_2,
    /*Linkage=*/GlobalValue::ExternalLinkage,
    /*Name=*/"array_to_vector", mod);
  func_array_to_vector->setCallingConv(CallingConv::C);

  // Global Variable Declarations


  // Constant Definitions
  Constant* const_int32_5 = Constant::getNullValue(IntegerType::get(32));
  UndefValue* const_packed_6 = UndefValue::get(VectorTy_1);
  ConstantInt* const_int32_7 = ConstantInt::get(APInt(32,  "1", 10));
  ConstantInt* const_int32_8 = ConstantInt::get(APInt(32,  "2", 10));
  ConstantInt* const_int32_9 = ConstantInt::get(APInt(32,  "3", 10));

  // Global Variable Definitions

  // Function Definitions

  // Function: array_to_vector (func_array_to_vector)
  {
    Function::arg_iterator args = func_array_to_vector->arg_begin();
    Value* ptr_array = args++;
    ptr_array->setName("array");
    Value* int32_num = args++;
    int32_num->setName("num");

    BasicBlock* label_entry = new BasicBlock("entry",func_array_to_vector,0);
    BasicBlock* label_forbody = new BasicBlock("forbody",func_array_to_vector,0);
    BasicBlock* label_afterfor = new BasicBlock("afterfor",func_array_to_vector,0);

    // Block entry (label_entry)
    MallocInst* ptr_call = new MallocInst(VectorTy_1, int32_num, "call", label_entry);
    ICmpInst* int1_cmp = new ICmpInst(ICmpInst::ICMP_SGT, int32_num, const_int32_5, "cmp", label_entry);
    new BranchInst(label_forbody, label_afterfor, int1_cmp, label_entry);

    // Block forbody (label_forbody)
    Argument* fwdref_11 = new Argument(IntegerType::get(32));
    PHINode* int32_i_02_0 = new PHINode(IntegerType::get(32), "i.02.0", label_forbody);
    int32_i_02_0->reserveOperandSpace(2);
    int32_i_02_0->addIncoming(const_int32_5, label_entry);
    int32_i_02_0->addIncoming(fwdref_11, label_forbody);

    Argument* fwdref_12 = new Argument(VectorTy_1);
    PHINode* packed_vec_01_0 = new PHINode(VectorTy_1, "vec.01.0", label_forbody);
    packed_vec_01_0->reserveOperandSpace(2);
    packed_vec_01_0->addIncoming(const_packed_6, label_entry);
    packed_vec_01_0->addIncoming(fwdref_12, label_forbody);

    GetElementPtrInst* ptr_arrayidx = new GetElementPtrInst(ptr_array, int32_i_02_0, "arrayidx", label_forbody);
    LoadInst* ptr_tmp6 = new LoadInst(ptr_arrayidx, "tmp6", false, label_forbody);
    LoadInst* float_tmp8 = new LoadInst(ptr_tmp6, "tmp8", false, label_forbody);
    InsertElementInst* packed_tmp10 = new InsertElementInst(packed_vec_01_0, float_tmp8, const_int32_5, "tmp10", label_forbody);
    GetElementPtrInst* ptr_arrayidx15 = new GetElementPtrInst(ptr_tmp6, const_int32_7, "arrayidx15", label_forbody);
    LoadInst* float_tmp16 = new LoadInst(ptr_arrayidx15, "tmp16", false, label_forbody);
    InsertElementInst* packed_tmp18 = new InsertElementInst(packed_tmp10, float_tmp16, const_int32_7, "tmp18", label_forbody);
    GetElementPtrInst* ptr_arrayidx23 = new GetElementPtrInst(ptr_tmp6, const_int32_8, "arrayidx23", label_forbody);
    LoadInst* float_tmp24 = new LoadInst(ptr_arrayidx23, "tmp24", false, label_forbody);
    InsertElementInst* packed_tmp26 = new InsertElementInst(packed_tmp18, float_tmp24, const_int32_9, "tmp26", label_forbody);
    GetElementPtrInst* ptr_arrayidx31 = new GetElementPtrInst(ptr_tmp6, const_int32_9, "arrayidx31", label_forbody);
    LoadInst* float_tmp32 = new LoadInst(ptr_arrayidx31, "tmp32", false, label_forbody);
    InsertElementInst* packed_tmp34 = new InsertElementInst(packed_tmp26, float_tmp32, const_int32_8, "tmp34", label_forbody);
    GetElementPtrInst* ptr_arrayidx37 = new GetElementPtrInst(ptr_call, int32_i_02_0, "arrayidx37", label_forbody);
    StoreInst* void_13 = new StoreInst(packed_tmp34, ptr_arrayidx37, false, label_forbody);
    BinaryOperator* int32_indvar_next1 = BinaryOperator::create(Instruction::Add, int32_i_02_0, const_int32_7, "indvar.next1", label_forbody);
    ICmpInst* int1_exitcond2 = new ICmpInst(ICmpInst::ICMP_EQ, int32_indvar_next1, int32_num, "exitcond2", label_forbody);
    new BranchInst(label_afterfor, label_forbody, int1_exitcond2, label_forbody);

    // Block afterfor (label_afterfor)
    new ReturnInst(ptr_call, label_afterfor);

    // Resolve Forward References
    fwdref_12->replaceAllUsesWith(packed_tmp34); delete fwdref_12;
    fwdref_11->replaceAllUsesWith(int32_indvar_next1); delete fwdref_11;

  }

  return mod;
}

int main()
{
    float array[5][4] = { {1, 1, 1, 1},
                          {2, 2, 2, 2},
                          {3, 3, 3, 3},
                          {4, 4, 4, 4}};
    Module *mod = makeLLVMModule();
    Function *atv = mod->getFunction("array_to_vector");

    ExistingModuleProvider *mp = new ExistingModuleProvider(mod);
    ExecutionEngine *ee = ExecutionEngine::create(mp, false);

    std::vector<GenericValue> args1(2);
    args1[0]        = GenericValue(array);
    args1[1].IntVal = APInt(32, 4);
    //std::cout<<"func is " <<*atv;
    GenericValue gv = ee->runFunction(atv, args1);

    return 0;
}
