Closed rdeusser closed 3 years ago
Hi @rdeusser!
Glad to see you experimenting with LLVM IR :)
The reason you cannot use ir.Instruction
directly as a value.Value
is because some instruction do not produce result values, and as such do not implement the value.Value
interface. For instance, the store
and the fence
instructions do not implement value.Value
.
You can get around this by defining your own ValueInst
interface, as done for instance in the uc
compiler:
From uc/irgen/irgen.go:132:
// valueInst represents an instruction producing a value.
type valueInst interface {
ir.Instruction
value.Named
}
compiler2.go
Just for clarity, rename local variable from inst
to val
(as it may for instance be a constant literal):
-inst := c.compileExpression(ret.Expression)
-return ir.NewRet(inst) // works now
+val := c.compileExpression(ret.Expression)
+return ir.NewRet(val) // works now
WIth regards to:
val := c.compileExpressionStatement(e)
block.Insts = append(block.Insts, val) // now value.Value can't be used as an ir.Instruction
@sangisos and I took a different approach to emitting IR than the one you've used here. The main idea being that you have emit
functions, which take AST nodes and emit corresponding instructions to the current basic block. They in turn return a "handle" to the value of the latest emitted instruction, but since the emit
functions themselves handle appending instructions to the current basic block, there is no need to append it from the caller function, as you've done on line 11 in compiler2.go
.
It's probably easier to look at an example of the code than trying to grasp the explanation I just wrote. A good example would be how to lower an if-statement, see uc/irgen/lower.go:263. Included below for reference.
// ifStmt lowers the given if statement to LLVM IR, emitting code to f.
func (m *Module) ifStmt(f *Func, stmt *ast.IfStmt) {
cond := m.cond(f, stmt.Cond)
trueBranch := f.NewBlock("")
end := f.NewBlock("")
falseBranch := end
if stmt.Else != nil {
falseBranch = f.NewBlock("")
}
termCondBr := ir.NewCondBr(cond, trueBranch.Block, falseBranch.Block)
f.curBlock.SetTerm(termCondBr)
f.curBlock = trueBranch
m.stmt(f, stmt.Body)
// Emit jump if body doesn't end with return statement (i.e. the current
// basic block is none nil).
if f.curBlock != nil {
termBr := ir.NewBr(end.Block)
f.curBlock.SetTerm(termBr)
}
if stmt.Else != nil {
f.curBlock = falseBranch
m.stmt(f, stmt.Else)
// Emit jump if body doesn't end with return statement (i.e. the current
// basic block is none nil).
if f.curBlock != nil {
termBr := ir.NewBr(end.Block)
f.curBlock.SetTerm(termBr)
}
}
f.curBlock = end
}
And for further reference, see lowering of expressions in uc/irgen/lower.go:356:
// expr lowers the given expression to LLVM IR, emitting code to f.
func (m *Module) expr(f *Func, expr ast.Expr) value.Value {
switch expr := expr.(type) {
case *ast.BasicLit:
return m.basicLit(f, expr)
case *ast.BinaryExpr:
return m.binaryExpr(f, expr)
case *ast.CallExpr:
return m.callExpr(f, expr)
case *ast.Ident:
return m.identUse(f, expr)
case *ast.IndexExpr:
return m.indexExprUse(f, expr)
case *ast.ParenExpr:
return m.expr(f, expr.X)
case *ast.UnaryExpr:
return m.unaryExpr(f, expr)
default:
panic(fmt.Sprintf("support for type %T not yet implemented", expr))
}
}
Note, neither @sangisos nor I have looked at the code of the uc
compiler for quite some time, so there may very well be better ways to do this. However, you can perhaps use it to get some inspiration at least.
Wish you all the best and happy hacking!
Cheers, Robin
Hi @mewmew!
This helps a lot. Thanks!
Hi @mewmew!
This helps a lot. Thanks!
@rdeusser glad to help :)
If you decide to open source your work later on, feel free to share a link to the repo with us! Would be happy to see what you're working on, and should you wish, also add a link to your repo on the Users list; so other can take inspiration from the examples.
Cheers, Robin
This gist (with comments) illustrates the issue I'm having: https://gist.github.com/rdeusser/26273ba769e8290ffdcb8841b69206f0
It feels like what I need to do is somehow get a
value.Value
either into anir.Instruction
or a slice of them. I must be doing something wrong if I'm thinking this way. Any help would be much appreciated.