From 8b4c5a061a0b169a3e893551dbb2d357fdefe1a2 Mon Sep 17 00:00:00 2001 From: MineGame159 Date: Sat, 13 Jan 2024 22:47:41 +0100 Subject: [PATCH] CORE: Rewrite LLVM abstraction --- cmd/cmd/build.go | 46 ++- core/codegen/codegen.go | 521 ++++++++------------------ core/codegen/declarations.go | 57 ++- core/codegen/expressions.go | 684 +++++++++++++++++++++++------------ core/codegen/instructions.go | 38 ++ core/codegen/scopes.go | 197 ++++++++++ core/codegen/statements.go | 82 +++-- core/codegen/types.go | 411 +++++++++++++++++++++ core/ir/block.go | 26 ++ core/ir/constants.go | 103 ++++++ core/ir/function.go | 45 +++ core/ir/global_var.go | 38 ++ core/ir/helpers.go | 43 +++ core/ir/instructions.go | 474 ++++++++++++++++++++++++ core/ir/metadata.go | 430 ++++++++++++++++++++++ core/ir/module.go | 91 +++++ core/ir/types.go | 99 +++++ core/ir/value.go | 19 + core/llvm/constants.go | 73 ++++ core/llvm/function.go | 444 ++++------------------- core/llvm/globals.go | 64 ++++ core/llvm/instructions.go | 589 ++++++++++++++++++++---------- core/llvm/main.go | 31 ++ core/llvm/metadata.go | 458 ++++++++++++++++++----- core/llvm/module.go | 525 --------------------------- core/llvm/text.go | 661 --------------------------------- core/llvm/types.go | 165 ++++----- core/llvm/value.go | 176 +++++++-- core/llvm/writer.go | 66 ++++ 29 files changed, 4002 insertions(+), 2654 deletions(-) create mode 100644 core/codegen/instructions.go create mode 100644 core/codegen/scopes.go create mode 100644 core/codegen/types.go create mode 100644 core/ir/block.go create mode 100644 core/ir/constants.go create mode 100644 core/ir/function.go create mode 100644 core/ir/global_var.go create mode 100644 core/ir/helpers.go create mode 100644 core/ir/instructions.go create mode 100644 core/ir/metadata.go create mode 100644 core/ir/module.go create mode 100644 core/ir/types.go create mode 100644 core/ir/value.go create mode 100644 core/llvm/constants.go create mode 100644 core/llvm/globals.go create mode 100644 core/llvm/main.go delete mode 100644 core/llvm/module.go delete mode 100644 core/llvm/text.go create mode 100644 core/llvm/writer.go diff --git a/cmd/cmd/build.go b/cmd/cmd/build.go index 1819ab8..b39eb3a 100644 --- a/cmd/cmd/build.go +++ b/cmd/cmd/build.go @@ -4,6 +4,7 @@ import ( "fireball/cmd/build" "fireball/core/ast" "fireball/core/codegen" + "fireball/core/ir" "fireball/core/llvm" "fireball/core/utils" "fireball/core/workspace" @@ -87,7 +88,10 @@ func buildProject() string { path = filepath.Join(project.Path, "build", path[:len(path)-3]+".ll") irFile, _ := os.Create(path) - codegen.Emit(file.AbsolutePath(), project.GetResolverFile(file.Ast), file.Ast, irFile) + + module := codegen.Emit(file.AbsolutePath(), project.GetResolverFile(file.Ast), file.Ast) + llvm.WriteText(module, irFile) + _ = irFile.Close() irPaths = append(irPaths, path) @@ -145,41 +149,43 @@ func buildProject() string { func generateEntrypoint(project *workspace.Project, path string) error { // Create module - m := llvm.NewModule() - m.Source("__entrypoint") + m := &ir.Module{Path: "__entrypoint"} resolver := project.GetResolverName(project.Config.Namespace) function := resolver.GetFunction("main") - void := m.Void() - i32 := m.Primitive("i32", 32, llvm.SignedEncoding) - - main := m.Define(m.Function("main", []llvm.Type{}, false, i32), "_fireball_entrypoint") - main.PushScope() + main := m.Define("main", &ir.FuncType{Returns: ir.I32}, 0) mainBlock := main.Block("") + // Flags + if function != nil { - var fbMain llvm.Value + var fbMain ir.Value if ast.IsPrimitive(function.Returns, ast.I32) { - fbMain = m.Declare(m.Function(function.MangledName(), []llvm.Type{}, false, i32)) + fbMain = m.Declare(function.MangledName(), &ir.FuncType{Returns: ir.I32}) } else { - fbMain = m.Declare(m.Function(function.MangledName(), []llvm.Type{}, false, void)) + fbMain = m.Declare(function.MangledName(), &ir.FuncType{}) } - if ast.IsPrimitive(function.Returns, ast.I32) { - call := mainBlock.Call(fbMain, []llvm.Value{}, i32) - call.SetLocation(nil) + call := mainBlock.Add(&ir.CallInst{ + Callee: fbMain, + Args: nil, + }) - mainBlock.Ret(call) + if ast.IsPrimitive(function.Returns, ast.I32) { + mainBlock.Add(&ir.RetInst{Value: call}) } else { - call := mainBlock.Call(fbMain, []llvm.Value{}, void) - call.SetLocation(nil) - - mainBlock.Ret(main.Literal(i32, llvm.Literal{Signed: 0})) + mainBlock.Add(&ir.RetInst{Value: &ir.IntConst{ + Typ: ir.I32, + Value: ir.Unsigned(0), + }}) } } else { - mainBlock.Ret(main.Literal(i32, llvm.Literal{Signed: 0})) + mainBlock.Add(&ir.RetInst{Value: &ir.IntConst{ + Typ: ir.I32, + Value: ir.Unsigned(0), + }}) } // Write module diff --git a/core/codegen/codegen.go b/core/codegen/codegen.go index 703484c..449dfc5 100644 --- a/core/codegen/codegen.go +++ b/core/codegen/codegen.go @@ -1,73 +1,59 @@ package codegen import ( - "fireball/core/architecture" "fireball/core/ast" - "fireball/core/llvm" - "fireball/core/scanner" - "io" + "fireball/core/ir" + "fmt" ) type codegen struct { path string resolver *ast.CombinedResolver - types []typePair + types types + scopes scopes staticVariables map[ast.Node]exprValue - functions map[*ast.Func]llvm.Value - - scopes []scope - variables []variable + functions map[*ast.Func]*ir.Func allocas map[ast.Node]exprValue - function *llvm.Function - block *llvm.Block + function *ir.Func + block *ir.Block - loopStart *llvm.Block - loopEnd *llvm.Block + loopStart *ir.Block + loopEnd *ir.Block exprResult exprValue this exprValue - module *llvm.Module -} - -type typePair struct { - fireball ast.Type - llvm llvm.Type -} - -type scope struct { - variableI int - variableCount int + module *ir.Module } type exprValue struct { - v llvm.Value + v ir.Value addressable bool } -type variable struct { - name ast.Node - value exprValue -} - -func Emit(path string, root ast.RootResolver, file *ast.File, writer io.Writer) { +func Emit(path string, root ast.RootResolver, file *ast.File) *ir.Module { // Init codegen c := &codegen{ path: path, resolver: ast.NewCombinedResolver(root), staticVariables: make(map[ast.Node]exprValue), - functions: make(map[*ast.Func]llvm.Value), + functions: make(map[*ast.Func]*ir.Func), - module: llvm.NewModule(), + module: &ir.Module{}, } - // File metadata - c.module.Source(path) + // File + c.module.Path = path + + c.types.module = c.module + c.scopes.c = c + + c.scopes.pushFile(path) // Find some declarations for _, decl := range file.Decls { @@ -100,56 +86,95 @@ func Emit(path string, root ast.RootResolver, file *ast.File, writer io.Writer) c.acceptDecl(decl) } - // Write - llvm.WriteText(c.module, writer) + // Return + return c.module } func (c *codegen) defineOrDeclare(function *ast.Func) { - t := c.getType(function) + t := c.types.get(function).(*ir.FuncType) + name := c.getMangledName(function) if function.HasBody() { - // Define - this := function.Method() + // Meta + meta := &ir.SubprogamMeta{ + Name: function.Name.String(), + LinkageName: name, + Scope: c.scopes.getMeta(), + File: c.scopes.file, + Type: c.types.getMeta(function), + Unit: c.scopes.unitId, + } - f := c.module.Define(t, function.MangledName()[3:]) - c.functions[function] = f + if function.Cst() != nil { + meta.Line = uint32(function.Cst().Range.Start.Line) + } + + // Define + var flags ir.FuncFlags - // Inline for _, attribute := range function.Attributes { if attribute.Name.String() == "Inline" { - f.SetAlwaysInline() + flags |= ir.InlineFlag break } } - // Set parameter names - if this != nil { - f.GetParameter(0).SetName("this") - } - - for i, param := range function.Params { - index := i - if this != nil { - index++ - } + f := c.module.Define(name, t, flags) + f.SetMeta(c.module.Meta(meta)) - f.GetParameter(index).SetName(param.Name.String()) - } + c.functions[function] = f } else { // Declare - c.functions[function] = c.module.Declare(t) + c.functions[function] = c.module.Declare(name, t) + } +} + +func (c *codegen) getMangledName(function *ast.Func) string { + intrinsicName := function.IntrinsicName() + + if intrinsicName != "" { + name := "" + + switch intrinsicName { + case "abs": + name = ternary(isFloating(function.Returns), "llvm.fabs", "llvm.abs") + + case "min": + name = ternary(isFloating(function.Returns), "llvm.minnum", ternary(isSigned(function.Returns), "llvm.smin", "llvm.umin")) + + case "max": + name = ternary(isFloating(function.Returns), "llvm.maxnum", ternary(isSigned(function.Returns), "llvm.smax", "llvm.umax")) + + case "sqrt", "pow", "sin", "cos", "exp", "exp2", "exp10", "log", "log2", "log10", "fma", "copysign", "floor", "ceil", "round": + name = "llvm." + intrinsicName + + case "memcpy", "memmove": + return "llvm.memcpy.p0.p0.i32" + + case "memset": + return "llvm.memset.p0.i32" + } + + if name == "" { + panic("codegen getMangledName() - Invalid intrinsic") + } + + return name + "." + function.Returns.String() } + + return function.MangledName() } // IR func (c *codegen) load(value exprValue, type_ ast.Type) exprValue { if value.addressable { - load := c.block.Load(value.v) - load.SetAlign(type_.Align()) - return exprValue{ - v: load, + v: c.block.Add(&ir.LoadInst{ + Typ: value.v.Type().(*ir.PointerType).Pointee, + Pointer: value.v, + Align: type_.Align() * 8, + }), addressable: false, } } @@ -184,13 +209,38 @@ func (c *codegen) getGlobalVariable(variable *ast.GlobalVar) exprValue { } func (c *codegen) createStaticVariable(field *ast.Field, external bool) exprValue { - ptr := ast.Pointer{Pointee: field.Type} + type_ := c.types.get(field.Type) + var initializer ir.Value - llvmValue := c.module.Variable(external, c.getType(field.Type), c.getType(&ptr)) - llvmValue.SetName(field.MangledName()) + if !external { + initializer = &ir.ZeroInitConst{Typ: type_} + } + + global := c.module.Global(field.MangledName(), type_, initializer) + + if !external { + meta := &ir.GlobalVarMeta{ + Name: fmt.Sprintf("%s.%s", field.Parent().(*ast.Struct).Name, field.Name), + LinkageName: field.MangledName(), + Scope: c.scopes.getMeta(), + File: c.scopes.file, + Type: c.types.getMeta(field.Type), + Local: false, + Definition: true, + } + + if field.Cst() != nil { + meta.Line = uint32(field.Cst().Range.Start.Line) + } + + id := c.module.Meta(&ir.GlobalVarExpr{Var: c.module.Meta(meta)}) + + global.SetMeta(id) + c.scopes.unitMeta.Globals = append(c.scopes.unitMeta.Globals, id) + } value := exprValue{ - v: llvmValue, + v: global, addressable: true, } @@ -199,13 +249,38 @@ func (c *codegen) createStaticVariable(field *ast.Field, external bool) exprValu } func (c *codegen) createGlobalVariable(variable *ast.GlobalVar, external bool) exprValue { - ptr := ast.Pointer{Pointee: variable.Type} + type_ := c.types.get(variable.Type) + var initializer ir.Value - llvmValue := c.module.Variable(external, c.getType(variable.Type), c.getType(&ptr)) - llvmValue.SetName(variable.MangledName()) + if !external { + initializer = &ir.ZeroInitConst{Typ: type_} + } + + global := c.module.Global(variable.MangledName(), type_, initializer) + + if !external { + meta := &ir.GlobalVarMeta{ + Name: variable.Name.String(), + LinkageName: variable.MangledName(), + Scope: c.scopes.getMeta(), + File: c.scopes.file, + Type: c.types.getMeta(variable.Type), + Local: false, + Definition: true, + } + + if variable.Cst() != nil { + meta.Line = uint32(variable.Cst().Range.Start.Line) + } + + id := c.module.Meta(&ir.GlobalVarExpr{Var: c.module.Meta(meta)}) + + global.SetMeta(id) + c.scopes.unitMeta.Globals = append(c.scopes.unitMeta.Globals, id) + } value := exprValue{ - v: llvmValue, + v: global, addressable: true, } @@ -232,13 +307,13 @@ func (c *codegen) getFunction(function *ast.Func) exprValue { } } - value := c.module.Declare(c.getType(function)) + value := c.module.Declare(c.getMangledName(function), c.types.get(function).(*ir.FuncType)) c.functions[function] = value return exprValue{v: value} } -func (c *codegen) beginBlock(block *llvm.Block) { +func (c *codegen) beginBlock(block *ir.Block) { c.block = block } @@ -256,12 +331,8 @@ type allocaFinder struct { func (a *allocaFinder) VisitNode(node ast.Node) { switch node := node.(type) { case *ast.Var: - pointer := a.c.block.Alloca(a.c.getType(node.ActualType)) - pointer.SetName(node.Name.String() + ".var") - pointer.SetAlign(node.ActualType.Align()) - a.c.allocas[node] = exprValue{ - v: pointer, + v: a.c.alloca(node.ActualType, node.Name.String()+".var", node), addressable: true, } @@ -269,11 +340,8 @@ func (a *allocaFinder) VisitNode(node ast.Node) { if callNeedsTempVariable(node) { returns := node.Callee.Result().Type.(*ast.Func).Returns - pointer := a.c.block.Alloca(a.c.getType(returns)) - pointer.SetAlign(returns.Align()) - a.c.allocas[node] = exprValue{ - v: pointer, + v: a.c.alloca(returns, "", node), addressable: true, } } @@ -283,11 +351,8 @@ func (a *allocaFinder) VisitNode(node ast.Node) { type_ := node.Value.Result().Type if type_ != nil { - pointer := a.c.block.Alloca(a.c.getType(type_)) - pointer.SetAlign(type_.Align()) - a.c.allocas[node] = exprValue{ - v: pointer, + v: a.c.alloca(type_, "", node), addressable: true, } } @@ -315,247 +380,20 @@ func callNeedsTempVariable(expr *ast.Call) bool { return false } -// Types - -func (c *codegen) getType(type_ ast.Type) llvm.Type { - type_ = type_.Resolved() - - // Check cache - for _, pair := range c.types { - if pair.fireball.Equals(type_) { - return pair.llvm - } - } - - // Create type - var llvmType llvm.Type - - if v, ok := ast.As[*ast.Primitive](type_); ok { - // Primitive - switch v.Kind { - case ast.Void: - llvmType = c.module.Void() - case ast.Bool: - llvmType = c.module.Primitive("bool", 8, llvm.BooleanEncoding) - - case ast.U8: - llvmType = c.module.Primitive("u8", 8, llvm.UnsignedEncoding) - case ast.U16: - llvmType = c.module.Primitive("u16", 16, llvm.UnsignedEncoding) - case ast.U32: - llvmType = c.module.Primitive("u32", 32, llvm.UnsignedEncoding) - case ast.U64: - llvmType = c.module.Primitive("u64", 64, llvm.UnsignedEncoding) - - case ast.I8: - llvmType = c.module.Primitive("i8", 8, llvm.SignedEncoding) - case ast.I16: - llvmType = c.module.Primitive("i16", 16, llvm.SignedEncoding) - case ast.I32: - llvmType = c.module.Primitive("i32", 32, llvm.SignedEncoding) - case ast.I64: - llvmType = c.module.Primitive("i64", 64, llvm.SignedEncoding) - - case ast.F32: - llvmType = c.module.Primitive("f32", 32, llvm.FloatEncoding) - case ast.F64: - llvmType = c.module.Primitive("f64", 64, llvm.FloatEncoding) - - default: - panic("codegen.getType() - Not implemented") - } - } else if v, ok := ast.As[*ast.Array](type_); ok { - // Array - llvmType = c.module.Array(v.String(), v.Count, c.getType(v.Base)) - } else if v, ok := ast.As[*ast.Pointer](type_); ok { - // Pointer - llvmType = c.module.Pointer(v.String(), c.getType(v.Pointee)) - } else if v, ok := ast.As[*ast.Func](type_); ok { - // Function - var parameters []llvm.Type - var returns llvm.Type - - intrinsicName := v.IntrinsicName() - - if intrinsicName != "" { - intrinsic := c.getIntrinsic(v, intrinsicName) - - parameters = intrinsic[1:] - returns = intrinsic[0] - } else { - this := v.Method() - - parameterCount := len(v.Params) - if this != nil { - parameterCount++ - } - - parameters = make([]llvm.Type, parameterCount) - - if this != nil { - type_ := ast.Pointer{Pointee: this} - parameters[0] = c.getType(&type_) - } - - for i, param := range v.Params { - index := i - if this != nil { - index++ - } - - parameters[index] = c.getType(param.Type) - } - - returns = c.getType(v.Returns) - } - - llvmType = c.module.Function(getMangledName(v), parameters, v.IsVariadic(), returns) - } else if v, ok := ast.As[*ast.Struct](type_); ok { - // Struct - layout := architecture.CLayout{} - fields := make([]llvm.Field, len(v.Fields)) - - for i, field := range v.Fields { - offset := layout.Add(field.Type.Size(), field.Type.Align()) - - fields[i] = llvm.Field{ - Name: field.Name.String(), - Type: c.getType(field.Type), - Offset: offset * 8, - } - } - - llvmType = c.module.Struct(v.Name.String(), layout.Size()*8, fields) - } else if v, ok := ast.As[*ast.Enum](type_); ok { - // Enum - llvmType = c.module.Alias(v.Name.String(), c.getType(v.ActualType)) - } - - if llvmType != nil { - c.types = append(c.types, typePair{ - fireball: type_, - llvm: llvmType, - }) - - return llvmType - } - - panic("codegen.getType() - Invalid type") -} - -func (c *codegen) getIntrinsic(function *ast.Func, intrinsicName string) []llvm.Type { - param := c.getType(function.Params[0].Type) - - switch intrinsicName { - case "abs": - if isFloating(function.Params[0].Type) { - return []llvm.Type{ - param, - param, - } - } else { - i1 := c.getPrimitiveType(ast.Bool) - - return []llvm.Type{ - param, - param, - i1, - } - } - - case "pow", "min", "max", "copysign": - return []llvm.Type{param, param, param} - - case "sqrt", "sin", "cos", "exp", "exp2", "exp10", "log", "log2", "log10", "floor", "ceil", "round": - return []llvm.Type{param, param} - - case "fma": - return []llvm.Type{param, param, param, param} - - case "memcpy", "memmove": - void := c.getPrimitiveType(ast.Void) - i1 := c.getPrimitiveType(ast.Bool) - i32 := c.getPrimitiveType(ast.I32) - - return []llvm.Type{ - void, - param, - param, - i32, - i1, - } - - case "memset": - void := c.getPrimitiveType(ast.Void) - i1 := c.getPrimitiveType(ast.Bool) - i8 := c.getPrimitiveType(ast.I8) - i32 := c.getPrimitiveType(ast.I32) - - return []llvm.Type{ - void, - param, - i8, - i32, - i1, - } - - default: - panic("codegen.getIntrinsic() - Invalid intrinsic") - } -} - -func (c *codegen) modifyIntrinsicArgs(function *ast.Func, intrinsicName string, args []llvm.Value) []llvm.Value { +func (c *codegen) modifyIntrinsicArgs(function *ast.Func, intrinsicName string, args []ir.Value) []ir.Value { switch intrinsicName { case "abs": if !isFloating(function.Returns) { - i1 := c.getPrimitiveType(ast.Bool) - args = append(args, c.function.Literal(i1, llvm.Literal{})) + args = append(args, ir.False) } case "memcpy", "memmove", "memset": - i1 := c.getPrimitiveType(ast.Bool) - args = append(args, c.function.Literal(i1, llvm.Literal{})) + args = append(args, ir.False) } return args } -func getMangledName(function *ast.Func) string { - intrinsicName := function.IntrinsicName() - - if intrinsicName != "" { - name := "" - - switch intrinsicName { - case "abs": - name = ternary(isFloating(function.Returns), "llvm.fabs", "llvm.abs") - - case "min": - name = ternary(isFloating(function.Returns), "llvm.minnum", ternary(isSigned(function.Returns), "llvm.smin", "llvm.umin")) - - case "max": - name = ternary(isFloating(function.Returns), "llvm.maxnum", ternary(isSigned(function.Returns), "llvm.smax", "llvm.umax")) - - case "sqrt", "pow", "sin", "cos", "exp", "exp2", "exp10", "log", "log2", "log10", "fma", "copysign", "floor", "ceil", "round": - name = "llvm." + intrinsicName - - case "memcpy", "memmove": - return "llvm.memcpy.p0.p0.i32" - - case "memset": - return "llvm.memset.p0.i32" - } - - if name == "" { - panic("codegen getMangledName() - Invalid intrinsic") - } - - return name + "." + function.Returns.String() - } - - return function.MangledName() -} - func isSigned(type_ ast.Type) bool { if v, ok := ast.As[*ast.Primitive](type_); ok { return ast.IsSigned(v.Kind) @@ -580,53 +418,6 @@ func ternary[T any](condition bool, true T, false T) T { return false } -func (c *codegen) getPrimitiveType(kind ast.PrimitiveKind) llvm.Type { - type_ := ast.Primitive{Kind: kind} - return c.getType(&type_) -} - -// Scope / Variables - -func (c *codegen) getVariable(name scanner.Token) *variable { - for i := len(c.variables) - 1; i >= 0; i-- { - if c.variables[i].name.String() == name.Lexeme { - return &c.variables[i] - } - } - - return nil -} - -func (c *codegen) addVariable(name ast.Node, value exprValue) *variable { - c.block.Variable(name.String(), value.v).SetLocation(name.Cst()) - - value.addressable = true - - c.variables = append(c.variables, variable{ - name: name, - value: value, - }) - - c.peekScope().variableCount++ - return &c.variables[len(c.variables)-1] -} - -func (c *codegen) pushScope() { - c.scopes = append(c.scopes, scope{ - variableI: len(c.variables), - variableCount: 0, - }) -} - -func (c *codegen) popScope() { - c.variables = c.variables[:c.peekScope().variableI] - c.scopes = c.scopes[:len(c.scopes)-1] -} - -func (c *codegen) peekScope() *scope { - return &c.scopes[len(c.scopes)-1] -} - // Accept func (c *codegen) acceptDecl(decl ast.Decl) { @@ -635,14 +426,16 @@ func (c *codegen) acceptDecl(decl ast.Decl) { } } -func (c *codegen) acceptStmt(stmt ast.Stmt) { - if stmt != nil { +func (c *codegen) acceptStmt(stmt ast.Stmt) bool { + if stmt != nil && c.block != nil { stmt.AcceptStmt(c) } + + return c.block != nil } func (c *codegen) acceptExpr(expr ast.Expr) exprValue { - if expr != nil { + if expr != nil && c.block != nil { expr.AcceptExpr(c) return c.exprResult } diff --git a/core/codegen/declarations.go b/core/codegen/declarations.go index 6cf6de3..e74ce87 100644 --- a/core/codegen/declarations.go +++ b/core/codegen/declarations.go @@ -2,18 +2,15 @@ package codegen import ( "fireball/core/ast" - "fireball/core/llvm" + "fireball/core/ir" "fireball/core/scanner" ) -func (c *codegen) VisitNamespace(_ *ast.Namespace) { -} +func (c *codegen) VisitNamespace(_ *ast.Namespace) {} -func (c *codegen) VisitUsing(_ *ast.Using) { -} +func (c *codegen) VisitUsing(_ *ast.Using) {} -func (c *codegen) VisitStruct(_ *ast.Struct) { -} +func (c *codegen) VisitStruct(_ *ast.Struct) {} func (c *codegen) VisitImpl(impl *ast.Impl) { for _, function := range impl.Methods { @@ -21,34 +18,28 @@ func (c *codegen) VisitImpl(impl *ast.Impl) { } } -func (c *codegen) VisitEnum(_ *ast.Enum) { -} +func (c *codegen) VisitEnum(_ *ast.Enum) {} func (c *codegen) VisitFunc(decl *ast.Func) { - // Get function - var function *llvm.Function - - if f, ok := c.functions[decl]; ok { - if fu, ok := f.(*llvm.Function); ok { - function = fu - } - } - - if function == nil { + if !decl.HasBody() { return } + // Get function + function := c.functions[decl] + // Setup state c.function = function c.beginBlock(function.Block("entry")) - c.pushScope() - function.PushScope() + c.scopes.push(function.Meta()) // Add this variable if struct_ := decl.Method(); struct_ != nil { name := scanner.Token{Kind: scanner.Identifier, Lexeme: "this"} - c.addVariable(&ast.Token{Token_: name}, exprValue{v: function.GetParameter(0)}) + type_ := ast.Pointer{Pointee: struct_} + + c.scopes.addVariable(&ast.Token{Token_: name}, &type_, exprValue{v: function.Typ.Params[0]}, 1) } // Copy parameters @@ -58,14 +49,15 @@ func (c *codegen) VisitFunc(decl *ast.Func) { index++ } - pointer := c.block.Alloca(c.getType(param.Type)) - pointer.SetName(param.Name.String() + ".var") - pointer.SetAlign(param.Type.Align()) + pointer := c.alloca(param.Type, param.Name.String()+".var", param) - store := c.block.Store(pointer, function.GetParameter(index)) - store.SetAlign(param.Type.Align()) + c.block.Add(&ir.StoreInst{ + Pointer: pointer, + Value: function.Typ.Params[index], + Align: param.Type.Align() * 8, + }) - c.addVariable(param.Name, exprValue{v: pointer}) + c.scopes.addVariable(param.Name, param.Type, exprValue{v: pointer}, uint32(index+1)) } // Body @@ -77,17 +69,14 @@ func (c *codegen) VisitFunc(decl *ast.Func) { // Add return if needed if ast.IsPrimitive(decl.Returns, ast.Void) { - c.block.Ret(nil) + c.block.Add(&ir.RetInst{}) } // Reset state - function.PopScope() - c.popScope() + c.scopes.pop() c.block = nil c.function = nil } -func (c *codegen) VisitGlobalVar(decl *ast.GlobalVar) { - -} +func (c *codegen) VisitGlobalVar(_ *ast.GlobalVar) {} diff --git a/core/codegen/expressions.go b/core/codegen/expressions.go index 7aaad1b..2aba2d3 100644 --- a/core/codegen/expressions.go +++ b/core/codegen/expressions.go @@ -2,8 +2,7 @@ package codegen import ( "fireball/core/ast" - "fireball/core/cst" - "fireball/core/llvm" + "fireball/core/ir" "fireball/core/scanner" "log" "strconv" @@ -16,15 +15,18 @@ func (c *codegen) VisitParen(expr *ast.Paren) { func (c *codegen) VisitLiteral(expr *ast.Literal) { // Convert fireball constant into a LLVM IR constant - var value llvm.Value - type_ := c.getType(expr.Result().Type) + var value ir.Value + type_ := c.types.get(expr.Result().Type) switch expr.Token().Kind { case scanner.Nil: - value = c.function.LiteralRaw(type_, "null") + value = ir.Null - case scanner.True, scanner.False: - value = c.function.LiteralRaw(type_, expr.String()) + case scanner.True: + value = &ir.IntConst{Typ: type_, Value: ir.Unsigned(1)} + + case scanner.False: + value = &ir.IntConst{Typ: type_, Value: ir.Unsigned(0)} case scanner.Number: raw := expr.String() @@ -32,29 +34,29 @@ func (c *codegen) VisitLiteral(expr *ast.Literal) { if last == 'f' || last == 'F' { v, _ := strconv.ParseFloat(raw[:len(raw)-1], 32) - value = c.function.Literal(type_, llvm.Literal{Floating: v}) + value = &ir.FloatConst{Typ: type_, Value: v} } else if strings.ContainsRune(raw, '.') { v, _ := strconv.ParseFloat(raw, 64) - value = c.function.Literal(type_, llvm.Literal{Floating: v}) + value = &ir.FloatConst{Typ: type_, Value: v} } else { t, _ := ast.As[*ast.Primitive](expr.Result().Type) if ast.IsSigned(t.Kind) { v, _ := strconv.ParseInt(raw, 10, 64) - value = c.function.Literal(type_, llvm.Literal{Signed: v}) + value = &ir.IntConst{Typ: type_, Value: ir.Signed(v)} } else { v, _ := strconv.ParseUint(raw, 10, 64) - value = c.function.Literal(type_, llvm.Literal{Unsigned: v}) + value = &ir.IntConst{Typ: type_, Value: ir.Unsigned(v)} } } case scanner.Hex: v, _ := strconv.ParseUint(expr.String()[2:], 16, 64) - value = c.function.Literal(type_, llvm.Literal{Unsigned: v}) + value = &ir.IntConst{Typ: type_, Value: ir.Unsigned(v)} case scanner.Binary: v, _ := strconv.ParseUint(expr.String()[2:], 2, 64) - value = c.function.Literal(type_, llvm.Literal{Unsigned: v}) + value = &ir.IntConst{Typ: type_, Value: ir.Unsigned(v)} case scanner.Character: char := expr.String()[1 : len(expr.String())-1] @@ -77,10 +79,10 @@ func (c *codegen) VisitLiteral(expr *ast.Literal) { number = char[0] } - value = c.function.Literal(type_, llvm.Literal{Unsigned: uint64(number)}) + value = &ir.IntConst{Typ: type_, Value: ir.Unsigned(uint64(number))} case scanner.String: - value = c.module.Constant(expr.String()[1 : len(expr.String())-1]) + value = c.module.Constant("", convertString(expr.String()[1:len(expr.String())-1])) default: panic("codegen.VisitLiteral() - Invalid literal kind") @@ -92,20 +94,79 @@ func (c *codegen) VisitLiteral(expr *ast.Literal) { } } +func convertString(s string) *ir.StringConst { + b := &ir.StringConst{ + Length: 0, + Value: make([]byte, 0, len(s)), + } + + for i := 0; i < len(s); i++ { + ch := s[i] + + switch ch { + case '\\': + if i+1 >= len(s) { + break + } + + switch s[i+1] { + case '0': + b.Value = append(b.Value, '\\') + b.Value = append(b.Value, '0') + b.Value = append(b.Value, '0') + + case 'n': + b.Value = append(b.Value, '\\') + b.Value = append(b.Value, '0') + b.Value = append(b.Value, 'A') + + case 'r': + b.Value = append(b.Value, '\\') + b.Value = append(b.Value, '0') + b.Value = append(b.Value, 'D') + + case 't': + b.Value = append(b.Value, '\\') + b.Value = append(b.Value, '0') + b.Value = append(b.Value, '9') + } + + i++ + + default: + b.Value = append(b.Value, ch) + } + + b.Length++ + } + + b.Length++ + + b.Value = append(b.Value, '\\') + b.Value = append(b.Value, '0') + b.Value = append(b.Value, '0') + + return b +} + func (c *codegen) VisitStructInitializer(expr *ast.StructInitializer) { // Value struct_, _ := ast.As[*ast.Struct](expr.Type) - type_ := c.getType(struct_) + type_ := c.types.get(struct_) - result := c.function.LiteralRaw(type_, "zeroinitializer") + var result ir.Value = &ir.ZeroInitConst{Typ: type_} for _, field := range expr.Fields { element := c.loadExpr(field.Value) i, _ := struct_.GetField(field.Name.String()) - r := c.block.InsertValue(result, element.v, i) - r.SetLocation(field.Name.Cst()) + r := c.block.Add(&ir.InsertValueInst{ + Value: result, + Element: element.v, + Indices: []uint32{uint32(i)}, + }) + c.setLocationMeta(r, field) result = r } @@ -116,32 +177,39 @@ func (c *codegen) VisitStructInitializer(expr *ast.StructInitializer) { mallocFunc := c.resolver.GetFunction("malloc") malloc := c.getFunction(mallocFunc) - pointer := c.block.Call( - malloc.v, - []llvm.Value{c.function.Literal( - c.getType(mallocFunc.Params[0].Type), - llvm.Literal{Unsigned: uint64(struct_.Size())}, - )}, - c.getType(mallocFunc.Returns), - ) + pointer := c.block.Add(&ir.CallInst{ + Callee: malloc.v, + Args: []ir.Value{&ir.IntConst{ + Typ: c.types.get(mallocFunc.Params[0].Type), + Value: ir.Unsigned(uint64(struct_.Size())), + }}, + }) - store := c.block.Store(pointer, result) - store.SetAlign(struct_.Align()) + c.block.Add(&ir.StoreInst{ + Pointer: pointer, + Value: result, + Align: struct_.Align() * 8, + }) c.exprResult = exprValue{v: pointer} } } func (c *codegen) VisitArrayInitializer(expr *ast.ArrayInitializer) { - type_ := c.getType(expr.Result().Type) + type_ := c.types.get(expr.Result().Type) + + var result ir.Value = &ir.ZeroInitConst{Typ: type_} - result := c.function.LiteralRaw(type_, "zeroinitializer") + for i, value := range expr.Values { + element := c.loadExpr(value) - for i, valueExpr := range expr.Values { - element := c.loadExpr(valueExpr) + r := c.block.Add(&ir.InsertValueInst{ + Value: result, + Element: element.v, + Indices: []uint32{uint32(i)}, + }) - r := c.block.InsertValue(result, element.v, i) - r.SetLocation(valueExpr.Cst()) + c.setLocationMeta(r, value) result = r } @@ -164,47 +232,33 @@ func (c *codegen) VisitAllocateArray(expr *ast.AllocateArray) { mallocFunc.Params[0].Type, a.Kind, b.Kind, - expr.Cst(), + expr, ) count = c.exprResult - pointer := c.block.Call( - malloc.v, - []llvm.Value{c.block.Binary( - llvm.Mul, - c.function.Literal( - c.getType(mallocFunc.Params[0].Type), - llvm.Literal{Unsigned: uint64(expr.Type.Size())}, - ), - count.v, - )}, - c.getType(mallocFunc.Returns), - ) + pointer := c.block.Add(&ir.CallInst{ + Callee: malloc.v, + Args: []ir.Value{&ir.IntConst{ + Typ: c.types.get(mallocFunc.Params[0].Type), + Value: ir.Unsigned(uint64(expr.Type.Size())), + }}, + }) c.exprResult = exprValue{v: pointer} } func (c *codegen) VisitUnary(expr *ast.Unary) { value := c.acceptExpr(expr.Value) - var result llvm.Value + var result ir.Value if expr.Prefix { // Prefix switch expr.Operator.Token().Kind { case scanner.Bang: - t := ast.Primitive{Kind: ast.Bool} - - r := c.block.Binary( - llvm.Xor, - c.function.Literal( - c.getType(&t), - llvm.Literal{Signed: 1}, - ), - c.load(value, expr.Value.Result().Type).v, - ) - - r.SetLocation(expr.Cst()) - result = r + result = c.block.Add(&ir.XorInst{ + Left: ir.True, + Right: c.load(value, expr.Value.Result().Type).v, + }) case scanner.Minus: if v, ok := ast.As[*ast.Primitive](expr.Value.Result().Type); ok { @@ -212,20 +266,16 @@ func (c *codegen) VisitUnary(expr *ast.Unary) { if ast.IsFloating(v.Kind) { // floating - result = c.block.FNeg(value.v) + result = c.block.Add(&ir.FNegInst{Value: value.v}) } else { // signed - r := c.block.Binary( - llvm.Sub, - c.function.Literal( - c.getType(expr.Value.Result().Type), - llvm.Literal{}, - ), - value.v, - ) - - r.SetLocation(expr.Cst()) - result = r + result = c.block.Add(&ir.SubInst{ + Left: &ir.IntConst{ + Typ: c.types.get(expr.Value.Result().Type), + Value: ir.Unsigned(0), + }, + Right: value.v, + }) } } @@ -241,59 +291,82 @@ func (c *codegen) VisitUnary(expr *ast.Unary) { result = c.load(value, expr.Value.Result().Type).v if _, ok := expr.Parent().(*ast.Assignment); !ok { - load := c.block.Load(result) - load.SetAlign(expr.Result().Type.Align()) - - result = load + result = c.block.Add(&ir.LoadInst{ + Typ: result.Type().(*ir.PointerType).Pointee, + Pointer: result, + Align: expr.Result().Type.Align() * 8, + }) } case scanner.PlusPlus, scanner.MinusMinus: + var one ir.Value + + if isFloating(expr.Value.Result().Type) { + one = &ir.FloatConst{ + Typ: c.types.get(expr.Value.Result().Type), + Value: 1, + } + } else { + one = &ir.IntConst{ + Typ: c.types.get(expr.Value.Result().Type), + Value: ir.Unsigned(1), + } + } + newValue := c.binary( expr.Operator, value, - exprValue{v: c.function.Literal( - c.getType(expr.Value.Result().Type), - llvm.Literal{ - Signed: 1, - Unsigned: 1, - Floating: 1, - }, - )}, + exprValue{v: one}, expr.Value.Result().Type, ) - store := c.block.Store(value.v, newValue.v) - store.SetAlign(expr.Value.Result().Type.Align()) - store.SetLocation(expr.Cst()) + c.block.Add(&ir.StoreInst{ + Pointer: value.v, + Value: newValue.v, + Align: expr.Value.Result().Type.Align() * 8, + }) result = newValue.v default: panic("codegen.VisitUnary() - Invalid unary prefix operator") } + + if metaValue, ok := result.(ir.MetaValue); ok { + c.setLocationMeta(metaValue, expr) + } } else { // Postfix switch expr.Operator.Token().Kind { case scanner.PlusPlus, scanner.MinusMinus: prevValue := c.load(value, expr.Value.Result().Type) + var one ir.Value + + if isFloating(expr.Value.Result().Type) { + one = &ir.FloatConst{ + Typ: c.types.get(expr.Value.Result().Type), + Value: 1, + } + } else { + one = &ir.IntConst{ + Typ: c.types.get(expr.Value.Result().Type), + Value: ir.Unsigned(1), + } + } + newValue := c.binary( expr.Operator, prevValue, - exprValue{v: c.function.Literal( - c.getType(expr.Value.Result().Type), - llvm.Literal{ - Signed: 1, - Unsigned: 1, - Floating: 1, - }, - )}, + exprValue{v: one}, expr.Value.Result().Type, ) - store := c.block.Store(value.v, newValue.v) - store.SetAlign(expr.Value.Result().Type.Align()) - store.SetLocation(expr.Cst()) + c.block.Add(&ir.StoreInst{ + Pointer: value.v, + Value: newValue.v, + Align: expr.Value.Result().Type.Align() * 8, + }) result = prevValue.v @@ -323,18 +396,35 @@ func (c *codegen) VisitLogical(expr *ast.Logical) { // Start startBlock := c.block - c.block.Br(left.v, end, false_).SetLocation(expr.Cst()) + + c.setLocationMeta( + c.block.Add(&ir.BrInst{Condition: left.v, True: end, False: false_}), + expr, + ) // False c.beginBlock(false_) - c.block.Br(nil, end, nil).SetLocation(expr.Cst()) + + c.setLocationMeta( + c.block.Add(&ir.BrInst{True: end}), + expr, + ) // End c.beginBlock(end) - result := c.block.Phi(c.function.LiteralRaw(c.getType(expr.Result().Type), "true"), startBlock, right.v, false_) - result.SetLocation(expr.Cst()) - + result := c.block.Add(&ir.PhiInst{Incs: []ir.Incoming{ + { + Value: ir.True, + Label: startBlock, + }, + { + Value: right.v, + Label: false_, + }, + }}) + + c.setLocationMeta(result, expr) c.exprResult = exprValue{v: result} case scanner.And: @@ -343,18 +433,35 @@ func (c *codegen) VisitLogical(expr *ast.Logical) { // Start startBlock := c.block - c.block.Br(left.v, true_, end).SetLocation(expr.Cst()) + + c.setLocationMeta( + c.block.Add(&ir.BrInst{Condition: left.v, True: end, False: end}), + expr, + ) // True c.beginBlock(true_) - c.block.Br(nil, end, nil).SetLocation(expr.Cst()) + + c.setLocationMeta( + c.block.Add(&ir.BrInst{True: end}), + expr, + ) // End c.beginBlock(end) - result := c.block.Phi(c.function.LiteralRaw(c.getType(expr.Result().Type), "false"), startBlock, right.v, true_) - result.SetLocation(expr.Cst()) - + result := c.block.Add(&ir.PhiInst{Incs: []ir.Incoming{ + { + Value: ir.False, + Label: startBlock, + }, + { + Value: right.v, + Label: true_, + }, + }}) + + c.setLocationMeta(result, expr) c.exprResult = exprValue{v: result} default: @@ -373,7 +480,7 @@ func (c *codegen) VisitIdentifier(expr *ast.Identifier) { c.exprResult = c.getGlobalVariable(node) default: - if v := c.getVariable(expr.Name); v != nil { + if v := c.scopes.getVariable(expr.Name); v != nil { c.exprResult = v.value } } @@ -387,7 +494,7 @@ func (c *codegen) VisitIdentifier(expr *ast.Identifier) { c.exprResult = c.getGlobalVariable(node) case *ast.Var, *ast.Param: - if v := c.getVariable(expr.Name); v != nil { + if v := c.scopes.getVariable(expr.Name); v != nil { c.exprResult = v.value } } @@ -414,10 +521,13 @@ func (c *codegen) VisitAssignment(expr *ast.Assignment) { } // Store - store := c.block.Store(assignee.v, value.v) - store.SetAlign(expr.Result().Type.Align()) - store.SetLocation(expr.Cst()) + store := c.block.Add(&ir.StoreInst{ + Pointer: assignee.v, + Value: value.v, + Align: expr.Result().Type.Align() * 8, + }) + c.setLocationMeta(store, expr) c.exprResult = assignee } @@ -427,7 +537,7 @@ func (c *codegen) VisitCast(expr *ast.Cast) { if from, ok := ast.As[*ast.Primitive](expr.Value.Result().Type); ok { if to, ok := ast.As[*ast.Primitive](expr.Result().Type); ok { // primitive to primitive - c.castPrimitiveToPrimitive(value, from, to, from.Kind, to.Kind, expr.Cst()) + c.castPrimitiveToPrimitive(value, from, to, from.Kind, to.Kind, expr) return } } @@ -437,7 +547,7 @@ func (c *codegen) VisitCast(expr *ast.Cast) { // enum to integer fromT, _ := ast.As[*ast.Primitive](from.Type) - c.castPrimitiveToPrimitive(value, from, to, fromT.Kind, to.Kind, expr.Cst()) + c.castPrimitiveToPrimitive(value, from, to, fromT.Kind, to.Kind, expr) return } } @@ -447,7 +557,7 @@ func (c *codegen) VisitCast(expr *ast.Cast) { // integer to enum toT, _ := ast.As[*ast.Primitive](to.Type) - c.castPrimitiveToPrimitive(value, from, to, from.Kind, toT.Kind, expr.Cst()) + c.castPrimitiveToPrimitive(value, from, to, from.Kind, toT.Kind, expr) return } } @@ -470,7 +580,7 @@ func (c *codegen) VisitCast(expr *ast.Cast) { panic("codegen.VisitCast() - Invalid cast") } -func (c *codegen) castPrimitiveToPrimitive(value exprValue, from, to ast.Type, fromKind, toKind ast.PrimitiveKind, location *cst.Node) { +func (c *codegen) castPrimitiveToPrimitive(value exprValue, from, to ast.Type, fromKind, toKind ast.PrimitiveKind, location ast.Node) { if fromKind == toKind || (ast.EqualsPrimitiveCategory(fromKind, toKind) && ast.GetBitSize(fromKind) == ast.GetBitSize(toKind)) { c.exprResult = value return @@ -480,58 +590,75 @@ func (c *codegen) castPrimitiveToPrimitive(value exprValue, from, to ast.Type, f if (ast.IsInteger(fromKind) || ast.IsFloating(fromKind)) && toKind == ast.Bool { // integer / floating to bool - result := c.block.Binary( - llvm.Ne, - value.v, - c.function.Literal( - value.v.Type(), - llvm.Literal{}, - ), - ) - - result.SetLocation(location) - c.exprResult = exprValue{ - v: result, + var result ir.MetaValue + + if ast.IsFloating(fromKind) { + result = c.block.Add(&ir.FCmpInst{ + Kind: ir.Ne, + Ordered: false, + Left: value.v, + Right: ir.False, + }) + } else { + result = c.block.Add(&ir.ICmpInst{ + Kind: ir.Ne, + Signed: ast.IsSigned(fromKind), + Left: value.v, + Right: ir.False, + }) } + + c.setLocationMeta(result, location) + c.exprResult = exprValue{v: result} } else { - var kind llvm.CastKind + type_ := c.types.get(to) + var result ir.MetaValue if (ast.IsInteger(fromKind) || fromKind == ast.Bool) && ast.IsInteger(toKind) { // integer / bool to integer if from.Size() > to.Size() { - kind = llvm.Trunc + result = c.block.Add(&ir.TruncInst{ + Value: value.v, + Typ: type_, + }) } else { - kind = llvm.ZExt + result = c.block.Add(&ir.ExtInst{ + SignExtend: false, + Value: value.v, + Typ: type_, + }) } } else if ast.IsFloating(fromKind) && ast.IsFloating(toKind) { // floating to floating if from.Size() > to.Size() { - kind = llvm.FpTrunc + result = c.block.Add(&ir.TruncInst{ + Value: value.v, + Typ: type_, + }) } else { - kind = llvm.FpExt + result = c.block.Add(&ir.FExtInst{ + Value: value.v, + Typ: type_, + }) } } else if (ast.IsInteger(fromKind) || fromKind == ast.Bool) && ast.IsFloating(toKind) { // integer / bool to floating - if ast.IsSigned(fromKind) { - kind = llvm.SiToFp - } else { - kind = llvm.UiToFp - } + result = c.block.Add(&ir.I2FInst{ + Signed: ast.IsSigned(fromKind), + Value: value.v, + Typ: type_, + }) } else if ast.IsFloating(fromKind) && ast.IsInteger(toKind) { // floating to integer - if ast.IsSigned(toKind) { - kind = llvm.FpToSi - } else { - kind = llvm.FpToUi - } + result = c.block.Add(&ir.F2IInst{ + Signed: ast.IsSigned(toKind), + Value: value.v, + Typ: type_, + }) } - result := c.block.Cast(kind, value.v, c.getType(to)) - result.SetLocation(location) - - c.exprResult = exprValue{ - v: result, - } + c.setLocationMeta(result, location) + c.exprResult = exprValue{v: result} } } @@ -541,20 +668,17 @@ func (c *codegen) VisitTypeCall(expr *ast.TypeCall) { switch expr.Callee.String() { case "sizeof": value = expr.Arg.Size() - case "alignof": value = expr.Arg.Align() default: - panic("codegen.VisitTypeCall() - Invalid name") + panic("codegen.VisitTypeCall() - Not implemented") } - c.exprResult = exprValue{ - v: c.function.Literal( - c.getType(expr.Result().Type), - llvm.Literal{Signed: int64(value)}, - ), - } + c.exprResult = exprValue{v: &ir.IntConst{ + Typ: c.types.get(expr.Result().Type), + Value: ir.Unsigned(uint64(value)), + }} } func (c *codegen) VisitCall(expr *ast.Call) { @@ -578,7 +702,7 @@ func (c *codegen) VisitCall(expr *ast.Call) { argCount++ } - args := make([]llvm.Value, argCount) + args := make([]ir.Value, argCount) if function.Method() != nil { args[0] = c.this.v @@ -601,19 +725,23 @@ func (c *codegen) VisitCall(expr *ast.Call) { } // Call - result := c.block.Call(callee.v, args, c.getType(function.Returns)) - result.SetLocation(expr.Cst()) + result := c.block.Add(&ir.CallInst{ + Callee: callee.v, + Args: args, + }) - c.exprResult = exprValue{ - v: result, - } + c.setLocationMeta(result, expr) + c.exprResult = exprValue{v: result} // If the function returns a constant-sized array and the array is immediately indexed then store it in an alloca first if callNeedsTempVariable(expr) { pointer := c.allocas[expr] - store := c.block.Store(pointer.v, c.exprResult.v) - store.SetAlign(function.Returns.Align()) + c.block.Add(&ir.StoreInst{ + Pointer: pointer.v, + Value: c.exprResult.v, + Align: function.Returns.Align() * 8, + }) c.exprResult = pointer } @@ -624,23 +752,24 @@ func (c *codegen) VisitIndex(expr *ast.Index) { index := c.loadExpr(expr.Index) if pointer, ok := ast.As[*ast.Pointer](expr.Value.Result().Type); ok { - load := c.block.Load(value.v) - load.SetAlign(pointer.Pointee.Align()) - - value = exprValue{v: load} + value = exprValue{v: c.block.Add(&ir.LoadInst{ + Typ: value.v.Type().(*ir.PointerType).Pointee, + Pointer: value.v, + Align: pointer.Pointee.Align() * 8, + })} } - t := ast.Pointer{Pointee: expr.Result().Type} - type_ := c.getType(&t) + ptrType := ast.Pointer{Pointee: expr.Result().Type} - result := c.block.GetElementPtr( - value.v, - []llvm.Value{index.v}, - type_, - c.getType(expr.Result().Type), - ) + result := c.block.Add(&ir.GetElementPtrInst{ + PointerTyp: c.types.get(&ptrType), + Typ: c.types.get(expr.Result().Type), + Pointer: value.v, + Indices: []ir.Value{index.v}, + Inbounds: true, + }) - result.SetLocation(expr.Cst()) + c.setLocationMeta(result, expr) c.exprResult = exprValue{ v: result, @@ -658,12 +787,10 @@ func (c *codegen) VisitMember(expr *ast.Member) { case ast.ValueResultKind: switch node := expr.Result().Value().(type) { case *ast.EnumCase: - c.exprResult = exprValue{ - v: c.function.Literal( - c.getType(node.Parent().(*ast.Enum).ActualType), - llvm.Literal{Signed: node.ActualValue, Unsigned: uint64(node.ActualValue)}, - ), - } + c.exprResult = exprValue{v: &ir.IntConst{ + Typ: c.types.get(node.Parent().(*ast.Enum).ActualType), + Value: ir.Signed(node.ActualValue), + }} case *ast.Field: if node.IsStatic() { @@ -672,34 +799,33 @@ func (c *codegen) VisitMember(expr *ast.Member) { value, s := c.memberLoad(expr.Value.Result().Type, value) if value.addressable { - i32Type_ := ast.Primitive{Kind: ast.I32} - i32Type := c.getType(&i32Type_) - - t := ast.Pointer{Pointee: node.Type} - - result := c.block.GetElementPtr( - value.v, - []llvm.Value{ - c.function.Literal(i32Type, llvm.Literal{Signed: 0}), - c.function.Literal(i32Type, llvm.Literal{Signed: int64(node.Index())}), + ptrType := ast.Pointer{Pointee: node.Type} + + result := c.block.Add(&ir.GetElementPtrInst{ + PointerTyp: c.types.get(&ptrType), + Typ: c.types.get(s), + Pointer: value.v, + Indices: []ir.Value{ + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(0)}, + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(uint64(node.Index()))}, }, - c.getType(&t), - c.getType(s), - ) + Inbounds: true, + }) - result.SetLocation(expr.Cst()) + c.setLocationMeta(result, expr) c.exprResult = exprValue{ v: result, addressable: true, } } else { - result := c.block.ExtractValue(value.v, node.Index()) - result.SetLocation(expr.Cst()) + result := c.block.Add(&ir.ExtractValueInst{ + Value: value.v, + Indices: []uint32{uint32(node.Index())}, + }) - c.exprResult = exprValue{ - v: result, - } + c.setLocationMeta(result, expr) + c.exprResult = exprValue{v: result} } } @@ -727,8 +853,11 @@ func (c *codegen) VisitMember(expr *ast.Member) { if node.Method() != nil && !value.addressable { pointer := c.allocas[expr] - store := c.block.Store(pointer.v, value.v) - store.SetAlign(node.Align()) + c.block.Add(&ir.StoreInst{ + Pointer: pointer.v, + Value: value.v, + Align: node.Align() * 8, + }) value = pointer } @@ -755,8 +884,11 @@ func (c *codegen) memberLoad(type_ ast.Type, value exprValue) (exprValue, *ast.S if v, ok := ast.As[*ast.Pointer](type_); ok { if v, ok := ast.As[*ast.Struct](v.Pointee); ok { - load := c.block.Load(value.v) - load.SetAlign(v.Align()) + load := c.block.Add(&ir.LoadInst{ + Typ: value.v.Type().(*ir.PointerType).Pointee, + Pointer: value.v, + Align: v.Align() * 8, + }) return exprValue{ v: load, @@ -774,51 +906,125 @@ func (c *codegen) binary(op ast.Node, left exprValue, right exprValue, type_ ast left = c.load(left, type_) right = c.load(right, type_) - var kind llvm.BinaryKind + var result ir.MetaValue switch op.Token().Kind { case scanner.Plus, scanner.PlusEqual, scanner.PlusPlus: - kind = llvm.Add + result = c.block.Add(&ir.AddInst{ + Left: left.v, + Right: right.v, + }) + case scanner.Minus, scanner.MinusEqual, scanner.MinusMinus: - kind = llvm.Sub + result = c.block.Add(&ir.SubInst{ + Left: left.v, + Right: right.v, + }) + case scanner.Star, scanner.StarEqual: - kind = llvm.Mul + result = c.block.Add(&ir.MulInst{ + Left: left.v, + Right: right.v, + }) + case scanner.Slash, scanner.SlashEqual: - kind = llvm.Div + if isFloating(type_) { + result = c.block.Add(&ir.FDivInst{ + Left: left.v, + Right: right.v, + }) + } else { + result = c.block.Add(&ir.IDivInst{ + Signed: isSigned(type_), + Left: left.v, + Right: right.v, + }) + } + case scanner.Percentage, scanner.PercentageEqual: - kind = llvm.Rem - - case scanner.EqualEqual: - kind = llvm.Eq - case scanner.BangEqual: - kind = llvm.Ne - - case scanner.Less: - kind = llvm.Lt - case scanner.LessEqual: - kind = llvm.Le - case scanner.Greater: - kind = llvm.Gt - case scanner.GreaterEqual: - kind = llvm.Ge + if isFloating(type_) { + result = c.block.Add(&ir.FRemInst{ + Left: left.v, + Right: right.v, + }) + } else { + result = c.block.Add(&ir.IRemInst{ + Signed: isSigned(type_), + Left: left.v, + Right: right.v, + }) + } case scanner.Pipe, scanner.PipeEqual: - kind = llvm.Or + result = c.block.Add(&ir.OrInst{ + Left: left.v, + Right: right.v, + }) + case scanner.Xor, scanner.XorEqual: - kind = llvm.Xor + result = c.block.Add(&ir.XorInst{ + Left: left.v, + Right: right.v, + }) + case scanner.Ampersand, scanner.AmpersandEqual: - kind = llvm.And + result = c.block.Add(&ir.AndInst{ + Left: left.v, + Right: right.v, + }) + case scanner.LessLess, scanner.LessLessEqual: - kind = llvm.Shl + result = c.block.Add(&ir.ShlInst{ + Left: left.v, + Right: right.v, + }) + case scanner.GreaterGreater, scanner.GreaterGreaterEqual: - kind = llvm.Shr + result = c.block.Add(&ir.ShrInst{ + SignExtend: false, + Left: left.v, + Right: right.v, + }) default: - panic("codegen.binary() - Invalid operator kind") - } + var kind ir.CmpKind + + switch op.Token().Kind { + case scanner.EqualEqual: + kind = ir.Eq + case scanner.BangEqual: + kind = ir.Ne + + case scanner.Less: + kind = ir.Lt + case scanner.LessEqual: + kind = ir.Le + case scanner.Greater: + kind = ir.Gt + case scanner.GreaterEqual: + kind = ir.Ge - result := c.block.Binary(kind, left.v, right.v) - result.SetLocation(op.Cst()) + default: + panic("codegen.binary() - Not implemented") + } + + if isFloating(type_) { + result = c.block.Add(&ir.FCmpInst{ + Kind: kind, + Ordered: false, + Left: left.v, + Right: right.v, + }) + } else { + result = c.block.Add(&ir.ICmpInst{ + Kind: kind, + Signed: isSigned(type_), + Left: left.v, + Right: right.v, + }) + } + } + c.setLocationMeta(result, op) return exprValue{v: result} } diff --git a/core/codegen/instructions.go b/core/codegen/instructions.go new file mode 100644 index 0000000..339f3c6 --- /dev/null +++ b/core/codegen/instructions.go @@ -0,0 +1,38 @@ +package codegen + +import ( + "fireball/core/ast" + "fireball/core/ir" +) + +func (c *codegen) alloca(type_ ast.Type, name string, node ast.Node) ir.Value { + pointer := c.block.Add(&ir.AllocaInst{ + Typ: c.types.get(type_), + Align: type_.Align() * 8, + }) + + if name != "" { + pointer.SetName(name) + } + + if node != nil { + c.setLocationMeta(pointer, node) + } + + return pointer +} + +func (c *codegen) setLocationMeta(value ir.MetaValue, node ast.Node) { + meta := &ir.LocationMeta{ + Scope: c.scopes.getMeta(), + } + + if node.Cst() != nil { + pos := node.Cst().Range.Start + + meta.Line = uint32(pos.Line) + meta.Column = uint32(pos.Column) + } + + value.SetMeta(c.module.Meta(meta)) +} diff --git a/core/codegen/scopes.go b/core/codegen/scopes.go new file mode 100644 index 0000000..48882bb --- /dev/null +++ b/core/codegen/scopes.go @@ -0,0 +1,197 @@ +package codegen + +import ( + "fireball/core/ast" + "fireball/core/ir" + "fireball/core/scanner" +) + +type scope struct { + variableI int + variableCount int +} + +type variable struct { + name ast.Node + value exprValue +} + +type scopes struct { + c *codegen + + scopes []scope + variables []variable + + file ir.MetaID + + unitMeta *ir.CompileUnitMeta + unitId ir.MetaID + + scopesMeta []ir.MetaID + + declare ir.Value +} + +func (s *scopes) pushFile(path string) { + const producer = "fireball compiler 0.1.0" + + // File + s.file = s.c.module.Meta(&ir.FileMeta{Path: path}) + + s.push(s.file) + + // Unit + s.unitMeta = &ir.CompileUnitMeta{ + File: s.file, + Producer: producer, + Emission: ir.FullDebug, + NameTable: ir.None, + } + + m := s.c.module + s.unitId = m.Meta(s.unitMeta) + + m.NamedMeta("llvm.dbg.cu", []ir.MetaID{s.unitId}) + + // Identifier + m.NamedMeta("llvm.ident", []ir.MetaID{m.Meta(&ir.GroupMeta{ + Metadata: []ir.MetaID{m.Meta(&ir.StringMeta{Value: producer})}, + })}) + + // Flags + m.NamedMeta("llvm.module.flags", []ir.MetaID{ + m.Meta(&ir.GroupMeta{Metadata: []ir.MetaID{ + m.Meta(&ir.IntMeta{Value: 7}), + m.Meta(&ir.StringMeta{Value: "Dwarf Version"}), + m.Meta(&ir.IntMeta{Value: 4}), + }}), + m.Meta(&ir.GroupMeta{Metadata: []ir.MetaID{ + m.Meta(&ir.IntMeta{Value: 2}), + m.Meta(&ir.StringMeta{Value: "Debug Info Version"}), + m.Meta(&ir.IntMeta{Value: 3}), + }}), + m.Meta(&ir.GroupMeta{Metadata: []ir.MetaID{ + m.Meta(&ir.IntMeta{Value: 1}), + m.Meta(&ir.StringMeta{Value: "wchar_size"}), + m.Meta(&ir.IntMeta{Value: 4}), + }}), + m.Meta(&ir.GroupMeta{Metadata: []ir.MetaID{ + m.Meta(&ir.IntMeta{Value: 8}), + m.Meta(&ir.StringMeta{Value: "PIC Level"}), + m.Meta(&ir.IntMeta{Value: 2}), + }}), + m.Meta(&ir.GroupMeta{Metadata: []ir.MetaID{ + m.Meta(&ir.IntMeta{Value: 7}), + m.Meta(&ir.StringMeta{Value: "PIE Level"}), + m.Meta(&ir.IntMeta{Value: 2}), + }}), + m.Meta(&ir.GroupMeta{Metadata: []ir.MetaID{ + m.Meta(&ir.IntMeta{Value: 7}), + m.Meta(&ir.StringMeta{Value: "uwtable"}), + m.Meta(&ir.IntMeta{Value: 2}), + }}), + m.Meta(&ir.GroupMeta{Metadata: []ir.MetaID{ + m.Meta(&ir.IntMeta{Value: 7}), + m.Meta(&ir.StringMeta{Value: "frame-pointer"}), + m.Meta(&ir.IntMeta{Value: 2}), + }}), + }) +} + +func (s *scopes) pushBlock(node ast.Node) { + block := &ir.LexicalBlockMeta{ + Scope: s.getMeta(), + File: s.file, + } + + if node.Cst() != nil { + block.Line = uint32(node.Cst().Range.Start.Line) + } + + s.push(s.c.module.Meta(block)) +} + +func (s *scopes) push(id ir.MetaID) { + s.scopes = append(s.scopes, scope{ + variableI: len(s.variables), + variableCount: 0, + }) + + s.scopesMeta = append(s.scopesMeta, id) +} + +func (s *scopes) pop() { + s.variables = s.variables[:s.get().variableI] + s.scopes = s.scopes[:len(s.scopes)-1] + + s.scopesMeta = s.scopesMeta[:len(s.scopesMeta)-0] +} + +func (s *scopes) get() *scope { + return &s.scopes[len(s.scopes)-1] +} + +func (s *scopes) getMeta() ir.MetaID { + return s.scopesMeta[len(s.scopesMeta)-1] +} + +// Variables + +func (s *scopes) getVariable(name scanner.Token) *variable { + for i := len(s.variables) - 1; i >= 0; i-- { + if s.variables[i].name.String() == name.Lexeme { + return &s.variables[i] + } + } + + return nil +} + +func (s *scopes) addVariable(name ast.Node, type_ ast.Type, value exprValue, arg uint32) *variable { + if s.declare == nil { + s.declare = s.c.module.Declare( + "llvm.dbg.declare", + &ir.FuncType{Params: []*ir.Param{ + {Typ: ir.MetaT}, + {Typ: ir.MetaT}, + {Typ: ir.MetaT}, + }}) + } + + if _, ok := ast.As[*ast.Func](type_); ok { + type_ = &ast.Pointer{Pointee: type_} + } + + meta := &ir.LocalVarMeta{ + Name: name.String(), + Type: s.c.types.getMeta(type_), + Arg: arg, + Scope: s.getMeta(), + File: s.file, + } + + if name.Cst() != nil { + meta.Line = uint32(name.Cst().Range.Start.Line) + } + + declare := s.c.block.Add(&ir.CallInst{ + Callee: s.declare, + Args: []ir.Value{ + value.v, + s.c.module.Meta(meta), + s.c.module.Meta(&ir.ExpressionMeta{}), + }, + }) + + s.c.setLocationMeta(declare, name) + + value.addressable = true + + s.variables = append(s.variables, variable{ + name: name, + value: value, + }) + + s.get().variableCount++ + return &s.variables[len(s.variables)-1] +} diff --git a/core/codegen/statements.go b/core/codegen/statements.go index 619134c..9989a7e 100644 --- a/core/codegen/statements.go +++ b/core/codegen/statements.go @@ -2,18 +2,17 @@ package codegen import ( "fireball/core/ast" + "fireball/core/ir" ) func (c *codegen) VisitBlock(stmt *ast.Block) { - c.pushScope() - c.module.PushScope(stmt.Cst()) + c.scopes.pushBlock(stmt) for _, s := range stmt.Stmts { c.acceptStmt(s) } - c.module.PopScope() - c.popScope() + c.scopes.pop() } func (c *codegen) VisitExpression(stmt *ast.Expression) { @@ -23,15 +22,19 @@ func (c *codegen) VisitExpression(stmt *ast.Expression) { func (c *codegen) VisitVar(stmt *ast.Var) { // Variable pointer := c.allocas[stmt] - c.addVariable(stmt.Name, pointer) + c.scopes.addVariable(stmt.Name, stmt.ActualType, pointer, 0) // Initializer if stmt.Value != nil { initializer := c.loadExpr(stmt.Value) - store := c.block.Store(pointer.v, initializer.v) - store.SetAlign(stmt.ActualType.Align()) - store.SetLocation(stmt.Name.Cst()) + store := c.block.Add(&ir.StoreInst{ + Pointer: pointer.v, + Value: initializer.v, + Align: stmt.ActualType.Align() * 8, + }) + + c.setLocationMeta(store, stmt) } } @@ -47,18 +50,20 @@ func (c *codegen) VisitIf(stmt *ast.If) { // Condition condition := c.loadExpr(stmt.Condition) - c.block.Br(condition.v, then, else_) + c.block.Add(&ir.BrInst{Condition: condition.v, True: then, False: else_}) // Then c.beginBlock(then) - c.acceptStmt(stmt.Then) - c.block.Br(nil, end, nil) + if c.acceptStmt(stmt.Then) { + c.block.Add(&ir.BrInst{True: end}) + } // Else if stmt.Else != nil { c.beginBlock(else_) - c.acceptStmt(stmt.Else) - c.block.Br(nil, end, nil) + if c.acceptStmt(stmt.Else) { + c.block.Add(&ir.BrInst{True: end}) + } } // End @@ -74,17 +79,18 @@ func (c *codegen) VisitWhile(stmt *ast.While) { body := c.function.Block("while.body") c.loopEnd = c.function.Block("while.end") - c.block.Br(nil, c.loopStart, nil) + c.block.Add(&ir.BrInst{True: c.loopStart}) // Condition c.beginBlock(c.loopStart) condition := c.acceptExpr(stmt.Condition) - c.block.Br(condition.v, body, c.loopEnd) + c.block.Add(&ir.BrInst{Condition: condition.v, True: body, False: c.loopEnd}) // Body c.beginBlock(body) - c.acceptStmt(stmt.Body) - c.block.Br(nil, c.loopStart, nil) + if c.acceptStmt(stmt.Body) { + c.block.Add(&ir.BrInst{True: c.loopStart}) + } // End c.beginBlock(c.loopEnd) @@ -108,18 +114,18 @@ func (c *codegen) VisitFor(stmt *ast.For) { } // Initializer - c.pushScope() - c.module.PushScope(stmt.Cst()) + c.scopes.pushBlock(stmt) - c.acceptStmt(stmt.Initializer) - c.block.Br(nil, c.loopStart, nil) + if c.acceptStmt(stmt.Initializer) { + c.block.Add(&ir.BrInst{True: c.loopStart}) + } // Condition c.beginBlock(c.loopStart) if stmt.Condition != nil { condition := c.loadExpr(stmt.Condition) - c.block.Br(condition.v, body, c.loopEnd) + c.block.Add(&ir.BrInst{Condition: condition.v, True: body, False: c.loopEnd}) } // Body and increment @@ -130,11 +136,10 @@ func (c *codegen) VisitFor(stmt *ast.For) { c.acceptStmt(stmt.Body) c.acceptExpr(stmt.Increment) - c.block.Br(nil, c.loopStart, nil) + c.block.Add(&ir.BrInst{True: c.loopStart}) // End - c.module.PopScope() - c.popScope() + c.scopes.pop() c.beginBlock(c.loopEnd) // Reset basic block names @@ -145,18 +150,37 @@ func (c *codegen) VisitFor(stmt *ast.For) { func (c *codegen) VisitReturn(stmt *ast.Return) { if stmt.Value == nil { // Void - c.block.Ret(nil).SetLocation(stmt.Cst()) + c.setLocationMeta( + c.block.Add(&ir.RetInst{}), + stmt, + ) } else { // Other value := c.loadExpr(stmt.Value) - c.block.Ret(value.v).SetLocation(stmt.Cst()) + + c.setLocationMeta( + c.block.Add(&ir.RetInst{Value: value.v}), + stmt, + ) } + + c.block = nil } func (c *codegen) VisitBreak(stmt *ast.Break) { - c.block.Br(nil, c.loopEnd, nil).SetLocation(stmt.Cst()) + c.setLocationMeta( + c.block.Add(&ir.BrInst{True: c.loopEnd}), + stmt, + ) + + c.block = nil } func (c *codegen) VisitContinue(stmt *ast.Continue) { - c.block.Br(nil, c.loopStart, nil).SetLocation(stmt.Cst()) + c.setLocationMeta( + c.block.Add(&ir.BrInst{True: c.loopStart}), + stmt, + ) + + c.block = nil } diff --git a/core/codegen/types.go b/core/codegen/types.go new file mode 100644 index 0000000..e62f924 --- /dev/null +++ b/core/codegen/types.go @@ -0,0 +1,411 @@ +package codegen + +import ( + "fireball/core/architecture" + "fireball/core/ast" + "fireball/core/ir" + "fmt" +) + +type cachedType struct { + type_ ast.Type + typ ir.Type +} + +type cachedTypeMeta struct { + type_ ast.Type + id ir.MetaID +} + +type types struct { + module *ir.Module + + types []cachedType + metadata []cachedTypeMeta +} + +// Types + +func (t *types) get(type_ ast.Type) ir.Type { + switch type_ := type_.Resolved().(type) { + case *ast.Primitive: + switch type_.Kind { + case ast.Void: + return ir.Void + case ast.Bool: + return ir.I1 + + case ast.U8, ast.I8: + return ir.I8 + case ast.U16, ast.I16: + return ir.I16 + case ast.U32, ast.I32: + return ir.I32 + case ast.U64, ast.I64: + return ir.I64 + + case ast.F32: + return ir.F32 + case ast.F64: + return ir.F64 + + default: + panic("codegen.types.get() - ast.Primitive - Not implemented") + } + + case *ast.Pointer: + if typ := t.getCachedType(type_); typ != nil { + return typ + } + + return t.cacheType(type_, &ir.PointerType{Pointee: t.get(type_.Pointee)}) + + case *ast.Array: + if typ := t.getCachedType(type_); typ != nil { + return typ + } + + return t.cacheType(type_, &ir.ArrayType{Count: type_.Count, Base: t.get(type_.Base)}) + + case *ast.Struct: + if typ := t.getCachedType(type_); typ != nil { + return typ + } + + fields := make([]ir.Type, len(type_.Fields)) + + for i, field := range type_.Fields { + fields[i] = t.get(field.Type) + } + + typ := &ir.StructType{ + Name: type_.Name.String(), + Fields: fields, + } + + t.module.Struct(typ) + return t.cacheType(type_, typ) + + case *ast.Enum: + return t.get(type_.ActualType) + + case *ast.Func: + if typ := t.getCachedType(type_); typ != nil { + return typ + } + + // Intrinsic + intrinsicName := type_.IntrinsicName() + + if intrinsicName != "" { + intrinsic := t.getIntrinsic(type_, intrinsicName) + + params := make([]*ir.Param, len(intrinsic[1:])) + + for i, param := range intrinsic[1:] { + params[i] = &ir.Param{ + Typ: param, + Name_: fmt.Sprintf("_%d", i), + } + } + + typ := &ir.FuncType{ + Returns: intrinsic[0], + Params: params, + } + + return t.cacheType(type_, typ) + } + + // Normal + this := type_.Method() + + parameterCount := len(type_.Params) + if this != nil { + parameterCount++ + } + + params := make([]*ir.Param, parameterCount) + + if this != nil { + type_ := ast.Pointer{Pointee: this} + + params[0] = &ir.Param{ + Typ: t.get(&type_), + Name_: "this", + } + } + + for index, param := range type_.Params { + i := index + if this != nil { + i++ + } + + params[i] = &ir.Param{ + Typ: t.get(param.Type), + Name_: param.Name.String(), + } + } + + typ := &ir.FuncType{ + Returns: t.get(type_.Returns), + Params: params, + Variadic: type_.IsVariadic(), + } + + return t.cacheType(type_, typ) + + default: + panic("codegen.types.get() - Not implemented") + } +} + +func (t *types) getIntrinsic(function *ast.Func, intrinsicName string) []ir.Type { + param := t.get(function.Params[0].Type) + + switch intrinsicName { + case "abs": + if isFloating(function.Params[0].Type) { + return []ir.Type{ + param, + param, + } + } else { + return []ir.Type{ + param, + param, + ir.I1, + } + } + + case "pow", "min", "max", "copysign": + return []ir.Type{param, param, param} + + case "sqrt", "sin", "cos", "exp", "exp2", "exp10", "log", "log2", "log10", "floor", "ceil", "round": + return []ir.Type{param, param} + + case "fma": + return []ir.Type{param, param, param, param} + + case "memcpy", "memmove": + return []ir.Type{ + ir.Void, + param, + param, + ir.I32, + ir.I1, + } + + case "memset": + return []ir.Type{ + ir.Void, + param, + ir.I8, + ir.I32, + ir.I1, + } + + default: + panic("codegen.types.getIntrinsic() - Not implemented") + } +} + +func (t *types) getCachedType(type_ ast.Type) ir.Type { + for _, cached := range t.types { + if cached.type_.Equals(type_) { + return cached.typ + } + } + + return nil +} + +func (t *types) cacheType(type_ ast.Type, typ ir.Type) ir.Type { + t.types = append(t.types, cachedType{ + type_: type_, + typ: typ, + }) + + return typ +} + +// Meta + +func (t *types) getMeta(type_ ast.Type) ir.MetaID { + for _, cached := range t.metadata { + if cached.type_.Equals(type_) { + return cached.id + } + } + + switch type_ := type_.Resolved().(type) { + case *ast.Primitive: + if type_.Kind == ast.Void { + return 0 + } + + typ := &ir.BasicTypeMeta{ + Size: type_.Size() * 8, + Align: type_.Align() * 8, + } + + switch type_.Kind { + case ast.Bool: + typ.Name = "bool" + typ.Encoding = ir.BooleanEncoding + + case ast.U8: + typ.Name = "u8" + typ.Encoding = ir.UnsignedEncoding + case ast.U16: + typ.Name = "u16" + typ.Encoding = ir.UnsignedEncoding + case ast.U32: + typ.Name = "u32" + typ.Encoding = ir.UnsignedEncoding + case ast.U64: + typ.Name = "u64" + typ.Encoding = ir.UnsignedEncoding + + case ast.I8: + typ.Name = "i8" + typ.Encoding = ir.SignedEncoding + case ast.I16: + typ.Name = "i16" + typ.Encoding = ir.SignedEncoding + case ast.I32: + typ.Name = "i32" + typ.Encoding = ir.SignedEncoding + case ast.I64: + typ.Name = "i64" + typ.Encoding = ir.SignedEncoding + + case ast.F32: + typ.Name = "f32" + typ.Encoding = ir.FloatEncoding + case ast.F64: + typ.Name = "f64" + typ.Encoding = ir.FloatEncoding + + default: + panic("codegen.types.getMeta() - ast.Primitive - Not implemented") + } + + return t.cacheMeta(type_, typ) + + case *ast.Pointer: + typ := &ir.DerivedTypeMeta{ + Tag: ir.PointerTypeTag, + BaseType: t.getMeta(type_.Pointee), + Size: type_.Size() * 8, + Align: type_.Align() * 8, + } + + return t.cacheMeta(type_, typ) + + case *ast.Array: + typ := &ir.CompositeTypeMeta{ + Tag: ir.ArrayTypeTag, + Size: type_.Size() * 8, + Align: type_.Align() * 8, + BaseType: t.getMeta(type_.Base), + Elements: []ir.MetaID{t.module.Meta(&ir.SubrangeMeta{ + LowerBound: 0, + Count: type_.Count, + })}, + } + + return t.cacheMeta(type_, typ) + + case *ast.Struct: + layout := architecture.CLayout{} + fields := make([]ir.MetaID, len(type_.Fields)) + + for i, field := range type_.Fields { + offset := layout.Add(field.Type.Size(), field.Type.Align()) + + fields[i] = t.module.Meta(&ir.DerivedTypeMeta{ + Tag: ir.MemberTag, + BaseType: t.getMeta(field.Type), + Offset: offset * 8, + }) + } + + typ := &ir.CompositeTypeMeta{ + Tag: ir.StructureTypeTag, + Name: type_.Name.String(), + Size: layout.Size() * 8, + Align: type_.Align() * 8, + Elements: fields, + } + + return t.cacheMeta(type_, typ) + + case *ast.Enum: + cases := make([]ir.MetaID, len(type_.Cases)) + + for i, case_ := range type_.Cases { + cases[i] = t.module.Meta(&ir.EnumeratorMeta{ + Name: case_.Name.String(), + Value: ir.Signed(case_.ActualValue), + }) + } + + typ := &ir.CompositeTypeMeta{ + Tag: ir.EnumerationTypeTag, + Name: type_.Name.String(), + Size: type_.Size() * 8, + Align: type_.Align() * 8, + BaseType: t.getMeta(type_.ActualType), + Elements: cases, + } + + return t.cacheMeta(type_, typ) + + case *ast.Func: + this := type_.Method() + + parameterCount := len(type_.Params) + if this != nil { + parameterCount++ + } + + params := make([]ir.MetaID, parameterCount) + + if this != nil { + type_ := ast.Pointer{Pointee: this} + params[0] = t.getMeta(&type_) + } + + for index, param := range type_.Params { + i := index + if this != nil { + i++ + } + + params[i] = t.getMeta(param.Type) + } + + typ := &ir.SubroutineTypeMeta{ + Returns: t.getMeta(type_.Returns), + Params: params, + } + + return t.cacheMeta(type_, typ) + + default: + panic("codegen.types.getMeta() - Not implemented") + } +} + +func (t *types) cacheMeta(type_ ast.Type, meta ir.Meta) ir.MetaID { + id := t.module.Meta(meta) + + t.metadata = append(t.metadata, cachedTypeMeta{ + type_: type_, + id: id, + }) + + return id +} diff --git a/core/ir/block.go b/core/ir/block.go new file mode 100644 index 0000000..9318115 --- /dev/null +++ b/core/ir/block.go @@ -0,0 +1,26 @@ +package ir + +type Block struct { + name string + + Instructions []Inst +} + +func (b *Block) Add(inst Inst) Inst { + b.Instructions = append(b.Instructions, inst) + return inst +} + +// Value + +func (b *Block) Type() Type { + return nil +} + +func (b *Block) Name() string { + return b.name +} + +func (b *Block) SetName(name string) { + b.name = name +} diff --git a/core/ir/constants.go b/core/ir/constants.go new file mode 100644 index 0000000..5077183 --- /dev/null +++ b/core/ir/constants.go @@ -0,0 +1,103 @@ +package ir + +type Const interface { + Value + + isConst() +} + +// Null + +var Null = &NullConst{} +var nullType = &PointerType{Pointee: Void} + +type NullConst struct{} + +func (n *NullConst) Type() Type { + return nullType +} + +func (n *NullConst) Name() string { + return "" +} + +func (n *NullConst) isConst() {} + +// Int + +var True = &IntConst{Typ: I1, Value: Unsigned(1)} +var False = &IntConst{Typ: I1, Value: Unsigned(0)} + +type IntConst struct { + Typ Type + Value Int +} + +func (i *IntConst) Type() Type { + return i.Typ +} + +func (i *IntConst) Name() string { + return "" +} + +func (i *IntConst) isConst() {} + +// Float + +type FloatConst struct { + Typ Type + Value float64 +} + +func (f *FloatConst) Type() Type { + return f.Typ +} + +func (f *FloatConst) Name() string { + return "" +} + +func (f *FloatConst) isConst() {} + +// String + +type StringConst struct { + typ ArrayType + + Length uint32 + Value []byte +} + +func (s *StringConst) Type() Type { + if s.typ.Count == 0 { + s.typ = ArrayType{ + Count: s.Length, + Base: I8, + } + } + + return &s.typ +} + +func (s *StringConst) Name() string { + return "" +} + +func (s *StringConst) isConst() {} + +// Zero Initializer + +type ZeroInitConst struct { + Typ Type +} + +func (z *ZeroInitConst) Type() Type { + return z.Typ +} + +func (z *ZeroInitConst) Name() string { + return "" +} + +func (z *ZeroInitConst) isConst() {} diff --git a/core/ir/function.go b/core/ir/function.go new file mode 100644 index 0000000..c44919a --- /dev/null +++ b/core/ir/function.go @@ -0,0 +1,45 @@ +package ir + +type FuncFlags uint8 + +const ( + InlineFlag FuncFlags = 1 << iota + HotFlag +) + +type Func struct { + name string + Typ *FuncType + + Flags FuncFlags + meta MetaID + + Blocks []*Block +} + +func (f *Func) Block(name string) *Block { + f.Blocks = append(f.Blocks, &Block{name: name}) + return f.Blocks[len(f.Blocks)-1] +} + +// Value + +func (f *Func) Type() Type { + return f.Typ +} + +func (f *Func) Name() string { + return f.name +} + +func (f *Func) SetName(name string) { + f.name = name +} + +func (f *Func) Meta() MetaID { + return f.meta +} + +func (f *Func) SetMeta(id MetaID) { + f.meta = id +} diff --git a/core/ir/global_var.go b/core/ir/global_var.go new file mode 100644 index 0000000..0068d67 --- /dev/null +++ b/core/ir/global_var.go @@ -0,0 +1,38 @@ +package ir + +type GlobalVar struct { + name string + Typ Type + ptrType PointerType + + Value Value + Constant bool + + meta MetaID +} + +// Value + +func (g *GlobalVar) Type() Type { + if g.ptrType.Pointee == nil { + g.ptrType.Pointee = g.Typ + } + + return &g.ptrType +} + +func (g *GlobalVar) Name() string { + return g.name +} + +func (g *GlobalVar) SetName(name string) { + g.name = name +} + +func (g *GlobalVar) Meta() MetaID { + return g.meta +} + +func (g *GlobalVar) SetMeta(id MetaID) { + g.meta = id +} diff --git a/core/ir/helpers.go b/core/ir/helpers.go new file mode 100644 index 0000000..a7007d0 --- /dev/null +++ b/core/ir/helpers.go @@ -0,0 +1,43 @@ +package ir + +type Int struct { + Negative bool + Value uint64 +} + +func Signed(value int64) Int { + var v uint64 + + if value < 0 { + v = uint64(-value) + } else { + v = uint64(value) + } + + return Int{ + Negative: value < 0, + Value: v, + } +} + +func Unsigned(value uint64) Int { + return Int{Value: value} +} + +func indexType(typ Type, indices []uint32) Type { + if len(indices) == 0 { + return typ + } + + switch typ := typ.(type) { + case *PointerType: + return indexType(typ.Pointee, indices[1:]) + case *ArrayType: + return indexType(typ.Base, indices[1:]) + case *StructType: + return indexType(typ.Fields[indices[0]], indices[1:]) + + default: + return nil + } +} diff --git a/core/ir/instructions.go b/core/ir/instructions.go new file mode 100644 index 0000000..9e649ed --- /dev/null +++ b/core/ir/instructions.go @@ -0,0 +1,474 @@ +package ir + +type Inst interface { + NamedValue + MetaValue +} + +type baseInst struct { + name string + meta MetaID +} + +func (b *baseInst) Name() string { + return b.name +} + +func (b *baseInst) SetName(name string) { + b.name = name +} + +func (b *baseInst) Meta() MetaID { + return b.meta +} + +func (b *baseInst) SetMeta(id MetaID) { + b.meta = id +} + +// Ret + +type RetInst struct { + baseInst + + Value Value +} + +func (r *RetInst) Type() Type { + return nil +} + +// Br + +type BrInst struct { + baseInst + + Condition Value + True Value + False Value +} + +func (b *BrInst) Type() Type { + return nil +} + +// FNeg + +type FNegInst struct { + baseInst + + Value Value +} + +func (f *FNegInst) Type() Type { + return f.Value.Type() +} + +// Add + +type AddInst struct { + baseInst + + Left Value + Right Value +} + +func (a *AddInst) Type() Type { + return a.Left.Type() +} + +// Sub + +type SubInst struct { + baseInst + + Left Value + Right Value +} + +func (s *SubInst) Type() Type { + return s.Left.Type() +} + +// Mul + +type MulInst struct { + baseInst + + Left Value + Right Value +} + +func (m *MulInst) Type() Type { + return m.Left.Type() +} + +// IDiv + +type IDivInst struct { + baseInst + + Signed bool + + Left Value + Right Value +} + +func (i *IDivInst) Type() Type { + return i.Left.Type() +} + +// FDiv + +type FDivInst struct { + baseInst + + Left Value + Right Value +} + +func (f *FDivInst) Type() Type { + return f.Left.Type() +} + +// IRem + +type IRemInst struct { + baseInst + + Signed bool + + Left Value + Right Value +} + +func (i *IRemInst) Type() Type { + return i.Left.Type() +} + +// FRem + +type FRemInst struct { + baseInst + + Left Value + Right Value +} + +func (f *FRemInst) Type() Type { + return f.Left.Type() +} + +// Shl + +type ShlInst struct { + baseInst + + Left Value + Right Value +} + +func (s *ShlInst) Type() Type { + return s.Left.Type() +} + +// Shr + +type ShrInst struct { + baseInst + + SignExtend bool + + Left Value + Right Value +} + +func (s *ShrInst) Type() Type { + return s.Left.Type() +} + +// And + +type AndInst struct { + baseInst + + Left Value + Right Value +} + +func (s *AndInst) Type() Type { + return s.Left.Type() +} + +// Or + +type OrInst struct { + baseInst + + Left Value + Right Value +} + +func (s *OrInst) Type() Type { + return s.Left.Type() +} + +// Xor + +type XorInst struct { + baseInst + + Left Value + Right Value +} + +func (s *XorInst) Type() Type { + return s.Left.Type() +} + +// Extract Value + +type ExtractValueInst struct { + baseInst + + Value Value + Indices []uint32 +} + +func (e *ExtractValueInst) Type() Type { + return indexType(e.Value.Type(), e.Indices) +} + +// Insert Value + +type InsertValueInst struct { + baseInst + + Value Value + Element Value + Indices []uint32 +} + +func (i *InsertValueInst) Type() Type { + return i.Value.Type() +} + +// Alloca + +type AllocaInst struct { + baseInst + + Typ Type + TypPtr PointerType + Align uint32 +} + +func (a *AllocaInst) Type() Type { + if a.TypPtr.Pointee == nil { + a.TypPtr.Pointee = a.Typ + } + + return &a.TypPtr +} + +// Load + +type LoadInst struct { + baseInst + + Typ Type + Pointer Value + Align uint32 +} + +func (l *LoadInst) Type() Type { + return l.Typ +} + +// Store + +type StoreInst struct { + baseInst + + Pointer Value + Value Value + Align uint32 +} + +func (s *StoreInst) Type() Type { + return nil +} + +type GetElementPtrInst struct { + baseInst + + PointerTyp Type + Typ Type + + Pointer Value + Indices []Value + + Inbounds bool +} + +func (g *GetElementPtrInst) Type() Type { + return g.PointerTyp +} + +// Trunc + +type TruncInst struct { + baseInst + + Value Value + Typ Type +} + +func (t *TruncInst) Type() Type { + return t.Typ +} + +// Ext + +type ExtInst struct { + baseInst + + SignExtend bool + + Value Value + Typ Type +} + +func (e *ExtInst) Type() Type { + return e.Typ +} + +// FExt + +type FExtInst struct { + baseInst + + Value Value + Typ Type +} + +func (f *FExtInst) Type() Type { + return f.Typ +} + +// F2I + +type F2IInst struct { + baseInst + + Signed bool + + Value Value + Typ Type +} + +func (f *F2IInst) Type() Type { + return f.Typ +} + +// I2F + +type I2FInst struct { + baseInst + + Signed bool + + Value Value + Typ Type +} + +func (i *I2FInst) Type() Type { + return i.Typ +} + +// ICmp + +type CmpKind uint8 + +const ( + Eq CmpKind = iota + Ne + Gt + Ge + Lt + Le +) + +type ICmpInst struct { + baseInst + + Kind CmpKind + Signed bool + + Left Value + Right Value +} + +func (i *ICmpInst) Type() Type { + return I1 +} + +// FCmp + +type FCmpInst struct { + baseInst + + Kind CmpKind + Ordered bool + + Left Value + Right Value +} + +func (f *FCmpInst) Type() Type { + return I1 +} + +// Phi + +type Incoming struct { + Value Value + Label Value +} + +type PhiInst struct { + baseInst + + Incs []Incoming +} + +func (p *PhiInst) Type() Type { + return p.Incs[0].Value.Type() +} + +// Select + +type SelectInst struct { + baseInst + + Condition Value + True Value + False Value +} + +func (s *SelectInst) Type() Type { + return s.True.Type() +} + +// Call + +type CallInst struct { + baseInst + + Callee Value + Args []Value +} + +func (c *CallInst) Type() Type { + return c.Callee.Type().(*FuncType).Returns +} diff --git a/core/ir/metadata.go b/core/ir/metadata.go new file mode 100644 index 0000000..0c80ab9 --- /dev/null +++ b/core/ir/metadata.go @@ -0,0 +1,430 @@ +package ir + +// ID + +type MetaID uint16 + +func (m MetaID) Valid() bool { + return m > 0 +} + +func (m MetaID) Index() uint32 { + return uint32(m) - 1 +} + +func (m MetaID) Name() string { + return "" +} + +func (m MetaID) Type() Type { + return MetaT +} + +// Meta + +type Meta interface { + Index() uint32 + + isMeta() +} + +type metaImpl interface { + Meta + + setIndex(i uint32) +} + +type baseMeta struct { + index uint32 +} + +func (b *baseMeta) Index() uint32 { + return b.index +} + +func (b *baseMeta) setIndex(i uint32) { + b.index = i +} + +func (b *baseMeta) isMeta() {} + +// Inline + +type IntMeta struct { + baseMeta + + Value int32 +} + +type StringMeta struct { + baseMeta + + Value string +} + +func IsMetaInline(meta Meta) bool { + switch meta.(type) { + case *IntMeta, *StringMeta: + return true + default: + return false + } +} + +// Group + +type GroupMeta struct { + baseMeta + + Metadata []MetaID +} + +// Compile Unit + +type EmissionKind uint8 + +const ( + NoDebug EmissionKind = iota + FullDebug + LineTablesOnly + DebugDirectivesOnly +) + +func (e EmissionKind) String() string { + switch e { + case NoDebug: + return "NoDebug" + case FullDebug: + return "FullDebug" + case LineTablesOnly: + return "LineTablesOnly" + case DebugDirectivesOnly: + return "DebugDirectivesOnly" + + default: + panic("llvm.EmissionKind.String() - Not implemented") + } +} + +type NameTableKind uint8 + +const ( + Default NameTableKind = iota + GNU + None + Apple +) + +func (n NameTableKind) String() string { + switch n { + case Default: + return "Default" + case GNU: + return "GNU" + case None: + return "None" + case Apple: + return "Apple" + + default: + panic("llvm.NameTableKind.String() - Not implemented") + } +} + +type CompileUnitMeta struct { + baseMeta + + File MetaID + Producer string + + Emission EmissionKind + NameTable NameTableKind + + Globals []MetaID +} + +// File + +type FileMeta struct { + baseMeta + + Path string +} + +// Basic Type + +type EncodingKind uint8 + +const ( + AddressEncoding EncodingKind = 1 + BooleanEncoding = 2 + FloatEncoding = 4 + SignedEncoding = 5 + SignedCharEncoding = 6 + UnsignedEncoding = 7 + UnsignedCharEncoding = 8 +) + +func (e EncodingKind) String() string { + switch e { + case AddressEncoding: + return "DW_ATE_address" + case BooleanEncoding: + return "DW_ATE_boolean" + case FloatEncoding: + return "DW_ATE_float" + case SignedEncoding: + return "DW_ATE_signed" + case SignedCharEncoding: + return "DW_ATE_signed_char" + case UnsignedEncoding: + return "DW_ATE_unsigned" + case UnsignedCharEncoding: + return "DW_ATE_unsigned_char" + + default: + panic("llvm.EncodingKind.String() - Not implemented") + } +} + +type BasicTypeMeta struct { + baseMeta + + Name string + Encoding EncodingKind + + Size uint32 + Align uint32 +} + +// Subroutine Type + +type SubroutineTypeMeta struct { + baseMeta + + Returns MetaID + Params []MetaID +} + +// Derived Type + +type DerivedTagKind uint8 + +const ( + MemberTag DerivedTagKind = 13 + PointerTypeTag = 15 + ReferenceTypeTag = 16 + TypedefTag = 22 + InheritanceTag = 28 + PtrToMemberTypeTag = 31 + ConstTypeTag = 38 + FriendTag = 42 + VolatileTypeTag = 53 + RestrictTypeTag = 55 + AtomicTypeTag = 71 + ImmutableTypeTag = 75 +) + +func (d DerivedTagKind) String() string { + switch d { + case MemberTag: + return "DW_TAG_member" + case PointerTypeTag: + return "DW_TAG_pointer_type" + case ReferenceTypeTag: + return "DW_TAG_reference_type" + case TypedefTag: + return "DW_TAG_typedef" + case InheritanceTag: + return "DW_TAG_inheritance" + case PtrToMemberTypeTag: + return "DW_TAG_ptr_to_member_type" + case ConstTypeTag: + return "DW_TAG_const_type" + case FriendTag: + return "DW_TAG_friend" + case VolatileTypeTag: + return "DW_TAG_volatile_type" + case RestrictTypeTag: + return "DW_TAG_restrict_Type" + case AtomicTypeTag: + return "DW_TAG_atomic_type" + case ImmutableTypeTag: + return "DW_TAG_immutable_Type" + + default: + panic("llvm.DerivedTagKind.String() - Not implemented") + } +} + +type DerivedTypeMeta struct { + baseMeta + + Tag DerivedTagKind + BaseType MetaID + + Size uint32 + Align uint32 + + Offset uint32 +} + +// Composite Type + +type CompositeTagKind uint8 + +const ( + ArrayTypeTag CompositeTagKind = 1 + ClassTypeTag = 2 + EnumerationTypeTag = 4 + StructureTypeTag = 19 + UnionTypeTag = 23 +) + +func (c CompositeTagKind) String() string { + switch c { + case ArrayTypeTag: + return "DW_TAG_array_type" + case ClassTypeTag: + return "DW_TAG_class_type" + case EnumerationTypeTag: + return "DW_TAG_enumeration_type" + case StructureTypeTag: + return "DW_TAG_structure_type" + case UnionTypeTag: + return "DW_TAG_union_type" + + default: + panic("llvm.CompositeTagKind.String() - Not implemented") + } +} + +type CompositeTypeMeta struct { + baseMeta + + Tag CompositeTagKind + Name string + + File MetaID + Line uint32 + + Size uint32 + Align uint32 + + BaseType MetaID + Elements []MetaID +} + +// Subrange + +type SubrangeMeta struct { + baseMeta + + LowerBound uint32 + Count uint32 +} + +// Enumerator + +type EnumeratorMeta struct { + baseMeta + + Name string + Value Int +} + +// Namespace + +type NamespaceMeta struct { + baseMeta + + Name string + + Scope MetaID + File MetaID + Line uint32 +} + +// Global Var + +type GlobalVarMeta struct { + baseMeta + + Name string + LinkageName string + + Scope MetaID + File MetaID + Line uint32 + + Type MetaID + + Local bool + Definition bool +} + +// GlobalVarExpr + +type GlobalVarExpr struct { + baseMeta + + Var MetaID +} + +// Subprogram + +type SubprogamMeta struct { + baseMeta + + Name string + LinkageName string + + Scope MetaID + File MetaID + Line uint32 + + Type MetaID + + Unit MetaID +} + +// Lexical Block + +type LexicalBlockMeta struct { + baseMeta + + Scope MetaID + File MetaID + Line uint32 +} + +// Location + +type LocationMeta struct { + baseMeta + + Scope MetaID + Line uint32 + Column uint32 +} + +// Local Var + +type LocalVarMeta struct { + baseMeta + + Name string + Type MetaID + + Arg uint32 + + Scope MetaID + File MetaID + Line uint32 +} + +// Expression + +type ExpressionMeta struct { + baseMeta +} diff --git a/core/ir/module.go b/core/ir/module.go new file mode 100644 index 0000000..13e55d3 --- /dev/null +++ b/core/ir/module.go @@ -0,0 +1,91 @@ +package ir + +type Module struct { + Path string + + Structs []*StructType + + Globals []*GlobalVar + Functions []*Func + + NamedMetadata map[string]GroupMeta + Metadata []Meta + + metaIndex uint32 + expressionMeta MetaID +} + +func (m *Module) Struct(s *StructType) { + m.Structs = append(m.Structs, s) +} + +func (m *Module) Constant(name string, value Value) *GlobalVar { + m.Globals = append(m.Globals, &GlobalVar{ + name: name, + Typ: value.Type(), + ptrType: PointerType{Pointee: value.Type()}, + Value: value, + Constant: true, + }) + + return m.Globals[len(m.Globals)-1] +} + +func (m *Module) Global(name string, typ Type, value Value) *GlobalVar { + m.Globals = append(m.Globals, &GlobalVar{ + name: name, + Typ: typ, + Value: value, + }) + + return m.Globals[len(m.Globals)-1] +} + +func (m *Module) Define(name string, typ *FuncType, flags FuncFlags) *Func { + m.Functions = append(m.Functions, &Func{ + name: name, + Typ: typ, + Flags: flags, + }) + + return m.Functions[len(m.Functions)-1] +} + +func (m *Module) Declare(name string, typ *FuncType) *Func { + m.Functions = append(m.Functions, &Func{ + name: name, + Typ: typ, + }) + + return m.Functions[len(m.Functions)-1] +} + +func (m *Module) NamedMeta(name string, metadata []MetaID) { + if m.NamedMetadata == nil { + m.NamedMetadata = make(map[string]GroupMeta) + } + + m.NamedMetadata[name] = GroupMeta{Metadata: metadata} +} + +func (m *Module) Meta(meta Meta) MetaID { + if _, ok := meta.(*ExpressionMeta); ok { + if !m.expressionMeta.Valid() { + meta.(metaImpl).setIndex(m.metaIndex) + m.metaIndex++ + + m.Metadata = append(m.Metadata, meta) + m.expressionMeta = MetaID(len(m.Metadata)) + } + + return m.expressionMeta + } + + if !IsMetaInline(meta) { + meta.(metaImpl).setIndex(m.metaIndex) + m.metaIndex++ + } + + m.Metadata = append(m.Metadata, meta) + return MetaID(len(m.Metadata)) +} diff --git a/core/ir/types.go b/core/ir/types.go new file mode 100644 index 0000000..2fbe664 --- /dev/null +++ b/core/ir/types.go @@ -0,0 +1,99 @@ +package ir + +type Type interface { + isType() +} + +// Void + +var Void = &VoidType{} + +type VoidType struct{} + +func (v *VoidType) isType() {} + +// Int + +var I1 = &IntType{BitSize: 1} +var I8 = &IntType{BitSize: 8} +var I16 = &IntType{BitSize: 16} +var I32 = &IntType{BitSize: 32} +var I64 = &IntType{BitSize: 64} + +type IntType struct { + BitSize uint8 +} + +func (i *IntType) isType() {} + +// Float + +var F32 = &FloatType{BitSize: 32} +var F64 = &FloatType{BitSize: 64} + +type FloatType struct { + BitSize uint8 +} + +func (f *FloatType) isType() {} + +// Pointer + +type PointerType struct { + Pointee Type +} + +func (p *PointerType) isType() {} + +// Array + +type ArrayType struct { + Count uint32 + Base Type +} + +func (a *ArrayType) isType() {} + +// Struct + +type StructType struct { + Name string + Fields []Type +} + +func (s *StructType) isType() {} + +// Function + +type Param struct { + Typ Type + Name_ string +} + +func (p *Param) Type() Type { + return p.Typ +} + +func (p *Param) Name() string { + return p.Name_ +} + +func (p *Param) SetName(name string) { + p.Name_ = name +} + +type FuncType struct { + Returns Type + Params []*Param + Variadic bool +} + +func (f *FuncType) isType() {} + +// Meta + +var MetaT = &MetaType{} + +type MetaType struct{} + +func (m *MetaType) isType() {} diff --git a/core/ir/value.go b/core/ir/value.go new file mode 100644 index 0000000..5704fdb --- /dev/null +++ b/core/ir/value.go @@ -0,0 +1,19 @@ +package ir + +type Value interface { + Type() Type + Name() string +} + +type NamedValue interface { + Value + + SetName(name string) +} + +type MetaValue interface { + Value + + Meta() MetaID + SetMeta(id MetaID) +} diff --git a/core/llvm/constants.go b/core/llvm/constants.go new file mode 100644 index 0000000..b1f1648 --- /dev/null +++ b/core/llvm/constants.go @@ -0,0 +1,73 @@ +package llvm + +import ( + "fireball/core/ir" + "math" +) + +func (w *textWriter) writeConst(c ir.Const) { + switch c := c.(type) { + case *ir.NullConst: + w.writeString("null") + + case *ir.IntConst: + if c.Type().(*ir.IntType).BitSize == 1 { + if c.Value.Value == 1 { + w.writeString("true") + } else { + w.writeString("false") + } + } else { + if c.Value.Negative { + w.writeRune('-') + } + + w.writeUint(c.Value.Value, 10) + } + + case *ir.FloatConst: + switch c.Type().(*ir.FloatType).BitSize { + case 32: + bits := math.Float64bits(c.Value) + + w.writeString("0x") + w.writeUint(bits, 16) + + case 64: + bits := math.Float64bits(c.Value) + + w.writeString("0x") + w.writeUint(bits, 16) + + default: + panic("ir.writeConst() - FloatConst - Not implemented") + } + + case *ir.StringConst: + w.writeString("c\"") + + for _, b := range c.Value { + switch b { + case '\000': + w.writeString("\\00") + case '\n': + w.writeString("\\0A") + case '\r': + w.writeString("\\0D") + case '\t': + w.writeString("\\09") + + default: + w.writeByte(b) + } + } + + w.writeRune('"') + + case *ir.ZeroInitConst: + w.writeString("zeroinitializer") + + default: + panic("ir.writeConst() - Not implemented") + } +} diff --git a/core/llvm/function.go b/core/llvm/function.go index 3510396..7eb5673 100644 --- a/core/llvm/function.go +++ b/core/llvm/function.go @@ -1,406 +1,120 @@ package llvm -import ( - "fmt" - "math" - "strconv" -) +import "fireball/core/ir" -type Literal struct { - Signed int64 - Unsigned uint64 - Floating float64 -} - -type Function struct { - module *Module - - type_ *functionType - - parameters []NameableValue - blocks []*Block - - alwaysInline bool - metadata int -} - -func (f *Function) Kind() ValueKind { - return GlobalValue -} - -func (f *Function) Type() Type { - return f.type_ -} - -func (f *Function) Name() string { - return f.type_.name -} - -func (f *Function) GetParameter(index int) NameableValue { - return f.parameters[index] -} - -func (f *Function) PushScope() { - f.module.scopes = append(f.module.scopes, f.metadata) -} - -func (f *Function) PopScope() { - f.module.scopes = f.module.scopes[:len(f.module.scopes)-1] -} - -func (f *Function) SetAlwaysInline() { - f.alwaysInline = true -} - -func (f *Function) Block(name string) *Block { - // TODO: If I use named blocks then LLVM for some reason reports that a terminator instruction is inside the middle - // of a basic block even tho it works fine when the blocks are unnamed. - - b := &Block{ - module: f.module, - name: "", - instructions: make([]Value, 0, 8), - } - - f.blocks = append(f.blocks, b) - return b -} - -// Literals - -type literal struct { - type_ Type - data string -} - -func (l *literal) Kind() ValueKind { - return LiteralValue -} - -func (l *literal) Type() Type { - return l.type_ -} - -func (l *literal) Name() string { - return l.data -} - -func (f *Function) Literal(type_ Type, data Literal) Value { - if isSigned(type_) { - return &literal{ - type_: type_, - data: strconv.FormatInt(data.Signed, 10), - } - } - - if isUnsigned(type_) { - return &literal{ - type_: type_, - data: strconv.FormatUint(data.Unsigned, 10), - } - } - - if isFloating(type_) { - return &literal{ - type_: type_, - data: fmt.Sprintf("0x%X", math.Float64bits(data.Floating)), - } - } - - panic("llvm.Function.Literal() - Invalid literal") -} - -func (f *Function) LiteralRaw(type_ Type, data string) Value { - return &literal{ - type_: type_, - data: data, - } -} +func (w *textWriter) writeFunctions() { + // Define + for _, function := range w.m.Functions { + if len(function.Blocks) != 0 { + w.resetLocalNames() -// Block + w.writeString("define ") + w.writeFunction(function) -type Block struct { - module *Module - - name string - instructions []Value -} - -func (b *Block) Kind() ValueKind { - return LocalValue -} - -func (b *Block) Type() Type { - panic("llvm.Block.Type() - Blocks dont have types") -} - -func (b *Block) Name() string { - return b.name -} - -func (b *Block) Variable(name string, pointer Value) Instruction { - i := &variableMetadata{ - instruction: instruction{ - module: b.module, - location: -1, - }, - pointer: pointer, - metadata: b.module.addMetadata(Metadata{ - Type: "DILocalVariable", - Fields: []MetadataField{ - { - Name: "name", - Value: stringMetadataValue(name), - }, - { - Name: "type", - Value: refMetadataValue(b.module.typeMetadata[pointer.Type().(*pointerType).pointee]), - }, - { - Name: "scope", - Value: refMetadataValue(b.module.getScope()), - }, - { - Name: "file", - Value: refMetadataValue(b.module.getFile()), - }, - }, - }), - } - - b.instructions = append(b.instructions, i) - return i -} + if function.Meta().Valid() { + w.writeString(" !dbg ") + w.writeMetaRef(function.Meta()) + } -func (b *Block) Lifetime(pointer Value, start bool) Instruction { - i := &lifetimeMetadata{ - instruction: instruction{ - module: b.module, - location: -1, - }, - pointer: pointer, - start: start, - } + w.writeString(" {\n") - b.instructions = append(b.instructions, i) - return i -} + for _, param := range function.Typ.Params { + w.cacheName(param, false) + } -func (b *Block) FNeg(value Value) InstructionValue { - i := &fNeg{ - instruction: instruction{ - module: b.module, - type_: value.Type(), - location: -1, - }, - value: value, - } + for _, block := range function.Blocks { + w.cacheName(block, false) - b.instructions = append(b.instructions, i) - return i -} + for _, inst := range block.Instructions { + if _, ok := inst.Type().(*ir.VoidType); inst.Type() != nil && !ok { + w.cacheName(inst, false) + } + } + } -func (b *Block) Binary(op BinaryKind, left, right Value) InstructionValue { - var type_ Type + for i, block := range function.Blocks { + if i > 0 { + w.writeRune('\n') + } - switch op { - case Eq, Ne, Lt, Le, Gt, Ge: - for _, t := range b.module.types { - if t2, ok := t.(*primitiveType); ok && t2.encoding == BooleanEncoding { - type_ = t2 + w.writeBlock(block) } - } - if type_ == nil { - type_ = b.module.Primitive("i1", 1, BooleanEncoding) + w.writeString("}\n\n") } - default: - type_ = left.Type() - } - - i := &binary{ - instruction: instruction{ - module: b.module, - type_: type_, - location: -1, - }, - op: op, - left: left, - right: right, - } - - b.instructions = append(b.instructions, i) - return i -} - -func (b *Block) Cast(kind CastKind, value Value, to Type) InstructionValue { - i := &cast{ - instruction: instruction{ - module: b.module, - type_: to, - location: -1, - }, - kind: kind, - value: value, } - b.instructions = append(b.instructions, i) - return i -} + // Declare + count := 0 -func (b *Block) ExtractValue(value Value, index int) InstructionValue { - var type_ Type + for _, function := range w.m.Functions { + if len(function.Blocks) == 0 { + w.resetLocalNames() - if v, ok := value.Type().(*arrayType); ok { - type_ = v.base - } else if v, ok := value.Type().(*structType); ok { - type_ = v.fields[index].Type - } + w.writeString("declare ") + w.writeFunction(function) + w.writeRune('\n') - i := &extractValue{ - instruction: instruction{ - module: b.module, - type_: type_, - location: -1, - }, - value: value, - index: index, + count++ + } } - b.instructions = append(b.instructions, i) - return i -} - -func (b *Block) InsertValue(value, element Value, index int) InstructionValue { - i := &insertValue{ - instruction: instruction{ - module: b.module, - type_: value.Type(), - location: -1, - }, - value: value, - element: element, - index: index, + if count > 0 { + w.writeRune('\n') } - - b.instructions = append(b.instructions, i) - return i } -func (b *Block) Alloca(type_ Type) AlignedInstructionValue { - i := &alloca{ - instruction: instruction{ - module: b.module, - type_: &pointerType{pointee: type_}, - location: -1, - }, - type_: type_, - } +func (w *textWriter) writeFunction(function *ir.Func) { + // Return type + w.writeType(function.Typ.Returns) + w.writeRune(' ') - b.instructions = append(b.instructions, i) - return i -} + // Name + w.writeName(function) -func (b *Block) Load(pointer Value) AlignedInstructionValue { - i := &load{ - instruction: instruction{ - module: b.module, - type_: pointer.Type().(*pointerType).pointee, - location: -1, - }, - pointer: pointer, - } + // Parameters + w.writeRune('(') - b.instructions = append(b.instructions, i) - return i -} + for i, param := range function.Typ.Params { + if i > 0 { + w.writeString(", ") + } -func (b *Block) Store(pointer Value, value Value) AlignedInstruction { - i := &store{ - instruction: instruction{ - module: b.module, - location: -1, - }, - pointer: pointer, - value: value, + w.writeType(param.Typ) + w.writeRune(' ') + w.writeName(param) } - b.instructions = append(b.instructions, i) - return i -} - -func (b *Block) GetElementPtr(pointer Value, indices []Value, pointerType Type, type_ Type) InstructionValue { - i := &getElementPtr{ - instruction: instruction{ - module: b.module, - type_: pointerType, - location: -1, - }, - type_: type_, - pointer: pointer, - indices: indices, + if function.Typ.Variadic { + if len(function.Typ.Params) > 0 { + w.writeString(", ...") + } else { + w.writeString("...") + } } - b.instructions = append(b.instructions, i) - return i -} + w.writeRune(')') -func (b *Block) Br(condition Value, true *Block, false *Block) Instruction { - i := &br{ - instruction: instruction{ - module: b.module, - location: -1, - }, - condition: condition, - true: true, - false: false, + // Flags + if function.Flags&ir.InlineFlag != 0 { + w.writeString(" alwaysinline") } - - b.instructions = append(b.instructions, i) - return i -} - -func (b *Block) Phi(firstValue Value, firstBlock *Block, secondValue Value, secondBlock *Block) InstructionValue { - i := &phi{ - instruction: instruction{ - module: b.module, - type_: firstValue.Type(), - location: -1, - }, - firstValue: firstValue, - firstBlock: firstBlock, - secondValue: secondValue, - secondBlock: secondBlock, + if function.Flags&ir.HotFlag != 0 { + w.writeString(" hot") } - b.instructions = append(b.instructions, i) - return i + // Metadata } -func (b *Block) Call(value Value, arguments []Value, returns Type) InstructionValue { - i := &call{ - instruction: instruction{ - module: b.module, - type_: returns, - location: -1, - }, - value: value, - arguments: arguments, - } - - b.instructions = append(b.instructions, i) - return i -} +func (w *textWriter) writeBlock(block *ir.Block) { + w.skipNameChar = true + w.writeName(block) + w.writeString(":\n") + w.skipNameChar = false -func (b *Block) Ret(value Value) Instruction { - i := &ret{ - instruction: instruction{ - module: b.module, - location: -1, - }, - value: value, + for _, inst := range block.Instructions { + w.writeString(" ") + w.writeInstruction(inst) + w.writeRune('\n') } - - b.instructions = append(b.instructions, i) - return i } diff --git a/core/llvm/globals.go b/core/llvm/globals.go new file mode 100644 index 0000000..be264d9 --- /dev/null +++ b/core/llvm/globals.go @@ -0,0 +1,64 @@ +package llvm + +import "fireball/core/ir" + +func (w *textWriter) writeGlobals() { + // Constants + count := 0 + + for _, global := range w.m.Globals { + if global.Constant { + w.writeGlobalConstant(global) + count++ + } + } + + if count > 0 { + w.writeRune('\n') + } + + // Variables + count = 0 + + for _, global := range w.m.Globals { + if !global.Constant { + w.writeGlobalVar(global) + count++ + } + } + + if count > 0 { + w.writeRune('\n') + } +} + +func (w *textWriter) writeGlobalConstant(global *ir.GlobalVar) { + w.writeName(global) + w.writeString(" = private unnamed_addr constant ") + w.writeType(global.Typ) + w.writeRune(' ') + w.writeConst(global.Value.(ir.Const)) + w.writeRune('\n') +} + +func (w *textWriter) writeGlobalVar(global *ir.GlobalVar) { + w.writeName(global) + w.writeString(" = ") + + if global.Value == nil { + w.writeString("external global ") + w.writeType(global.Typ) + } else { + w.writeString("global ") + w.writeType(global.Typ) + w.writeRune(' ') + w.writeConst(global.Value.(ir.Const)) + } + + if global.Meta().Valid() { + w.writeString(", !dbg ") + w.writeMetaRef(global.Meta()) + } + + w.writeRune('\n') +} diff --git a/core/llvm/instructions.go b/core/llvm/instructions.go index 4e14de5..3d7d097 100644 --- a/core/llvm/instructions.go +++ b/core/llvm/instructions.go @@ -1,202 +1,403 @@ package llvm -import ( - "fireball/core/cst" -) +import "fireball/core/ir" -type instruction struct { - module *Module - - type_ Type - name string - - location int -} - -func (i *instruction) Kind() ValueKind { - return LocalValue -} - -func (i *instruction) Type() Type { - return i.type_ -} - -func (i *instruction) Name() string { - return i.name -} - -func (i *instruction) SetName(name string) { - i.name = name -} - -func (i *instruction) SetLocation(node *cst.Node) { - fields := []MetadataField{ - { - Name: "scope", - Value: refMetadataValue(i.module.getScope()), - }, +func (w *textWriter) writeInstruction(inst ir.Inst) { + // Value + if _, ok := inst.Type().(*ir.VoidType); inst.Type() != nil && !ok { + w.writeName(inst) + w.writeString(" = ") } - if node != nil { - fields = append(fields, MetadataField{ - Name: "line", - Value: numberMetadataValue(int(node.Range.Start.Line)), - }) - fields = append(fields, MetadataField{ - Name: "column", - Value: numberMetadataValue(int(node.Range.Start.Column)), - }) + // Instruction + switch inst := inst.(type) { + case *ir.RetInst: + if inst.Value == nil { + w.writeString("ret void") + } else { + w.writeString("ret ") + w.writeValue(inst.Value) + } + + case *ir.BrInst: + if inst.Condition == nil { + w.writeString("br ") + w.writeValue(inst.True) + } else { + w.writeString("br ") + w.writeValue(inst.Condition) + w.writeString(", ") + w.writeValue(inst.True) + w.writeString(", ") + w.writeValue(inst.False) + } + + case *ir.FNegInst: + w.writeString("fneg ") + w.writeValue(inst.Value) + + case *ir.AddInst: + if _, ok := inst.Type().(*ir.FloatType); ok { + w.writeString("fadd ") + } else { + w.writeString("add ") + } + + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.SubInst: + if _, ok := inst.Type().(*ir.FloatType); ok { + w.writeString("fsub ") + } else { + w.writeString("sub ") + } + + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.MulInst: + if _, ok := inst.Type().(*ir.FloatType); ok { + w.writeString("fmul ") + } else { + w.writeString("mul ") + } + + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.IDivInst: + if inst.Signed { + w.writeString("sdiv ") + } else { + w.writeString("udiv ") + } + + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.FDivInst: + w.writeString("fdiv ") + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.IRemInst: + if inst.Signed { + w.writeString("srem ") + } else { + w.writeString("urem ") + } + + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.FRemInst: + w.writeString("frem ") + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.ShlInst: + w.writeString("shl ") + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.ShrInst: + if inst.SignExtend { + w.writeString("ashr") + } else { + w.writeString("lshr") + } + + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.AndInst: + w.writeString("add ") + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.OrInst: + w.writeString("or ") + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.XorInst: + w.writeString("xor ") + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.ExtractValueInst: + w.writeString("extractvalue ") + w.writeValue(inst.Value) + + for i, index := range inst.Indices { + if i > 0 { + w.writeString(", ") + } + + w.writeUint(uint64(index), 10) + } + + case *ir.InsertValueInst: + w.writeString("insertvalue ") + w.writeValue(inst.Value) + w.writeString(", ") + w.writeValue(inst.Element) + + for _, index := range inst.Indices { + w.writeString(", ") + w.writeUint(uint64(index), 10) + } + + case *ir.AllocaInst: + w.writeString("alloca ") + w.writeType(inst.Typ) + + if inst.Align != 0 { + w.writeString(", align ") + w.writeUint(uint64(inst.Align), 10) + } + + case *ir.LoadInst: + w.writeString("load ") + w.writeType(inst.Typ) + w.writeString(", ") + w.writeValue(inst.Pointer) + + if inst.Align != 0 { + w.writeString(", align ") + w.writeUint(uint64(inst.Align), 10) + } + + case *ir.StoreInst: + w.writeString("store ") + w.writeValue(inst.Value) + w.writeString(", ") + w.writeValue(inst.Pointer) + + if inst.Align != 0 { + w.writeString(", align ") + w.writeUint(uint64(inst.Align), 10) + } + + case *ir.GetElementPtrInst: + w.writeString("getelementptr ") + + if inst.Inbounds { + w.writeString("inbounds ") + } + + w.writeType(inst.Typ) + w.writeString(", ") + w.writeValue(inst.Pointer) + + for _, index := range inst.Indices { + w.writeString(", ") + w.writeValue(index) + } + + case *ir.TruncInst: + if _, ok := inst.Typ.(*ir.FloatType); ok { + w.writeString("fptrunc ") + } else { + w.writeString("trunc ") + } + + w.writeValue(inst.Value) + w.writeString(" to ") + w.writeType(inst.Typ) + + case *ir.ExtInst: + if inst.SignExtend { + w.writeString("sext ") + } else { + w.writeString("zext ") + } + + w.writeValue(inst.Value) + w.writeString(" to ") + w.writeType(inst.Typ) + + case *ir.FExtInst: + w.writeString("fpext ") + w.writeValue(inst.Value) + w.writeString(" to ") + w.writeType(inst.Typ) + + case *ir.F2IInst: + if inst.Signed { + w.writeString("fptosi ") + } else { + w.writeString("fptoui ") + } + + w.writeValue(inst.Value) + w.writeString(" to ") + w.writeType(inst.Typ) + + case *ir.I2FInst: + if inst.Signed { + w.writeString("sitofp ") + } else { + w.writeString("uitofp ") + } + + w.writeValue(inst.Value) + w.writeString(" to ") + w.writeType(inst.Typ) + + case *ir.ICmpInst: + w.writeString("icmp ") + + if inst.Signed { + switch inst.Kind { + case ir.Eq: + w.writeString("eq ") + case ir.Ne: + w.writeString("ne ") + case ir.Gt: + w.writeString("sgt ") + case ir.Ge: + w.writeString("sge ") + case ir.Lt: + w.writeString("slt ") + case ir.Le: + w.writeString("sle ") + } + } else { + switch inst.Kind { + case ir.Eq: + w.writeString("eq ") + case ir.Ne: + w.writeString("ne ") + case ir.Gt: + w.writeString("ugt ") + case ir.Ge: + w.writeString("uge ") + case ir.Lt: + w.writeString("ult ") + case ir.Le: + w.writeString("ule ") + } + } + + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.FCmpInst: + w.writeString("fcmp ") + + if inst.Ordered { + switch inst.Kind { + case ir.Eq: + w.writeString("oeq ") + case ir.Ne: + w.writeString("one ") + case ir.Gt: + w.writeString("ogt ") + case ir.Ge: + w.writeString("oge ") + case ir.Lt: + w.writeString("olt ") + case ir.Le: + w.writeString("ole ") + } + } else { + switch inst.Kind { + case ir.Eq: + w.writeString("ueq ") + case ir.Ne: + w.writeString("une ") + case ir.Gt: + w.writeString("ugt ") + case ir.Ge: + w.writeString("uge ") + case ir.Lt: + w.writeString("ult ") + case ir.Le: + w.writeString("ule ") + } + } + + w.writeValue(inst.Left) + w.writeString(", ") + w.writeValueValue(inst.Right) + + case *ir.PhiInst: + w.writeString("phi ") + w.writeType(inst.Type()) + + for i, incoming := range inst.Incs { + if i > 0 { + w.writeString(", [ ") + } else { + w.writeString(" [ ") + } + + w.writeValueValue(incoming.Value) + w.writeString(", ") + w.writeValueValue(incoming.Label) + + w.writeString(" ]") + } + + case *ir.SelectInst: + w.writeString("select ") + w.writeValue(inst.Condition) + w.writeString(", ") + w.writeValue(inst.True) + w.writeString(", ") + w.writeValue(inst.False) + + case *ir.CallInst: + type_ := inst.Callee.Type().(*ir.FuncType) + + w.writeString("call ") + w.writeType(type_.Returns) + w.writeRune(' ') + w.writeName(inst.Callee) + w.writeRune('(') + + for i, arg := range inst.Args { + if i > 0 { + w.writeString(", ") + } + + if i < len(type_.Params) { + param := type_.Params[i] + + if _, ok := param.Type().(*ir.MetaType); ok { + if _, ok := arg.(ir.MetaID); !ok { + w.writeString("metadata ") + } + } + } + + w.writeValue(arg) + } + + w.writeRune(')') + + default: + panic("ir.writeInstruction() - Not implemented") } - i.location = i.module.addMetadata(Metadata{ - Type: "DILocation", - Fields: fields, - }) -} - -type variableMetadata struct { - instruction - pointer Value - metadata int -} - -type lifetimeMetadata struct { - instruction - pointer Value - start bool -} - -type BinaryKind uint8 - -type fNeg struct { - instruction - value Value -} - -const ( - Add BinaryKind = iota - Sub - Mul - Div - Rem - - Eq - Ne - Lt - Le - Gt - Ge - - Or - Xor - And - Shl - Shr -) - -type binary struct { - instruction - op BinaryKind - left Value - right Value -} - -type CastKind uint8 - -const ( - Trunc CastKind = iota - ZExt - SExt - FpTrunc - FpExt - FpToUi - FpToSi - UiToFp - SiToFp - PtrToInt - IntToPtr - Bitcast -) - -type cast struct { - instruction - kind CastKind - value Value -} - -type extractValue struct { - instruction - value Value - index int -} - -type insertValue struct { - instruction - value Value - element Value - index int -} - -type alloca struct { - instruction - type_ Type - align uint32 -} - -func (a *alloca) SetAlign(align uint32) { - a.align = align -} - -type load struct { - instruction - pointer Value - align uint32 -} - -func (l *load) SetAlign(align uint32) { - l.align = align -} - -type store struct { - instruction - pointer Value - value Value - align uint32 -} - -func (s *store) SetAlign(align uint32) { - s.align = align -} - -type getElementPtr struct { - instruction - type_ Type - pointer Value - indices []Value -} - -type br struct { - instruction - condition Value - true *Block - false *Block -} - -type phi struct { - instruction - firstValue Value - firstBlock *Block - secondValue Value - secondBlock *Block -} - -type call struct { - instruction - value Value - arguments []Value -} - -type ret struct { - instruction - value Value + // Metadata + if inst.Meta().Valid() { + w.writeString(", !dbg ") + w.writeMetaRef(inst.Meta()) + } } diff --git a/core/llvm/main.go b/core/llvm/main.go new file mode 100644 index 0000000..e3fc75e --- /dev/null +++ b/core/llvm/main.go @@ -0,0 +1,31 @@ +package llvm + +import ( + "bufio" + "fireball/core/ir" + "io" +) + +func WriteText(m *ir.Module, writer io.Writer) { + w := textWriter{ + m: m, + w: bufio.NewWriter(writer), + + globalNameCounts: make(map[string]uint64), + globalNames: make(map[ir.Value]Name), + + localNameCounts: make(map[string]uint64), + localNames: make(map[ir.Value]Name), + } + + w.writeString("source_filename = \"") + w.writeString(m.Path) + w.writeString("\"\n\n") + + w.writeStructs() + w.writeGlobals() + w.writeFunctions() + w.writeMetadata() + + _ = w.w.Flush() +} diff --git a/core/llvm/metadata.go b/core/llvm/metadata.go index 93f282c..887614b 100644 --- a/core/llvm/metadata.go +++ b/core/llvm/metadata.go @@ -1,115 +1,379 @@ package llvm -import "path/filepath" - -type MetadataValueKind uint8 - -const ( - StringMetadataValueKind MetadataValueKind = iota - EnumMetadataValueKind - NumberMetadataValueKind - RefMetadataValueKind +import ( + "fireball/core/ir" + "path/filepath" ) -type MetadataValue struct { - Kind MetadataValueKind +func (w *textWriter) writeMetadata() { + for name, meta := range w.m.NamedMetadata { + w.writeRune('!') + w.writeString(name) + w.writeString(" = ") + w.writeMeta(&meta) + w.writeRune('\n') + } - String string - Number int -} + if len(w.m.NamedMetadata) > 0 { + w.writeRune('\n') + } -type MetadataField struct { - Name string - Value MetadataValue + for _, meta := range w.m.Metadata { + if !ir.IsMetaInline(meta) { + w.writeRune('!') + w.writeUint(uint64(meta.Index()), 10) + w.writeString(" = ") + w.writeMeta(meta) + w.writeRune('\n') + } + } } -type Metadata struct { - Distinct bool - Type string - Fields []MetadataField -} +func (w *textWriter) writeMeta(meta ir.Meta) { + switch meta := meta.(type) { + case *ir.GroupMeta: + w.writeString("!{") -func fileMetadata(path string) Metadata { - return Metadata{ - Type: "DIFile", - Fields: []MetadataField{ - { - Name: "filename", - Value: stringMetadataValue(filepath.Base(path)), - }, - { - Name: "directory", - Value: stringMetadataValue(filepath.Dir(path)), - }, - }, - } -} + for i, childId := range meta.Metadata { + if i > 0 { + w.writeString(", ") + } -func compileUnitMetadata(file int, producer string) Metadata { - return Metadata{ - Distinct: true, - Type: "DICompileUnit", - Fields: []MetadataField{ - { - Name: "language", - Value: enumMetadataValue("DW_LANG_C"), - }, - { - Name: "file", - Value: refMetadataValue(file), - }, - { - Name: "producer", - Value: stringMetadataValue(producer), - }, - { - Name: "isOptimized", - Value: enumMetadataValue("false"), - }, - { - Name: "runtimeVersion", - Value: numberMetadataValue(0), - }, - { - Name: "emissionKind", - Value: enumMetadataValue("FullDebug"), - }, - { - Name: "splitDebugInlining", - Value: enumMetadataValue("false"), - }, - { - Name: "nameTableKind", - Value: enumMetadataValue("None"), - }, - }, - } -} + switch child := w.m.Metadata[childId-1].(type) { + case *ir.IntMeta: + w.writeString("i32 ") + w.writeInt(int64(child.Value)) -func stringMetadataValue(str string) MetadataValue { - return MetadataValue{ - Kind: StringMetadataValueKind, - String: str, - } -} + case *ir.StringMeta: + w.writeRune('!') + w.writeQuotedString(child.Value) -func enumMetadataValue(str string) MetadataValue { - return MetadataValue{ - Kind: EnumMetadataValueKind, - String: str, - } -} + default: + w.writeMetaRef(childId) + } + } + + w.writeRune('}') + + case *ir.CompileUnitMeta: + w.writeString("distinct !DICompileUnit(") + + w.writeString("file: ") + w.writeMetaRef(meta.File) + + w.writeString(", producer: ") + w.writeQuotedString(meta.Producer) + + w.writeString(", emissionKind: ") + w.writeString(meta.Emission.String()) + + w.writeString(", nameTableKind: ") + w.writeString(meta.NameTable.String()) + + if len(meta.Globals) > 0 { + w.writeString(", globals: !{") + + for i, global := range meta.Globals { + if i > 0 { + w.writeString(", ") + } + + w.writeMetaRef(global) + } + + w.writeRune('}') + } + + w.writeString(", language: ") + w.writeString("DW_LANG_C") + + w.writeString(", isOptimized: ") + w.writeBool(false) + + w.writeString(", runtimeVersion: ") + w.writeUint(0, 10) + + w.writeString(", splitDebugInlining: ") + w.writeBool(false) + + w.writeRune(')') + + case *ir.FileMeta: + w.writeString("distinct !DIFile(") + + w.writeString("filename: ") + w.writeQuotedString(filepath.Base(meta.Path)) + + w.writeString(", directory: ") + w.writeQuotedString(filepath.Dir(meta.Path)) + + w.writeRune(')') + + case *ir.BasicTypeMeta: + w.writeString("!DIBasicType(") + + w.writeString("name: ") + w.writeQuotedString(meta.Name) + + w.writeString(", encoding: ") + w.writeString(meta.Encoding.String()) + + w.writeString(", size: ") + w.writeUint(uint64(meta.Size), 10) + + w.writeString(", align: ") + w.writeUint(uint64(meta.Align), 10) + + w.writeRune(')') + + case *ir.SubroutineTypeMeta: + w.writeString("!DISubroutineType(types: !{") + + w.writeMetaRef(meta.Returns) + + for _, param := range meta.Params { + w.writeString(", ") + w.writeMetaRef(param) + } + + w.writeString("})") + + case *ir.DerivedTypeMeta: + w.writeString("!DIDerivedType(") + + w.writeString("tag: ") + w.writeString(meta.Tag.String()) + + w.writeString(", baseType: ") + w.writeMetaRef(meta.BaseType) + + w.writeString(", size: ") + w.writeUint(uint64(meta.Size), 10) + + w.writeString(", align: ") + w.writeUint(uint64(meta.Align), 10) + + w.writeRune(')') + + case *ir.CompositeTypeMeta: + w.writeString("!DICompositeType(") + + w.writeString("tag: ") + w.writeString(meta.Tag.String()) + + w.writeString(", name: ") + w.writeQuotedString(meta.Name) + + w.writeString(", file: ") + w.writeMetaRef(meta.File) + + w.writeString(", line: ") + w.writeUint(uint64(meta.Line), 10) + + w.writeString(", size: ") + w.writeUint(uint64(meta.Size), 10) + + w.writeString(", align: ") + w.writeUint(uint64(meta.Align), 10) + + if meta.BaseType.Valid() { + w.writeString(", baseType: ") + w.writeMetaRef(meta.BaseType) + } + if len(meta.Elements) != 0 { + w.writeString(", elements: !{") + + for i, element := range meta.Elements { + if i > 0 { + w.writeString(", ") + } + + w.writeMetaRef(element) + } -func numberMetadataValue(num int) MetadataValue { - return MetadataValue{ - Kind: NumberMetadataValueKind, - Number: num, + w.writeRune('}') + } + + w.writeRune(')') + + case *ir.SubrangeMeta: + w.writeString("!DISubrange(") + + w.writeString("lowerBound: ") + w.writeUint(uint64(meta.LowerBound), 10) + + w.writeString(", count: ") + w.writeUint(uint64(meta.Count), 10) + + w.writeRune(')') + + case *ir.EnumeratorMeta: + w.writeString("!DIEnumerator(") + + w.writeString("name: ") + w.writeQuotedString(meta.Name) + + w.writeString(", value: ") + if meta.Value.Negative { + w.writeRune('-') + } + w.writeUint(meta.Value.Value, 10) + + w.writeRune(')') + + case *ir.NamespaceMeta: + w.writeString("!DINamespace(") + + w.writeString("name: ") + w.writeQuotedString(meta.Name) + + w.writeString(", scope: ") + w.writeMetaRef(meta.Scope) + + w.writeString(", file: ") + w.writeMetaRef(meta.File) + + w.writeString(", line: ") + w.writeUint(uint64(meta.Line), 10) + + w.writeRune(')') + + case *ir.GlobalVarMeta: + w.writeString("distinct !DIGlobalVariable(") + + w.writeString("name: ") + w.writeQuotedString(meta.Name) + + w.writeString(", linkageName: ") + w.writeQuotedString(meta.LinkageName) + + w.writeString(", scope: ") + w.writeMetaRef(meta.Scope) + + w.writeString(", file: ") + w.writeMetaRef(meta.File) + + w.writeString(", line: ") + w.writeUint(uint64(meta.Line), 10) + + w.writeString(", type: ") + w.writeMetaRef(meta.Type) + + w.writeString(", isLocal: ") + w.writeBool(meta.Local) + + w.writeString(", isDefinition: ") + w.writeBool(meta.Definition) + + w.writeRune(')') + + case *ir.GlobalVarExpr: + w.writeString("!DIGlobalVariableExpression(") + + w.writeString("var: ") + w.writeMetaRef(meta.Var) + + w.writeString(", expr: ") + w.writeString("!DIExpression()") + + w.writeRune(')') + + case *ir.SubprogamMeta: + w.writeString("distinct !DISubprogram(") + + w.writeString("name: ") + w.writeQuotedString(meta.Name) + + w.writeString(", linkageName: ") + w.writeQuotedString(meta.LinkageName) + + w.writeString(", scope: ") + w.writeMetaRef(meta.Scope) + + w.writeString(", file: ") + w.writeMetaRef(meta.File) + + w.writeString(", line: ") + w.writeUint(uint64(meta.Line), 10) + + w.writeString(", type: ") + w.writeMetaRef(meta.Type) + + w.writeString(", unit: ") + w.writeMetaRef(meta.Unit) + + w.writeString(", spFlags: ") + w.writeString("DISPFlagDefinition") + + w.writeRune(')') + + case *ir.LexicalBlockMeta: + w.writeString("!DILexicalBlock(") + + w.writeString("scope: ") + w.writeMetaRef(meta.Scope) + + w.writeString(", file: ") + w.writeMetaRef(meta.File) + + w.writeString(", line: ") + w.writeUint(uint64(meta.Line), 10) + + w.writeRune(')') + + case *ir.LocationMeta: + w.writeString("!DILocation(") + + w.writeString("scope: ") + w.writeMetaRef(meta.Scope) + + w.writeString(", line: ") + w.writeUint(uint64(meta.Line), 10) + + w.writeString(", column: ") + w.writeUint(uint64(meta.Column), 10) + + w.writeRune(')') + + case *ir.LocalVarMeta: + w.writeString("distinct !DILocalVariable(") + + w.writeString("name: ") + w.writeQuotedString(meta.Name) + + w.writeString(", type: ") + w.writeMetaRef(meta.Type) + + if meta.Arg != 0 { + w.writeString(", arg: ") + w.writeUint(uint64(meta.Arg), 10) + } + + w.writeString(", scope: ") + w.writeMetaRef(meta.Scope) + + w.writeString(", file: ") + w.writeMetaRef(meta.File) + + w.writeString(", line: ") + w.writeUint(uint64(meta.Line), 10) + + w.writeRune(')') + + case *ir.ExpressionMeta: + w.writeString("!DIExpression()") + + default: + panic("ir.writeMeta() - Not implemented") } } -func refMetadataValue(index int) MetadataValue { - return MetadataValue{ - Kind: RefMetadataValueKind, - Number: index, +func (w *textWriter) writeMetaRef(id ir.MetaID) { + if id == 0 { + w.writeString("null") + return } + + w.writeRune('!') + w.writeUint(uint64(w.m.Metadata[id-1].Index()), 10) } diff --git a/core/llvm/module.go b/core/llvm/module.go deleted file mode 100644 index 54d044f..0000000 --- a/core/llvm/module.go +++ /dev/null @@ -1,525 +0,0 @@ -package llvm - -import ( - "fireball/core/cst" -) - -type Module struct { - source string - - types []Type - - constants []*constant - constantType Type - - variables []*variable - - declares []*functionType - defines []*Function - - namedMetadata map[string]Metadata - metadata []Metadata - - typeMetadata map[Type]int - scopes []int -} - -func NewModule() *Module { - return &Module{ - namedMetadata: make(map[string]Metadata), - typeMetadata: make(map[Type]int), - } -} - -func (m *Module) Source(path string) { - m.source = path - producer := "fireball version 0.1.0" - - file := m.addMetadata(fileMetadata(path)) - m.scopes = append(m.scopes, file) - - m.namedMetadata["llvm.dbg.cu"] = Metadata{Fields: []MetadataField{ - {Value: refMetadataValue(m.addMetadata(compileUnitMetadata(file, producer)))}, - }} - - m.namedMetadata["llvm.module.flags"] = Metadata{Fields: []MetadataField{ - {Value: refMetadataValue(m.addFlagMetadata(7, "Dwarf Version", 4))}, - {Value: refMetadataValue(m.addFlagMetadata(2, "Debug Info Version", 3))}, - {Value: refMetadataValue(m.addFlagMetadata(1, "wchar_size", 4))}, - {Value: refMetadataValue(m.addFlagMetadata(8, "PIC Level", 2))}, - {Value: refMetadataValue(m.addFlagMetadata(7, "PIE Level", 2))}, - {Value: refMetadataValue(m.addFlagMetadata(7, "uwtable", 2))}, - {Value: refMetadataValue(m.addFlagMetadata(7, "frame-pointer", 2))}, - }} - - m.namedMetadata["llvm.ident"] = Metadata{Fields: []MetadataField{ - {Value: refMetadataValue(m.addMetadata(Metadata{Fields: []MetadataField{{Value: stringMetadataValue(producer)}}}))}, - }} -} - -// Functions - -type declare struct { - type_ Type - name string -} - -func (d *declare) Kind() ValueKind { - return GlobalValue -} - -func (d *declare) Type() Type { - return d.type_ -} - -func (d *declare) Name() string { - return d.name -} - -func (m *Module) Declare(type_ Type) Value { - m.declares = append(m.declares, type_.(*functionType)) - - return &declare{ - type_: type_, - name: type_.(*functionType).name, - } -} - -func (m *Module) Define(type_ Type, debugName string) *Function { - t := type_.(*functionType) - - metadata := m.addMetadata(Metadata{ - Distinct: true, - Type: "DISubprogram", - Fields: []MetadataField{ - { - Name: "name", - Value: stringMetadataValue(debugName), - }, - { - Name: "scope", - Value: refMetadataValue(m.getScope()), - }, - { - Name: "file", - Value: refMetadataValue(m.getFile()), - }, - { - Name: "type", - Value: refMetadataValue(m.typeMetadata[type_]), - }, - { - Name: "spFlags", - Value: enumMetadataValue("DISPFlagDefinition"), - }, - { - Name: "unit", - Value: refMetadataValue(m.getCompileUnit()), - }, - }, - }) - - f := &Function{ - module: m, - - type_: t, - - parameters: make([]NameableValue, len(t.parameters)), - blocks: make([]*Block, 0, 4), - - metadata: metadata, - } - - for i, parameter := range t.parameters { - f.parameters[i] = &instruction{ - type_: parameter, - } - } - - m.defines = append(m.defines, f) - return f -} - -// Types - -func (m *Module) Void() Type { - return &voidType{} -} - -func (m *Module) Primitive(name string, bitSize uint32, encoding Encoding) Type { - t := &primitiveType{ - name: name, - bitSize: bitSize, - encoding: encoding, - } - - m.typeMetadata[t] = m.addMetadata(Metadata{ - Type: "DIBasicType", - Fields: []MetadataField{ - { - Name: "name", - Value: stringMetadataValue(name), - }, - { - Name: "size", - Value: numberMetadataValue(int(bitSize)), - }, - { - Name: "encoding", - Value: enumMetadataValue(string(encoding)), - }, - }, - }) - - m.types = append(m.types, t) - return t -} - -func (m *Module) Array(name string, count uint32, base Type) Type { - t := &arrayType{ - name: name, - count: count, - base: base, - } - - m.typeMetadata[t] = m.addMetadata(Metadata{ - Type: "DICompositeType", - Fields: []MetadataField{ - { - Name: "tag", - Value: enumMetadataValue("DW_TAG_array_type"), - }, - { - Name: "baseType", - Value: refMetadataValue(m.typeMetadata[base]), - }, - { - Name: "size", - Value: numberMetadataValue(int(t.Size())), - }, - { - Name: "elements", - Value: refMetadataValue(m.addMetadata(Metadata{Fields: []MetadataField{ - {Value: refMetadataValue(m.addMetadata(Metadata{ - Type: "DISubrange", - Fields: []MetadataField{ - { - Name: "count", - Value: numberMetadataValue(int(count)), - }, - }, - }))}, - }})), - }, - }, - }) - - m.types = append(m.types, t) - return t -} - -func (m *Module) Pointer(name string, pointee Type) Type { - t := &pointerType{ - name: name, - pointee: pointee, - } - - m.typeMetadata[t] = m.addMetadata(Metadata{ - Type: "DIDerivedType", - Fields: []MetadataField{ - { - Name: "tag", - Value: enumMetadataValue("DW_TAG_pointer_type"), - }, - { - Name: "baseType", - Value: refMetadataValue(m.typeMetadata[pointee]), - }, - { - Name: "size", - Value: numberMetadataValue(int(t.Size())), - }, - }, - }) - - m.types = append(m.types, t) - return t -} - -func (m *Module) Function(name string, parameters []Type, variadic bool, returns Type) Type { - t := &functionType{ - name: name, - parameters: parameters, - variadic: variadic, - returns: returns, - } - - types := make([]MetadataField, len(parameters)+1) - - if _, ok := returns.(*voidType); ok { - types[0] = MetadataField{Value: enumMetadataValue("null")} - } else { - types[0] = MetadataField{Value: refMetadataValue(m.typeMetadata[returns])} - } - - for i, parameter := range parameters { - types[i+1] = MetadataField{Value: refMetadataValue(m.typeMetadata[parameter])} - } - - m.typeMetadata[t] = m.addMetadata(Metadata{ - Type: "DISubroutineType", - Fields: []MetadataField{ - { - Name: "types", - Value: refMetadataValue(m.addMetadata(Metadata{Fields: types})), - }, - }, - }) - - m.types = append(m.types, t) - return t -} - -func (m *Module) Struct(name string, size uint32, fields []Field) Type { - t := &structType{ - name: name, - size: size, - fields: fields, - } - - elements := make([]MetadataField, len(fields)) - - for i, field := range fields { - elements[i] = MetadataField{Value: refMetadataValue(m.addMetadata(Metadata{ - Type: "DIDerivedType", - Fields: []MetadataField{ - { - Name: "tag", - Value: enumMetadataValue("DW_TAG_member"), - }, - { - Name: "baseType", - Value: refMetadataValue(m.typeMetadata[field.Type]), - }, - { - Name: "name", - Value: stringMetadataValue(field.Name), - }, - { - Name: "scope", - Value: refMetadataValue(m.getScope()), - }, - { - Name: "file", - Value: refMetadataValue(m.getFile()), - }, - { - Name: "offset", - Value: numberMetadataValue(int(field.Offset)), - }, - }, - }))} - } - - m.typeMetadata[t] = m.addMetadata(Metadata{ - Distinct: true, - Type: "DICompositeType", - Fields: []MetadataField{ - { - Name: "tag", - Value: enumMetadataValue("DW_TAG_structure_type"), - }, - { - Name: "name", - Value: stringMetadataValue(name), - }, - { - Name: "file", - Value: refMetadataValue(m.getFile()), - }, - { - Name: "size", - Value: numberMetadataValue(int(size)), - }, - { - Name: "flags", - Value: enumMetadataValue("DIFlagTypePassByValue"), - }, - { - Name: "elements", - Value: refMetadataValue(m.addMetadata(Metadata{Fields: elements})), - }, - }, - }) - - m.types = append(m.types, t) - return t -} - -func (m *Module) Alias(name string, underlying Type) Type { - t := &aliasType{ - name: name, - underlying: underlying, - } - - m.typeMetadata[t] = m.addMetadata(Metadata{ - Type: "DIDerivedType", - Fields: []MetadataField{ - { - Name: "tag", - Value: enumMetadataValue("DW_TAG_typedef"), - }, - { - Name: "baseType", - Value: refMetadataValue(m.typeMetadata[underlying]), - }, - { - Name: "size", - Value: numberMetadataValue(int(underlying.Size())), - }, - }, - }) - - m.types = append(m.types, t) - return t -} - -// Constants - -type constant struct { - type_ Type - data string -} - -func (c *constant) Kind() ValueKind { - return GlobalValue -} - -func (c *constant) Type() Type { - return c.type_ -} - -func (c *constant) Name() string { - return "" -} - -func (m *Module) Constant(data string) Value { - // Get constant from already created constants - for _, c := range m.constants { - if c.data == data { - return c - } - } - - // Create constant type - if m.constantType == nil { - m.constantType = m.Pointer("*u8", m.Primitive("u8", 8, UnsignedEncoding)) - } - - // Create constant - c := &constant{ - type_: m.constantType, - data: data, - } - - m.constants = append(m.constants, c) - return c -} - -// Variables - -type variable struct { - type_ Type - ptr Type - - external bool - name string -} - -func (v *variable) Kind() ValueKind { - return GlobalValue -} - -func (v *variable) Type() Type { - return v.ptr -} - -func (v *variable) Name() string { - return v.name -} - -func (v *variable) SetName(name string) { - v.name = name -} - -func (m *Module) Variable(external bool, type_ Type, ptr Type) NameableValue { - v := &variable{ - type_: type_, - ptr: ptr, - external: external, - } - - m.variables = append(m.variables, v) - return v -} - -// Metadata - -func (m *Module) PushScope(node *cst.Node) { - fields := []MetadataField{ - { - Name: "scope", - Value: refMetadataValue(m.getScope()), - }, - { - Name: "file", - Value: refMetadataValue(m.getFile()), - }, - } - - if node != nil { - fields = append(fields, MetadataField{ - Name: "line", - Value: numberMetadataValue(int(node.Range.Start.Line)), - }) - fields = append(fields, MetadataField{ - Name: "column", - Value: numberMetadataValue(int(node.Range.Start.Column)), - }) - } - - m.scopes = append(m.scopes, m.addMetadata(Metadata{ - Distinct: true, - Type: "DILexicalBlock", - Fields: fields, - })) -} - -func (m *Module) PopScope() { - m.scopes = m.scopes[:len(m.scopes)-1] -} - -func (m *Module) getScope() int { - return m.scopes[len(m.scopes)-1] -} - -func (m *Module) getFile() int { - return m.scopes[0] -} - -func (m *Module) getCompileUnit() int { - return 1 -} - -func (m *Module) addFlagMetadata(num1 int, str string, num2 int) int { - return m.addMetadata(Metadata{Fields: []MetadataField{ - {Value: numberMetadataValue(num1)}, - {Value: stringMetadataValue(str)}, - {Value: numberMetadataValue(num2)}, - }}) -} - -func (m *Module) addMetadata(metadata Metadata) int { - m.metadata = append(m.metadata, metadata) - return len(m.metadata) - 1 -} diff --git a/core/llvm/text.go b/core/llvm/text.go deleted file mode 100644 index 47e58f2..0000000 --- a/core/llvm/text.go +++ /dev/null @@ -1,661 +0,0 @@ -package llvm - -import ( - "fireball/core/utils" - "fmt" - "io" - "regexp" -) - -type textWriter struct { - w io.Writer - - intrinsics utils.Set[string] - - typeNames map[Type]string - - localUnnamedCount int - globalUnnamedCount int - - localValueNames map[Value]string - localValueNamesCount map[string]int - - globalValueNames map[Value]string - globalValueNamesCount map[string]int - - metadataCount int -} - -func WriteText(module *Module, writer io.Writer) { - w := &textWriter{ - w: writer, - - intrinsics: utils.NewSet[string](), - - typeNames: make(map[Type]string), - - globalValueNames: make(map[Value]string), - globalValueNamesCount: make(map[string]int), - } - - // Source - w.fmt("source_filename = \"%s\"\n", module.source) - w.line() - - // Types - types := 0 - - for _, t := range module.types { - if v, ok := t.(*structType); ok { - name := fmt.Sprintf("%%struct.%s", v.name) - w.typeNames[t] = name - - w.fmt("%s = type { ", name) - - for i, field := range v.fields { - if i > 0 { - w.raw(", ") - } - - w.raw(w.type_(field.Type)) - } - - w.raw(" }\n") - types++ - } - } - - if types > 0 { - w.line() - } - - // Constants - for _, c := range module.constants { - data, length := escape(c.data) - w.fmt("%s = private unnamed_addr constant [%d x i8] c\"%s\"\n", w.value(c), length, data) - } - - if len(module.constants) > 0 { - w.line() - } - - // Variables - - for _, v := range module.variables { - if v.external { - w.fmt("%s = external global %s\n", w.value(v), w.type_(v.type_)) - } else { - w.fmt("%s = global %s zeroinitializer\n", w.value(v), w.type_(v.type_)) - } - } - - if len(module.variables) > 0 { - w.line() - } - - // Defines - for _, define := range module.defines { - w.beginFunction() - w.fmt("define %s @%s(", w.type_(define.type_.returns), surroundName(define.type_.name)) - - for i, parameter := range define.parameters { - if i > 0 { - w.raw(", ") - } - - w.fmt("%s %s", w.type_(parameter.Type()), w.value(parameter)) - } - - if define.type_.variadic { - if len(define.parameters) > 0 { - w.raw(", ...") - } else { - w.raw("...") - } - } - - if define.alwaysInline { - w.fmt(") alwaysinline !dbg !%d {\n", define.metadata) - } else { - w.fmt(") !dbg !%d {\n", define.metadata) - } - - w.body(define.blocks) - w.raw("}\n\n") - } - - // Declares - for _, declare := range module.declares { - w.beginFunction() - w.fmt("declare %s @%s(", w.type_(declare.returns), surroundName(declare.name)) - - for i, parameter := range declare.parameters { - if i > 0 { - w.raw(", ") - } - - w.raw(w.type_(parameter)) - } - - if declare.variadic { - if len(declare.parameters) > 0 { - w.raw(", ...") - } else { - w.raw("...") - } - } - - w.raw(")\n") - } - - for intrinsic := range w.intrinsics.Data { - w.fmt("declare %s\n", intrinsic) - } - - if len(module.declares) > 0 { - w.line() - } - - // Debug - w.debug(module) -} - -// Functions - -func (w *textWriter) beginFunction() { - w.localUnnamedCount = 0 - - w.localValueNames = make(map[Value]string) - w.localValueNamesCount = make(map[string]int) -} - -func (w *textWriter) body(blocks []*Block) { - // Gather names of blocks and instructions - for _, block := range blocks { - w.value(block) - - for _, inst := range block.instructions { - if _, ok := inst.Type().(*voidType); inst.Type() != nil && !ok { - w.value(inst) - } - } - } - - // Emit blocks and instructions - for i, block := range blocks { - if i > 0 { - w.line() - } - - w.fmt("%s:\n", w.value(block)[1:]) - - for _, inst := range block.instructions { - w.raw(" ") - - if w.instruction(inst) { - break - } - } - } -} - -func (w *textWriter) instruction(i Value) bool { - if _, ok := i.Type().(*voidType); i.Type() != nil && !ok { - w.fmt("%s = ", w.value(i)) - } - - location := -1 - terminal := false - - switch inst := i.(type) { - case *variableMetadata: - w.intrinsics.Add("void @llvm.dbg.declare(metadata, metadata, metadata)") - w.fmt("call void @llvm.dbg.declare(metadata ptr %s, metadata !%d, metadata !DIExpression())", w.value(inst.pointer), inst.metadata) - - location = inst.location - - case *lifetimeMetadata: - if inst.start { - w.intrinsics.Add("void @llvm.lifetime.start(i64, ptr)") - w.fmt("call void @llvm.lifetime.start(i64 %d, ptr %s)", inst.pointer.Type().Size(), w.value(inst.pointer)) - } else { - w.intrinsics.Add("void @llvm.lifetime.end(i64, ptr)") - w.fmt("call void @llvm.lifetime.end(i64 %d, ptr %s)", inst.pointer.Type().Size(), w.value(inst.pointer)) - } - - location = inst.location - - case *fNeg: - w.fmt("fneg %s %s", w.type_(inst.Type()), w.value(inst.value)) - location = inst.location - - case *binary: - a := "" - - switch inst.op { - case Add: - a = ternary(isFloating(inst.left.Type()), "fadd", "add") - case Sub: - a = ternary(isFloating(inst.left.Type()), "fsub", "sub") - case Mul: - a = ternary(isFloating(inst.left.Type()), "fmul", "mul") - case Div: - a = ternary(isFloating(inst.left.Type()), "fdiv", ternary(isSigned(inst.Type()), "sdiv", "udiv")) - case Rem: - a = ternary(isFloating(inst.left.Type()), "frem", ternary(isSigned(inst.Type()), "srem", "urem")) - - case Eq: - a = ternary(isFloating(inst.left.Type()), "fcmp oeq", "icmp eq") - case Ne: - a = ternary(isFloating(inst.left.Type()), "fcmp one", "icmp ne") - case Lt: - a = ternary(isFloating(inst.left.Type()), "fcmp olt", ternary(isSigned(inst.Type()), "icmp slt", "icmp ult")) - case Le: - a = ternary(isFloating(inst.left.Type()), "fcmp ole", ternary(isSigned(inst.Type()), "icmp sle", "icmp ule")) - case Gt: - a = ternary(isFloating(inst.left.Type()), "fcmp ogt", ternary(isSigned(inst.Type()), "icmp sgt", "icmp ugt")) - case Ge: - a = ternary(isFloating(inst.left.Type()), "fcmp oge", ternary(isSigned(inst.Type()), "icmp sge", "icmp uge")) - - case Or: - a = "or" - case Xor: - a = "xor" - case And: - a = "and" - case Shl: - a = "shl" - case Shr: - a = ternary(isSigned(inst.left.Type()), "ashr", "lshr") - } - - w.fmt("%s %s %s, %s", a, w.type_(inst.left.Type()), w.value(inst.left), w.value(inst.right)) - location = inst.location - - case *cast: - a := "" - - switch inst.kind { - case Trunc: - a = "trunc" - case ZExt: - a = "zext" - case SExt: - a = "sext" - case FpTrunc: - a = "fptrunc" - case FpExt: - a = "fpext" - case FpToUi: - a = "fptoui" - case FpToSi: - a = "fptosi" - case UiToFp: - a = "uitofp" - case SiToFp: - a = "sitofp" - case PtrToInt: - a = "ptrtoint" - case IntToPtr: - a = "inttoptr" - case Bitcast: - a = "bitcast" - } - - w.fmt("%s %s %s to %s", a, w.type_(inst.value.Type()), w.value(inst.value), w.type_(inst.Type())) - location = inst.location - - case *extractValue: - w.fmt("extractvalue %s %s, %d", w.type_(inst.value.Type()), w.value(inst.value), inst.index) - location = inst.location - - case *insertValue: - w.fmt("insertvalue %s %s, %s %s, %d", w.type_(inst.value.Type()), w.value(inst.value), w.type_(inst.element.Type()), w.value(inst.element), inst.index) - location = inst.location - - case *alloca: - if inst.align != 0 { - w.fmt("alloca %s, align %d", w.type_(inst.type_), inst.align) - } else { - w.fmt("alloca %s", w.type_(inst.type_)) - } - - location = inst.location - - case *load: - if inst.align != 0 { - w.fmt("load %s, ptr %s, align %d", w.type_(inst.Type()), w.value(inst.pointer), inst.align) - } else { - w.fmt("load %s, ptr %s", w.type_(inst.Type()), w.value(inst.pointer)) - } - - location = inst.location - - case *store: - if inst.align != 0 { - w.fmt("store %s %s, ptr %s, align %d", w.type_(inst.value.Type()), w.value(inst.value), w.value(inst.pointer), inst.align) - } else { - w.fmt("store %s %s, ptr %s", w.type_(inst.value.Type()), w.value(inst.value), w.value(inst.pointer)) - } - - location = inst.location - - case *getElementPtr: - w.fmt("getelementptr inbounds %s, %s %s", w.type_(inst.type_), w.type_(inst.pointer.Type()), w.value(inst.pointer)) - location = inst.location - - for _, index := range inst.indices { - w.fmt(", %s %s", w.type_(index.Type()), w.value(index)) - } - - case *br: - if inst.condition == nil { - w.fmt("br label %s", w.value(inst.true)) - } else { - w.fmt("br i1 %s, label %s, label %s", w.value(inst.condition), w.value(inst.true), w.value(inst.false)) - } - - location = inst.location - terminal = true - - case *phi: - w.fmt("phi %s [ %s, %s ], [ %s, %s ]", w.type_(inst.type_), w.value(inst.firstValue), w.value(inst.firstBlock), w.value(inst.secondValue), w.value(inst.secondBlock)) - location = inst.location - - case *call: - w.fmt("call %s %s(", w.type_(inst.Type()), w.value(inst.value)) - - for i, argument := range inst.arguments { - if i > 0 { - w.raw(", ") - } - - w.fmt("%s %s", w.type_(argument.Type()), w.value(argument)) - } - - w.raw(")") - location = inst.location - - case *ret: - if inst.value == nil { - w.raw("ret void") - } else { - w.fmt("ret %s %s", w.type_(inst.value.Type()), w.value(inst.value)) - } - - location = inst.location - terminal = true - - default: - panic("textWriter.instruction() - Invalid instruction") - } - - if location != -1 { - w.fmt(", !dbg !%d", location) - } - - w.raw("\n") - return terminal -} - -// Types - -func (w *textWriter) type_(type_ Type) string { - if name, ok := w.typeNames[type_]; ok { - return name - } - - name := "" - - if _, ok := type_.(*voidType); ok { - // Void - name = "void" - } else if v, ok := type_.(*primitiveType); ok { - // Primitive - switch v.encoding { - case BooleanEncoding: - name = "i1" - - case FloatEncoding: - if v.bitSize == 32 { - name = "float" - } else if v.bitSize == 64 { - name = "double" - } - - case SignedEncoding, UnsignedEncoding: - name = fmt.Sprintf("i%d", v.bitSize) - } - } else if v, ok := type_.(*arrayType); ok { - // Array - name = fmt.Sprintf("[%d x %s]", v.count, w.type_(v.base)) - } else if _, ok := type_.(*pointerType); ok { - // Pointer - name = "ptr" - } else if _, ok := type_.(*functionType); ok { - // Function - name = "ptr" - } else if v, ok := type_.(*aliasType); ok { - // Alias - name = w.type_(v.underlying) - } - - if name != "" { - w.typeNames[type_] = name - return name - } else { - panic("textWriter.type_() - Invalid type") - } -} - -// Constants - -func escape(original string) (string, int) { - data := make([]uint8, 0, len(original)+3) - length := 1 - - for i := 0; i < len(original); i++ { - char := original[i] - - if char == '\\' { - i++ - - switch original[i] { - case '0': - data = append(data, '\\') - data = append(data, '0') - data = append(data, '0') - - case 'n': - data = append(data, '\\') - data = append(data, '0') - data = append(data, 'A') - - case 'r': - data = append(data, '\\') - data = append(data, '0') - data = append(data, 'D') - - case 't': - data = append(data, '\\') - data = append(data, '0') - data = append(data, '9') - } - - length++ - } else { - data = append(data, char) - length++ - } - } - - data = append(data, '\\') - data = append(data, '0') - data = append(data, '0') - - return string(data), length -} - -// Names - -func (w *textWriter) value(value Value) string { - switch value.Kind() { - case GlobalValue: - if name, ok := w.globalValueNames[value]; ok { - return name - } - - name := value.Name() - - if name == "" { - name = fmt.Sprintf("@%d", w.globalUnnamedCount) - w.globalUnnamedCount++ - } else { - name = "@" + surroundName(name) - - if count, ok := w.globalValueNamesCount[name]; ok { - w.globalValueNamesCount[name]++ - name += fmt.Sprintf(".%d", count+1) - } else { - w.globalValueNamesCount[name] = 0 - } - } - - w.globalValueNames[value] = name - return name - - case LocalValue: - if name, ok := w.localValueNames[value]; ok { - return name - } - - name := value.Name() - - if name == "" { - name = fmt.Sprintf("%%%d", w.localUnnamedCount) - w.localUnnamedCount++ - } else { - name = "%" + surroundName(name) - - if count, ok := w.localValueNamesCount[name]; ok { - w.localValueNamesCount[name]++ - name += fmt.Sprintf(".%d", count+1) - } else { - w.localValueNamesCount[name] = 0 - } - } - - w.localValueNames[value] = name - return name - } - - return value.Name() -} - -var namePattern = regexp.MustCompile("^[-a-zA-Z$._][-a-zA-Z$._0-9]*$") - -func surroundName(name string) string { - if namePattern.MatchString(name) { - return name - } - - return "\"" + name + "\"" -} - -// Debug - -func (w *textWriter) debug(module *Module) { - // Named metadata - for name, metadata := range module.namedMetadata { - w.fmt("!%s = ", name) - w.metadata(metadata) - } - - w.line() - - // Unnamed metadata - for i, metadata := range module.metadata { - w.fmt("!%d = ", i) - w.metadata(metadata) - } -} - -func (w *textWriter) metadata(metadata Metadata) { - if metadata.Distinct { - w.raw("distinct ") - } - - if metadata.Type != "" { - w.fmt("!%s(", metadata.Type) - } else { - w.raw("!{") - } - - for i, field := range metadata.Fields { - if i > 0 { - w.raw(", ") - } - - if field.Name != "" { - w.fmt("%s: ", field.Name) - - switch field.Value.Kind { - case StringMetadataValueKind: - w.fmt("\"%s\"", field.Value.String) - - case EnumMetadataValueKind: - w.raw(field.Value.String) - - case NumberMetadataValueKind: - w.fmt("%d", field.Value.Number) - - case RefMetadataValueKind: - w.fmt("!%d", field.Value.Number) - } - } else { - switch field.Value.Kind { - case StringMetadataValueKind: - w.fmt("!\"%s\"", field.Value.String) - - case EnumMetadataValueKind: - w.raw(field.Value.String) - - case NumberMetadataValueKind: - w.fmt("i32 %d", field.Value.Number) - - case RefMetadataValueKind: - w.fmt("!%d", field.Value.Number) - } - } - } - - if metadata.Type != "" { - w.raw(")\n") - } else { - w.raw("}\n") - } -} - -// Utils - -func ternary[T any](condition bool, true T, false T) T { - if condition { - return true - } - - return false -} - -// Write - -func (w *textWriter) fmt(format string, args ...any) { - _, _ = fmt.Fprintf(w.w, format, args...) -} - -func (w *textWriter) raw(str string) { - _, _ = w.w.Write([]byte(str)) -} - -func (w *textWriter) line() { - _, _ = w.w.Write([]byte{'\n'}) -} diff --git a/core/llvm/types.go b/core/llvm/types.go index d5b8529..73ea927 100644 --- a/core/llvm/types.go +++ b/core/llvm/types.go @@ -1,128 +1,91 @@ package llvm -type Encoding string - -const ( - BooleanEncoding Encoding = "DW_ATE_boolean" - FloatEncoding Encoding = "DW_ATE_float" - SignedEncoding Encoding = "DW_ATE_signed" - UnsignedEncoding Encoding = "DW_ATE_unsigned" +import ( + "fireball/core/ir" ) -type Type interface { - isType() - - Size() uint32 -} - -type voidType struct { -} - -func (v *voidType) isType() {} - -func (v *voidType) Size() uint32 { - return 0 -} - -type primitiveType struct { - name string - bitSize uint32 - encoding Encoding -} - -func (v *primitiveType) isType() {} - -func (v *primitiveType) Size() uint32 { - return v.bitSize -} - -type arrayType struct { - name string - count uint32 - base Type -} - -func (v *arrayType) isType() {} - -func (v *arrayType) Size() uint32 { - return v.count * v.base.Size() -} +func (w *textWriter) writeStructs() { + for _, s := range w.m.Structs { + w.writeString("%struct.") + w.writeString(s.Name) + w.writeString(" = ") + w.writeStruct(s) + w.writeRune('\n') + } -type pointerType struct { - name string - pointee Type + if len(w.m.Structs) > 0 { + w.writeRune('\n') + } } -func (v *pointerType) isType() {} - -func (v *pointerType) Size() uint32 { - return 64 -} +func (w *textWriter) writeType(t ir.Type) { + if t == nil { + w.writeString("void") + return + } -type functionType struct { - name string - parameters []Type - variadic bool - returns Type -} + switch t := t.(type) { + case *ir.VoidType: + w.writeString("void") -func (v *functionType) isType() {} + case *ir.IntType: + w.writeRune('i') + w.writeInt(int64(t.BitSize)) -func (v *functionType) Size() uint32 { - return 64 -} + case *ir.FloatType: + switch t.BitSize { + case 32: + w.writeString("float") + case 64: + w.writeString("double") -type Field struct { - Name string - Type Type - Offset uint32 -} + default: + panic("ir.writeType() - llvm.FloatType - Not implemented") + } -type structType struct { - name string - size uint32 - fields []Field -} + case *ir.PointerType: + w.writeString("ptr") -func (v *structType) isType() {} + case *ir.ArrayType: + w.writeRune('[') + w.writeInt(int64(t.Count)) + w.writeString(" x ") + w.writeType(t.Base) + w.writeRune(']') -func (v *structType) Size() uint32 { - return v.size -} + case *ir.StructType: + for _, s := range w.m.Structs { + if s == t { + w.writeString("%struct.") + w.writeString(s.Name) -type aliasType struct { - name string - underlying Type -} + return + } + } -func (v *aliasType) isType() {} + w.writeStruct(t) -func (v *aliasType) Size() uint32 { - return v.underlying.Size() -} + case *ir.FuncType: + w.writeString("ptr") -// Utils + case *ir.MetaType: + w.writeString("metadata") -func isSigned(type_ Type) bool { - if v, ok := type_.(*primitiveType); ok { - return v.encoding == SignedEncoding || v.encoding == BooleanEncoding + default: + panic("ir.writeType() - Not implemented") } - - return false } -func isUnsigned(type_ Type) bool { - if v, ok := type_.(*primitiveType); ok { - return v.encoding == UnsignedEncoding - } +func (w *textWriter) writeStruct(s *ir.StructType) { + w.writeString("type { ") - return false -} + for i, field := range s.Fields { + if i > 0 { + w.writeString(", ") + } -func isFloating(type_ Type) bool { - if v, ok := type_.(*primitiveType); ok { - return v.encoding == FloatEncoding + w.writeType(field) } - return false + w.writeString(" }") } diff --git a/core/llvm/value.go b/core/llvm/value.go index d335469..4d8058a 100644 --- a/core/llvm/value.go +++ b/core/llvm/value.go @@ -1,45 +1,171 @@ package llvm import ( - "fireball/core/cst" + "fireball/core/ir" + "regexp" ) -type ValueKind uint8 +type Name struct { + char rune -const ( - GlobalValue ValueKind = iota - LocalValue - LiteralValue -) + unnamed int64 + + named string + namedSuffix uint64 +} + +func (w *textWriter) writeValue(v ir.Value) { + switch v := v.(type) { + case *ir.Block: + w.writeString("label") + default: + w.writeType(v.Type()) + } + + w.writeRune(' ') + + w.writeValueValue(v) +} -type Value interface { - Kind() ValueKind - Type() Type - Name() string +func (w *textWriter) writeValueValue(v ir.Value) { + switch v := v.(type) { + case ir.Const: + w.writeConst(v) + case ir.MetaID: + w.writeMetaRef(v) + default: + w.writeName(v) + } } -type NameableValue interface { - Value +func (w *textWriter) writeName(v ir.Value) { + switch v := v.(type) { + case *ir.GlobalVar, *ir.Func: + w.writeName_(v, true) + case *ir.Param, *ir.Block, ir.Inst: + w.writeName_(v, false) + } +} + +func (w *textWriter) writeName_(v ir.Value, global bool) { + w.cacheName(v, global) - SetName(name string) + if global { + w.writeNameImpl(w.globalNames[v]) + } else { + w.writeNameImpl(w.localNames[v]) + } } -type Instruction interface { - SetLocation(node *cst.Node) +func (w *textWriter) cacheName(v ir.Value, global bool) { + // Global + if global { + // Cache + if _, ok := w.globalNames[v]; ok { + return + } + + // Unnamed + if v.Name() == "" { + name := Name{ + char: '@', + unnamed: w.globalUnnamedCount, + } + + w.globalUnnamedCount++ + w.globalNames[v] = name + + return + } + + // Named + suffix := uint64(0) + + if count, ok := w.globalNameCounts[v.Name()]; ok { + suffix = count + w.globalNameCounts[v.Name()]++ + } else { + w.globalNameCounts[v.Name()] = 1 + } + + name := Name{ + char: '@', + unnamed: -1, + named: surroundName(v.Name()), + namedSuffix: suffix, + } + + w.globalNames[v] = name + + return + } + + // Local + if _, ok := w.localNames[v]; ok { + return + } + + // Unnamed + if v.Name() == "" { + name := Name{ + char: '%', + unnamed: w.localUnnamedCount, + } + + w.localUnnamedCount++ + w.localNames[v] = name + + return + } + + // Named + suffix := uint64(0) + + if count, ok := w.localNameCounts[v.Name()]; ok { + suffix = count + w.localNameCounts[v.Name()]++ + } else { + w.localNameCounts[v.Name()] = 1 + } + + name := Name{ + char: '%', + unnamed: -1, + named: surroundName(v.Name()), + namedSuffix: suffix, + } + + w.localNames[v] = name } -type AlignedInstruction interface { - Instruction +func (w *textWriter) writeNameImpl(name Name) { + if !w.skipNameChar { + w.writeRune(name.char) + } - SetAlign(align uint32) + if name.unnamed >= 0 { + w.writeInt(name.unnamed) + } else { + w.writeString(name.named) + + if name.namedSuffix > 0 { + w.writeUint(name.namedSuffix, 10) + } + } } -type InstructionValue interface { - NameableValue - Instruction +func (w *textWriter) resetLocalNames() { + w.localUnnamedCount = 0 + clear(w.localNameCounts) + clear(w.localNames) } -type AlignedInstructionValue interface { - InstructionValue - AlignedInstruction +var namePattern = regexp.MustCompile("^[-a-zA-Z$._][-a-zA-Z$._0-9]*$") + +func surroundName(name string) string { + if namePattern.MatchString(name) { + return name + } + + return "\"" + name + "\"" } diff --git a/core/llvm/writer.go b/core/llvm/writer.go new file mode 100644 index 0000000..a2d375c --- /dev/null +++ b/core/llvm/writer.go @@ -0,0 +1,66 @@ +package llvm + +import ( + "bufio" + "fireball/core/ir" + "strconv" +) + +type textWriter struct { + m *ir.Module + + // Writing + + w *bufio.Writer + buffer [65]byte + + // Names + + globalUnnamedCount int64 + globalNameCounts map[string]uint64 + globalNames map[ir.Value]Name + + localUnnamedCount int64 + localNameCounts map[string]uint64 + localNames map[ir.Value]Name + + skipNameChar bool +} + +// Write + +func (w *textWriter) writeString(s string) { + _, _ = w.w.WriteString(s) +} + +func (w *textWriter) writeRune(r rune) { + _, _ = w.w.WriteRune(r) +} + +func (w *textWriter) writeByte(b byte) { + _ = w.w.WriteByte(b) +} + +func (w *textWriter) writeBool(b bool) { + if b { + w.writeString("true") + } else { + w.writeString("false") + } +} + +func (w *textWriter) writeQuotedString(s string) { + w.writeRune('"') + w.writeString(s) + w.writeRune('"') +} + +func (w *textWriter) writeInt(v int64) { + buffer := strconv.AppendInt(w.buffer[0:0], v, 10) + _, _ = w.w.Write(buffer) +} + +func (w *textWriter) writeUint(v uint64, base int) { + buffer := strconv.AppendUint(w.buffer[0:0], v, base) + _, _ = w.w.Write(buffer) +}