Skip to content

Commit

Permalink
irgen: Add support for identifier expressions.
Browse files Browse the repository at this point in the history
  • Loading branch information
mewmew committed Jun 14, 2016
1 parent 12e2373 commit 329c4fa
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 29 deletions.
8 changes: 4 additions & 4 deletions irgen/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type Function struct {
// Current basic block being generated.
curBlock *BasicBlock
// Local variables.
local map[string]value.Value
locals map[string]value.Value
}

// NewFunction returns a new function generator based on the given function name
Expand All @@ -59,7 +59,7 @@ type Function struct {
// The caller is responsible for initializing basic blocks.
func NewFunction(name string, sig *irtypes.Func) *Function {
f := ir.NewFunction(name, sig)
return &Function{Function: f, local: make(map[string]value.Value)}
return &Function{Function: f, locals: make(map[string]value.Value)}
}

// startBody initializes the generation of the function body.
Expand All @@ -73,7 +73,7 @@ func (f *Function) endBody() error {
if block := f.curBlock; block != nil && block.Term() == nil {
// Add void return terminator to the current basic block, if a terminator
// is missing.
if result := f.Type().Result(); !irtypes.IsVoid(result) {
if result := f.Sig().Result(); !irtypes.IsVoid(result) {
panic(fmt.Sprintf("unable to finalize current basic block of function body; expected void return since terminator was missing, got %v", result))
}
term, err := instruction.NewRet(irtypes.NewVoid(), nil)
Expand Down Expand Up @@ -124,6 +124,6 @@ func (b *BasicBlock) emitInst(inst instruction.ValueInst) value.Value {
func (b *BasicBlock) emitLocal(name string, inst instruction.ValueInst) value.Value {
def := instruction.NewLocalVarDef(name, inst)
b.AppendInst(def)
b.parent.local[name] = def
b.parent.locals[name] = def
return def
}
4 changes: 4 additions & 0 deletions irgen/irgen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ func TestGen(t *testing.T) {
path: "../testdata/extra/irgen/int_ret.c",
want: "../testdata/extra/irgen/int_ret.ll",
},
{
path: "../testdata/extra/irgen/expr_ret.c",
want: "../testdata/extra/irgen/expr_ret.ll",
},
// Local variable declarations.
{
path: "../testdata/extra/irgen/local_def.c",
Expand Down
74 changes: 49 additions & 25 deletions irgen/lower.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func (m *Module) typeDef(def *ast.TypeDef) {
func (m *Module) localVarDef(f *Function, n *ast.VarDecl) {
// Input:
// void f() {
// int a;
// int a; // <-- relevant line
// }
// Output:
// %a = alloca i32
Expand Down Expand Up @@ -215,7 +215,7 @@ func (m *Module) ifStmt(f *Function, stmt *ast.IfStmt) {
func (m *Module) returnStmt(f *Function, stmt *ast.ReturnStmt) {
// Input:
// int f() {
// return 42;
// return 42; // <-- relevant line
// }
// Output:
// ret i32 42
Expand All @@ -228,7 +228,7 @@ func (m *Module) returnStmt(f *Function, stmt *ast.ReturnStmt) {
f.curBlock = nil
return
}
result := m.expr(stmt.Result)
result := m.expr(f, stmt.Result)
term, err := instruction.NewRet(result.Type(), result)
if err != nil {
panic(fmt.Sprintf("unable to create ret instruction; %v", err))
Expand All @@ -249,36 +249,17 @@ func (m *Module) whileStmt(f *Function, stmt *ast.WhileStmt) {
// (e.g. initializer of global variable definition).

// expr lowers the given expression to LLVM IR, emitting code to f.
func (m *Module) expr(expr ast.Expr) value.Value {
func (m *Module) expr(f *Function, expr ast.Expr) value.Value {
typ := m.typeOf(expr)
switch expr := expr.(type) {
case *ast.BasicLit:
switch expr.Kind {
case token.CharLit:
s, err := strconv.Unquote(expr.Val)
if err != nil {
panic(fmt.Sprintf("unable to unquote character literal; %v", err))
}
val, err := constant.NewInt(typ, strconv.Itoa(int(s[0])))
if err != nil {
panic(fmt.Sprintf("unable to create integer constant; %v", err))
}
return val
case token.IntLit:
val, err := constant.NewInt(typ, expr.Val)
if err != nil {
panic(fmt.Sprintf("unable to create integer constant; %v", err))
}
return val
default:
panic(fmt.Sprintf("support for basic literal kind %v not yet implemented", expr.Kind))
}
return m.basicLit(f, expr, typ)
case *ast.BinaryExpr:
panic(fmt.Sprintf("support for type %T not yet implemented", expr))
case *ast.CallExpr:
panic(fmt.Sprintf("support for type %T not yet implemented", expr))
case *ast.Ident:
panic(fmt.Sprintf("support for type %T not yet implemented", expr))
return m.ident(f, expr, typ)
case *ast.IndexExpr:
panic(fmt.Sprintf("support for type %T not yet implemented", expr))
case *ast.ParenExpr:
Expand All @@ -291,6 +272,49 @@ func (m *Module) expr(expr ast.Expr) value.Value {
panic("unreachable")
}

// basicLit lowers the given basic literal to LLVM IR, emitting code to f.
func (m *Module) basicLit(f *Function, n *ast.BasicLit, typ irtypes.Type) value.Value {
switch n.Kind {
case token.CharLit:
s, err := strconv.Unquote(n.Val)
if err != nil {
panic(fmt.Sprintf("unable to unquote character literal; %v", err))
}
val, err := constant.NewInt(typ, strconv.Itoa(int(s[0])))
if err != nil {
panic(fmt.Sprintf("unable to create integer constant; %v", err))
}
return val
case token.IntLit:
val, err := constant.NewInt(typ, n.Val)
if err != nil {
panic(fmt.Sprintf("unable to create integer constant; %v", err))
}
return val
default:
panic(fmt.Sprintf("support for basic literal kind %v not yet implemented", n.Kind))
}
}

// ident lowers the given identifier to LLVM IR, emitting code to f.
func (m *Module) ident(f *Function, ident *ast.Ident, typ irtypes.Type) value.Value {
// Input:
// void f() {
// int x;
// x; // <-- relevant line
// }
// Output:
// %1 = load i32, i32* %x
addr := f.local(ident.String())
inst, err := instruction.NewLoad(typ, addr)
if err != nil {
panic(fmt.Sprintf("unable to create local instruction; %v", err))
}

// Emit load instruction.
return f.emitLocal("", inst)
}

// constExpr converts the given expression to an LLVM IR constant expression.
func (m *Module) constExpr(expr ast.Expr) constant.Constant {
typ := m.typeOf(expr)
Expand Down
9 changes: 9 additions & 0 deletions irgen/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"

irtypes "github.com/llir/llvm/ir/types"
"github.com/llir/llvm/ir/value"
"github.com/mewkiz/pkg/errutil"
"github.com/mewmew/uc/ast"
uctypes "github.com/mewmew/uc/types"
Expand All @@ -17,6 +18,14 @@ func isTentativeDef(n ast.Decl) bool {
return ident.Start() != def.Start()
}

// getLocal returns the LLVM IR value of the given local variable name.
func (f *Function) local(name string) value.Value {
if v, ok := f.locals[name]; ok {
return v
}
panic(fmt.Sprintf("unable to locate local variable %q", name))
}

// typeOf returns the LLVM IR type of the given expression.
func (m *Module) typeOf(expr ast.Expr) irtypes.Type {
if typ, ok := m.info.Types[expr]; ok {
Expand Down
4 changes: 4 additions & 0 deletions testdata/extra/irgen/expr_ret.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
int f() {
int x;
return x;
}
6 changes: 6 additions & 0 deletions testdata/extra/irgen/expr_ret.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
define i32 @f() {
0:
%x = alloca i32
%1 = load i32, i32* %x
ret i32 %1
}

0 comments on commit 329c4fa

Please sign in to comment.