PHI and Allocas

Hi,

I'm incrementing my toy compiler to account for variable mutations
around if/else blocks.

Reading the Kaleidoscope tutorial, I've learnt that I can substitute
the PHI node creation by allocas and retrieving the value later, if
needed. The Kaleidoscope example seems to account for one value only,
the result of:

Value *ThenV = Then->Codegen();
(...)
Value *ElseV = Else->Codegen();
(...)
PN->addIncoming(ThenV, ThenBB);
PN->addIncoming(ElseV, ElseBB);

But both Then and Else are expressions, so the Value returned is in
the form of a single variable.

ExprAST *Then = ParseExpression();
(...)
ExprAST *Else = ParseExpression();

In my toy language, I accept any number of statements inside a block.

Ignoring nested if statements, should I keep track of *each* variable
mutation inside the block, or is there some LLVM magic (as I'm getting
used to)?

A simple C example would be:

int a, b, c, d;
(...)
if (a > 10) {
  b = c = d = 10 * a;
} else {
  b = c = d = 10 / a;
}

As I'm always using alloca, the mem2reg pass seems to do the trick,
but I'm not sure it would in all cases. The tutorial states that it
works on all scalar values (I have no structs or arrays), so I believe
I'm safe.

Am I?

cheers,
--renato

I hope so, because I've been doing the same thing. My local variables
are always bound to alloca's, and my test makefile just calls "opt
-std-compile-opts" after my compiler runs; so far it's been giving me
pretty good code as a result.

Same here, but I'm not compiling any really complex code, so I thought
it was just beginner's luck.

Looks like it's another score for the "LLVM magic"! :wink:

It might create problems for a potential debugger, but I'm not
planning to create one too soon.

cheers,
--renato

There's a lot more magic where that came from.

Case in point: my language has an "exit" statement, where any
statement you put after it gets run at the end of the scope it appears
in. You can also put return statements in any scope you like, and
every containing scope will have its exit functions run on the way
out. To make that work, I alloca'd a "destination scope" variable,
then when I finish a scope, I go to the exit statements, and the exit
statements are followed by a conditional jump based on the value of
the destination scope... I can go to the code right after the scope,
or to the containing scope's exit code. A return statement just
compiles down to:

returnval = whatever
destScope = -1
goto scopeExit

If execution just goes to the end of the scope, it does:

destScope--;
goto scopeExit

Anyway, the code that my front-end outputs is full of statements
fiddling with scope destinations and conditional jumps, but when you
run it through "opt -std-compile-opts", all that goes away and, when
control unconditionally leaves a scope normally, the conditional
branches are gone too.

Yes, you're safe; if the alloca is a scalar and you don't do anything
other than load and store to the alloca, mem2reg will eliminate the
alloca regardless of the control flow. It's a fundamental property of
the algorithm to convert to SSA form; there's a comment in the source
code pointing to the paper in question if you're interested in the
theory.

-Eli