Precompiled templates

Hello list,

I'm currently investigating the use of LLVM for a project. For example,
if I have a function like

int a(int x, int y) {
  if (P == 0) {
    return x - y;
  } else {
    return y - x;
  }
}

and P could be considered constant, one of the code paths could be
optimized out. Now I want to load a compiled bitcode file into a CFG,
replace all occurrences of P with the value (either 0 or 1), run the
optimization and JIT-compile into a anonymous function on the heap.
Is that possible and if yes how? Basically I thought of declaring the
parameter P as "extern const" and when the CFG is created, also create a
constant node with the actual value and run a "replace-symbol (P,
const_instr)". After that everything should be as easy as in the JIT
compiler tutorial.

Is something like that feasible? And how? I would appreciate any
suggestions.

Thanks in advance,

Andreas

Hi Andreas,

I'm currently investigating the use of LLVM for a project. For example,
if I have a function like

int a(int x, int y) {
   if (P == 0) {
     return x - y;
   } else {
     return y - x;
   }
}

and P could be considered constant, one of the code paths could be
optimized out. Now I want to load a compiled bitcode file into a CFG,
replace all occurrences of P with the value (either 0 or 1), run the
optimization and JIT-compile into a anonymous function on the heap.
Is that possible and if yes how?

It is. For example you can declare a new global constant P2 with the
appropriate initial value (0 or 1), then do: P->replaceAllUsesWith(P2).
At that you can erase (i.e. delete) P. Running the instcombine pass on
the module should then propagate the information into all functions;
following that by a run of the simplifycfg pass will get rid of dead
basic blocks.

  Basically I thought of declaring the

parameter P as "extern const" and when the CFG is created, also create a
constant node with the actual value and run a "replace-symbol (P,
const_instr)". After that everything should be as easy as in the JIT
compiler tutorial.

Yup, that's a good approach.

Ciao, Duncan.

Hi Duncan,

thanks for your response. Meanwhile I got a quick and dirty version
working.

It is. For example you can declare a new global constant P2 with the
appropriate initial value (0 or 1), then do:
P->replaceAllUsesWith(P2).
At that you can erase (i.e. delete) P.

I'm currently using a global variable, set it's initializer to a freshly
created constant value, declare the variable as itself being constant
and set it's linkage to internal.

Running the instcombine pass on
the module should then propagate the information into all functions;
following that by a run of the simplifycfg pass will get rid of dead
basic blocks.

I used a complete set of standard optimizations copy-pasted from some
tutorials. I was surprised that's all I had to do. Pretty cool and fast
:slight_smile:
So everything ends up in something like:

  bc = MemoryBuffer::getFile ("test.bc");
  mod = ParseBitcodeFile (bc, Context);

  GlobalVariable *val;
  ConstantInt *const_val;

  val = mod->getNamedGlobal ("P");
  const_val = ConstantInt::get (Context, APInt (32, 1234));
  val->setInitializer (const_val);
  val->setConstant (true);
  val->setLinkage (GlobalValue::InternalLinkage);

  FunctionPassManager fpm (mod);
  fpm.add (createInstructionCombiningPass());
  fpm.add (createCFGSimplificationPass ());
  fpm.doInitialization ();
  Function *f = mod->getFunction ("t");
  fpm.run (*f);

This allows me to write in C:
  int P;

  float t (float a, float b) {
    if (P == 0)
      return a - b;
    else
      return b - a;
  }

compiling this with: clang -O3 -x c test.c -emit-llvm -c -o test.bc
results in:
  @P = common global i32 0, align 4 ; <i32*> [#uses=1]

  define float @t(float %a, float %b) nounwind readonly {
    %1 = load i32* @P ; <i32> [#uses=1]
    %2 = icmp eq i32 %1, 0 ; <i1> [#uses=1]
    br i1 %2, label %3, label %5

  ; <label>:3 ; preds = %0
    %4 = fsub float %a, %b ; <float> [#uses=1]
    ret float %4

  ; <label>:5 ; preds = %0
    %6 = fsub float %b, %a ; <float> [#uses=1]
    ret float %6
  }

and after replacing the global with a constant and run the optimizations
I get a simple:
  define float @t(float %a, float %b) nounwind readnone {
    %1 = fsub float %a, %b ; <float> [#uses=1]
    ret float %1
  }

That is what I wanted. LLVM is really pretty cool stuff...