diff --git a/irgen/irgen.go b/irgen/irgen.go index c759a06..2c91ab3 100644 --- a/irgen/irgen.go +++ b/irgen/irgen.go @@ -478,10 +478,17 @@ func (gen *generator) createVar(n *ast.VarDecl) error { return errutil.Err(err) } } else { + // Ignore tentative definitions. + if isTentativeVarDef(n) { + log.Printf("ignoring tentative global variable definition of %v", n.Name()) + return nil + } // Global values are compile time constant, no need for ssa. - if err := gen.createGlobal(n); err != nil { + global, err := gen.createGlobal(n) + if err != nil { return errutil.Err(err) } + gen.module.Globals = append(gen.module.Globals, global) } return nil } @@ -564,17 +571,76 @@ func (gen *generator) createLocal(n *ast.VarDecl) error { } // createGlobal creates the instructions for a global variable declaration. -func (gen *generator) createGlobal(n *ast.VarDecl) error { - // Ignore tentative definitions. - if isTentativeVarDef(n) { - log.Printf("ignoring tentative global variable definition of %v", n.Name()) - return nil +func (gen *generator) createGlobal(n *ast.VarDecl) (*ir.GlobalDecl, error) { + name := n.Name().Name + log.Printf("create global variable %q", name) + typ := toIrType(n.Type()) + var val value.Value + var err error + switch { + case n.Val != nil: + val, err = gen.createConstant(n.Val) + if err != nil { + return nil, errutil.Err(err) + } + case irtypes.IsInt(typ): + val, err = constant.NewInt(typ, "0") + if err != nil { + return nil, errutil.Err(err) + } + default: + val, err = constant.NewZeroInitializer(typ) + if err != nil { + return nil, errutil.Err(err) + } } - // TODO: Implement - log.Printf("create global variable %v", n) - var gv *ir.GlobalDecl - gen.module.Globals = append(gen.module.Globals, gv) - return nil + global := ir.NewGlobalDef(name, val, false) + return global, nil +} + +// createConstant converts the given uC expression to an LLVM IR constant +// expression. +func (gen *generator) createConstant(expr ast.Expr) (constant.Constant, error) { + typ := gen.typeOf(expr) + switch expr := expr.(type) { + case *ast.BasicLit: + switch expr.Kind { + case token.CharLit: + s, err := strconv.Unquote(expr.Val) + if err != nil { + return nil, errutil.Err(err) + } + val, err := constant.NewInt(typ, s) + if err != nil { + return nil, errutil.Err(err) + } + return val, nil + case token.IntLit: + val, err := constant.NewInt(typ, expr.Val) + if err != nil { + return nil, errutil.Err(err) + } + return val, nil + default: + panic(fmt.Sprintf("support for basic literal kind %v not yet implemented", expr.Kind)) + } + //case *ast.BinaryExpr: + //case *ast.CallExpr: + //case *ast.Ident: + //case *ast.IndexExpr: + //case *ast.ParenExpr: + //case *ast.UnaryExpr: + default: + panic(fmt.Sprintf("support for type %T not yet implemented", expr)) + } +} + +// typeOf returns the LLVM IR type of the given expression. +func (gen *generator) typeOf(expr ast.Expr) irtypes.Type { + if typ, ok := gen.info.Types[expr]; ok { + return toIrType(typ) + } + panic(fmt.Sprintf("unable to locate type for expression %v", expr)) } // isTentative reports whether the given global variable declaration is a diff --git a/irgen/irgen_test.go b/irgen/irgen_test.go index 41b533a..c170cc9 100644 --- a/irgen/irgen_test.go +++ b/irgen/irgen_test.go @@ -24,6 +24,10 @@ func TestGen(t *testing.T) { path: "../testdata/extra/irgen/tentative_def.c", want: "../testdata/extra/irgen/tentative_def.ll", }, + { + path: "../testdata/extra/irgen/function.c", + want: "../testdata/extra/irgen/function.ll", + }, } for _, g := range golden { @@ -46,13 +50,14 @@ func TestGen(t *testing.T) { file := f.(*ast.File) // Verify input. - if err := sem.Check(file); err != nil { + info, err := sem.Check(file) + if err != nil { t.Errorf("%q: semantic analysis error: %v", g.path, err) continue } // Generate IR. - module, err := irgen.Gen(file) + module, err := irgen.Gen(file, info) if err != nil { t.Errorf("%q: unable to generate IR: %v", g.path, err) continue diff --git a/testdata/extra/irgen/Makefile b/testdata/extra/irgen/Makefile index 5816e8c..2485928 100644 --- a/testdata/extra/irgen/Makefile +++ b/testdata/extra/irgen/Makefile @@ -2,8 +2,13 @@ CFILES = $(wildcard *.c) LLFILES = $(CFILES:.c=.ll) -all: $(LLFILES) +all: $(LLFILES) post_strip %.ll: %.c clang -S -emit-llvm -o $@ $< ./strip.sh $@ + +post_strip: + ./post_strip.sh + +.PHONY: post_strip diff --git a/testdata/extra/irgen/function.c b/testdata/extra/irgen/function.c new file mode 100644 index 0000000..394d839 --- /dev/null +++ b/testdata/extra/irgen/function.c @@ -0,0 +1,3 @@ +int main() { + return 42; +} diff --git a/testdata/extra/irgen/function.ll b/testdata/extra/irgen/function.ll new file mode 100644 index 0000000..d6275ff --- /dev/null +++ b/testdata/extra/irgen/function.ll @@ -0,0 +1,4 @@ +define i32 @main() { +0: + ret i32 42 +}