diff --git a/cmd/build/compiler.go b/cmd/build/compiler.go index b8e7c49..c83a140 100644 --- a/cmd/build/compiler.go +++ b/cmd/build/compiler.go @@ -156,7 +156,6 @@ func getLinker() string { switch runtime.GOOS { case "linux": return "ld.lld" - case "darwin": return "ld" diff --git a/cmd/lsp/hover.go b/cmd/lsp/hover.go index c01dcba..fcb70a3 100644 --- a/cmd/lsp/hover.go +++ b/cmd/lsp/hover.go @@ -2,6 +2,7 @@ package lsp import ( "fireball/core" + "fireball/core/abi" "fireball/core/ast" "github.com/MineGame159/protocol" "strconv" @@ -66,9 +67,9 @@ func getHoverToken(token *ast.Token) *protocol.Hover { value := uint32(0) if parent.Callee.String() == "sizeof" { - value = parent.Arg.Size() + value = abi.GetTargetAbi().Size(parent.Arg) } else { - value = parent.Arg.Align() + value = abi.GetTargetAbi().Align(parent.Arg) } return newHover(token, strconv.FormatUint(uint64(value), 10)) diff --git a/core/abi/abi.go b/core/abi/abi.go new file mode 100644 index 0000000..b8325d6 --- /dev/null +++ b/core/abi/abi.go @@ -0,0 +1,59 @@ +package abi + +import ( + "fireball/core/ast" + "runtime" +) + +type ClassKind uint8 + +const ( + None ClassKind = iota + Integer + SSE + Memory +) + +type Arg struct { + Class ClassKind + Bits uint32 +} + +func (a Arg) Bytes() uint32 { + return max(a.Bits, 8) / 8 +} + +type Abi interface { + Size(type_ ast.Type) uint32 + Align(type_ ast.Type) uint32 + + Fields(decl *ast.Struct) ([]*ast.Field, []uint32) + + Classify(type_ ast.Type, args []Arg) []Arg +} + +func GetTargetAbi() Abi { + switch runtime.GOOS { + case "linux", "darwin": + return AMD64 + + default: + panic("abi.GetTargetAbi() - Not implemented") + } +} + +func GetStructAbi(decl *ast.Struct) Abi { + return GetTargetAbi() +} + +func GetFuncAbi(decl *ast.Func) Abi { + return GetTargetAbi() + + /*for _, attribute := range decl.Attributes { + if attribute.Name.String() == "Extern" { + return GetTargetAbi() + } + } + + return FB*/ +} diff --git a/core/abi/amd64.go b/core/abi/amd64.go new file mode 100644 index 0000000..cc7411b --- /dev/null +++ b/core/abi/amd64.go @@ -0,0 +1,168 @@ +package abi + +import ( + "fireball/core/ast" +) + +var AMD64 Abi = &amd64{} + +type amd64 struct{} + +func (a *amd64) Size(type_ ast.Type) uint32 { + if type_, ok := ast.As[*ast.Struct](type_); ok { + layout := cLayout{} + + for _, field := range type_.Fields { + layout.add(a.Size(field.Type), a.Align(field.Type)) + } + + return layout.size() + } + + return getX64Size(a, type_) +} + +func (a *amd64) Align(type_ ast.Type) uint32 { + return getX64Align(type_) +} + +func (a *amd64) Fields(decl *ast.Struct) ([]*ast.Field, []uint32) { + layout := cLayout{} + offsets := make([]uint32, len(decl.Fields)) + + for i, field := range decl.Fields { + offsets[i] = layout.add(a.Size(field.Type), a.Align(field.Type)) + } + + return decl.Fields, offsets +} + +func (a *amd64) Classify(type_ ast.Type, args []Arg) []Arg { + switch type_ := type_.Resolved().(type) { + case *ast.Primitive: + var arg Arg + + switch type_.Kind { + case ast.Bool: + arg = i1 + + case ast.U8, ast.I8: + arg = i8 + case ast.U16, ast.I16: + arg = i16 + case ast.U32, ast.I32: + arg = i32 + case ast.U64, ast.I64: + arg = i64 + + case ast.F32: + arg = f32 + case ast.F64: + arg = f64 + + default: + return args + } + + return append(args, arg) + + case *ast.Pointer: + return append(args, ptr) + + case *ast.Struct, *ast.Array: + if a.Size(type_) > 64 { + return append(args, memory) + } + + args = a.flatten(type_, getSize(args), args) + size := getSize(args) + + if size > 16 { + args = args[0:0] + return append(args, memory) + } + + for _, arg := range args { + if arg.Class == Memory { + args = args[0:0] + return append(args, memory) + } + } + + return args + + case *ast.Enum: + return a.Classify(type_.ActualType, args) + + case *ast.Interface: + args = append(args, ptr) + return append(args, ptr) + + case *ast.Func: + return append(args, ptr) + + default: + return args + } +} + +func (a *amd64) flatten(type_ ast.Type, baseOffset uint32, args []Arg) []Arg { + switch type_ := type_.Resolved().(type) { + case *ast.Array: + baseSize := a.Size(type_.Base) + baseAlign := a.Align(type_.Base) + + for i := uint32(0); i < type_.Count; i++ { + offset := alignBytes(baseOffset+baseSize*i, baseAlign) + args = a.flatten(type_.Base, offset, args) + } + + return args + + case *ast.Struct: + fields, offsets := a.Fields(type_) + + for i, field := range fields { + offset := baseOffset + offsets[i] + args = a.flatten(field.Type, offset, args) + } + + return args + + case *ast.Interface: + void := ast.Primitive{Kind: ast.Void} + ptr := ast.Pointer{Pointee: &void} + + args = a.flatten(&ptr, baseOffset, args) + return a.flatten(&ptr, baseOffset+8, args) + + default: + typeArgs := a.Classify(type_, nil) + if len(typeArgs) != 1 { + panic("abi.amd64.flatten() - Failed to flatten type") + } + + arg := typeArgs[0] + offset := alignBytes(baseOffset, a.Align(type_)) + + var finalArg *Arg + args = getArg(args, offset, &finalArg) + + if finalArg.Class == None { + *finalArg = arg + } else if finalArg.Class == arg.Class { + finalArg.Bits += arg.Bits + } else if finalArg.Class == Memory || arg.Class == Memory { + finalArg.Class = Memory + finalArg.Bits += arg.Bits + } else if finalArg.Class == Integer || arg.Class == Integer { + finalArg.Class = Integer + finalArg.Bits += arg.Bits + } else { + finalArg.Class = SSE + finalArg.Bits += arg.Bits + } + + return args + } +} diff --git a/core/abi/c_layout.go b/core/abi/c_layout.go new file mode 100644 index 0000000..4028fa9 --- /dev/null +++ b/core/abi/c_layout.go @@ -0,0 +1,23 @@ +package abi + +type cLayout struct { + biggestAlign uint32 + offset uint32 +} + +func (l *cLayout) add(size, align uint32) uint32 { + l.biggestAlign = max(l.biggestAlign, align) + + offset := alignBytes(l.offset, align) + l.offset = offset + size + + return offset +} + +func (l *cLayout) size() uint32 { + if l.offset == 0 { + return 0 + } + + return alignBytes(l.offset, l.biggestAlign) +} diff --git a/core/abi/fb.go b/core/abi/fb.go new file mode 100644 index 0000000..b9da2dd --- /dev/null +++ b/core/abi/fb.go @@ -0,0 +1,43 @@ +package abi + +import "fireball/core/ast" + +var FB Abi = &fb{} + +type fb struct{} + +func (f *fb) Size(type_ ast.Type) uint32 { + if type_, ok := ast.As[*ast.Struct](type_); ok { + layout := cLayout{} + + for _, field := range type_.Fields { + layout.add(f.Size(field.Type), f.Align(field.Type)) + } + + return layout.size() + } + + return getX64Size(f, type_) +} + +func (f *fb) Align(type_ ast.Type) uint32 { + return getX64Align(type_) +} + +func (f *fb) Fields(decl *ast.Struct) ([]*ast.Field, []uint32) { + layout := cLayout{} + offsets := make([]uint32, len(decl.Fields)) + + for i, field := range decl.Fields { + offsets[i] = layout.add(f.Size(field.Type), f.Align(field.Type)) + } + + return decl.Fields, offsets +} + +func (f *fb) Classify(type_ ast.Type, args []Arg) []Arg { + switch type_.Resolved().(type) { + default: + panic("abi.amd64.Classify() - Not implemented") + } +} diff --git a/core/abi/utils.go b/core/abi/utils.go new file mode 100644 index 0000000..89eec66 --- /dev/null +++ b/core/abi/utils.go @@ -0,0 +1,44 @@ +package abi + +var i1 = Arg{Class: Integer, Bits: 1} + +var i8 = Arg{Class: Integer, Bits: 8} +var i16 = Arg{Class: Integer, Bits: 16} +var i32 = Arg{Class: Integer, Bits: 32} +var i64 = Arg{Class: Integer, Bits: 64} + +var f32 = Arg{Class: SSE, Bits: 32} +var f64 = Arg{Class: SSE, Bits: 64} + +var ptr = Arg{Class: Integer, Bits: 64} +var memory = Arg{Class: Memory, Bits: 64} + +func alignBytes(bytes, align uint32) uint32 { + if bytes%align != 0 { + bytes += align - (bytes % align) + } + + return bytes +} + +func getSize(args []Arg) uint32 { + length := uint32(len(args)) + + if length == 0 { + return 0 + } + + return (length-1)*8 + args[length-1].Bytes() +} + +func getArg(args []Arg, offset uint32, arg **Arg) []Arg { + i := offset / 8 + + for i >= uint32(len(args)) { + args = append(args, Arg{}) + } + + *arg = &args[i] + + return args +} diff --git a/core/abi/x64.go b/core/abi/x64.go new file mode 100644 index 0000000..2a47132 --- /dev/null +++ b/core/abi/x64.go @@ -0,0 +1,80 @@ +package abi + +import "fireball/core/ast" + +func getX64Size(abi Abi, type_ ast.Type) uint32 { + switch type_ := type_.Resolved().(type) { + case *ast.Primitive: + return getX64PrimitiveSize(type_.Kind) + + case *ast.Pointer: + return 8 + + case *ast.Array: + return abi.Size(type_.Base) * type_.Count + + case *ast.Enum: + return abi.Size(type_.ActualType) + + case *ast.Interface: + return 8 * 2 + + case *ast.Func: + return 8 + + default: + panic("abi.getX64Size() - Not implemented") + } +} + +func getX64Align(type_ ast.Type) uint32 { + switch type_ := type_.Resolved().(type) { + case *ast.Primitive: + return getX64PrimitiveSize(type_.Kind) + + case *ast.Pointer: + return 8 + + case *ast.Array: + return getX64Align(type_.Base) + + case *ast.Struct: + maxAlign := uint32(0) + + for _, field := range type_.Fields { + maxAlign = max(maxAlign, getX64Align(field.Type)) + } + + return maxAlign + + case *ast.Enum: + return getX64Align(type_.ActualType) + + case *ast.Interface: + return 8 + + case *ast.Func: + return 8 + + default: + panic("abi.getX64Align() - Not implemented") + } +} + +func getX64PrimitiveSize(kind ast.PrimitiveKind) uint32 { + switch kind { + case ast.Void: + return 0 + case ast.Bool, ast.U8, ast.I8: + return 1 + case ast.U16, ast.I16: + return 2 + case ast.U32, ast.I32, ast.F32: + return 4 + case ast.U64, ast.I64, ast.F64: + return 8 + + default: + panic("abi.getX64Size.Primitive() - Not implemented") + } +} diff --git a/core/architecture/layout.go b/core/architecture/layout.go deleted file mode 100644 index 4e3b50c..0000000 --- a/core/architecture/layout.go +++ /dev/null @@ -1,36 +0,0 @@ -package architecture - -type Layout interface { - Add(size, align uint32) uint32 - Size() uint32 -} - -type CLayout struct { - biggestAlign uint32 - offset uint32 -} - -func (l *CLayout) Add(size, align uint32) uint32 { - l.biggestAlign = max(l.biggestAlign, align) - - offset := alignValue(l.offset, align) - l.offset = offset + size - - return offset -} - -func (l *CLayout) Size() uint32 { - if l.offset == 0 { - return 0 - } - - return alignValue(l.offset, l.biggestAlign) -} - -func alignValue(value, align uint32) uint32 { - if value%align != 0 { - value += align - (value % align) - } - - return value -} diff --git a/core/ast/casts.go b/core/ast/casts.go deleted file mode 100644 index 5faed5c..0000000 --- a/core/ast/casts.go +++ /dev/null @@ -1,143 +0,0 @@ -package ast - -type CastKind uint8 - -const ( - None CastKind = iota - - Truncate - Extend - - Int2Float - Float2Int - - Pointer2Interface -) - -func GetCast(from, to Type) (CastKind, bool) { - if from.Equals(to) { - return None, true - } - - switch from := from.Resolved().(type) { - // Primitive -> ... - case *Primitive: - switch to := to.Resolved().(type) { - // Primitive -> Primitive - case *Primitive: - if (IsInteger(from.Kind) && IsInteger(to.Kind)) || (IsFloating(from.Kind) && IsFloating(to.Kind)) { - if to.Size() > from.Size() { - return Extend, true - } else if to.Size() < from.Size() { - return Truncate, true - } else { - return None, true - } - } else if IsInteger(from.Kind) && IsFloating(to.Kind) { - return Int2Float, true - } else if IsFloating(from.Kind) && IsInteger(to.Kind) { - return Float2Int, true - } - - // Primitive (Integer) -> Enum - case *Enum: - if IsInteger(from.Kind) { - if to.Size() > from.Size() { - return Extend, true - } else if to.Size() < from.Size() { - return Truncate, true - } else { - return None, true - } - } - } - - // Pointer -> Interface, Pointer, Func - case *Pointer: - switch to := to.Resolved().(type) { - case *Interface: - if implements(from, to) { - return Pointer2Interface, true - } - - case *Pointer, *Func: - return None, true - } - - // Enum -> Primitive (Integer) - case *Enum: - if to, ok := As[*Primitive](to); ok && IsInteger(to.Kind) { - if to.Size() > from.Size() { - return Extend, true - } else if to.Size() < from.Size() { - return Truncate, true - } else { - return None, true - } - } - } - - return None, false -} - -func GetImplicitCast(from, to Type) (CastKind, bool) { - if from.Equals(to) { - return None, true - } - - switch from := from.Resolved().(type) { - // Primitive -> Primitive - case *Primitive: - if to, ok := As[*Primitive](to); ok { - // Primitive (smaller integer / floating) -> Primitive (bigger integer / floating) - if ((IsInteger(from.Kind) && IsInteger(to.Kind)) || (IsFloating(from.Kind) && IsFloating(to.Kind))) && to.Size() > from.Size() { - return Extend, true - } - - // Primitive (same integer) -> Primitive (same floating) - // TODO: Allow converting a smaller integer to the next bigger floating (eg. i16 -> f32) - if IsInteger(from.Kind) && IsFloating(to.Kind) && to.Size() == from.Size() { - return Int2Float, true - } - } - - // Pointer -> Interface, Pointer (*void) - case *Pointer: - switch to := to.Resolved().(type) { - case *Interface: - if implements(from, to) { - return Pointer2Interface, true - } - - case *Pointer: - if IsPrimitive(to.Pointee, Void) { - return None, true - } - } - } - - return None, false -} - -func implements(type_ Type, inter *Interface) bool { - // Check struct pointee - if pointer, ok := type_.(*Pointer); ok { - if s, ok := As[*Struct](pointer.Pointee); ok { - type_ = s - } else { - return false - } - } else { - return false - } - - // Get resolver - resolver := GetParent[*File](type_).Resolver - - // Check impl - if resolver.GetImpl(type_, inter) != nil { - return true - } - - return false -} diff --git a/core/ast/statements.go b/core/ast/statements.go index 8f06f01..f300b18 100644 --- a/core/ast/statements.go +++ b/core/ast/statements.go @@ -600,10 +600,6 @@ type Return struct { } func NewReturn(node cst.Node, value Expr) *Return { - if value == nil { - return nil - } - r := &Return{ cst: node, Value: value, diff --git a/core/ast/types.go b/core/ast/types.go index bda0a77..d82651a 100644 --- a/core/ast/types.go +++ b/core/ast/types.go @@ -21,9 +21,6 @@ type TypeVisitor interface { type Type interface { Node - Size() uint32 - Align() uint32 - Equals(other Type) bool Resolved() Type diff --git a/core/ast/types_manual.go b/core/ast/types_manual.go index df59983..61b3a52 100644 --- a/core/ast/types_manual.go +++ b/core/ast/types_manual.go @@ -1,7 +1,6 @@ package ast import ( - "fireball/core/architecture" "fmt" "slices" ) @@ -28,42 +27,12 @@ func As[T Type](type_ Type) (T, bool) { // Primitive -func (p *Primitive) Size() uint32 { - switch p.Kind { - case Void: - return 0 - case Bool, U8, I8: - return 1 - case U16, I16: - return 2 - case U32, I32, F32: - return 4 - case U64, I64, F64: - return 8 - - default: - panic("ast.Primitive.Size() - Not implemented") - } -} - -func (p *Primitive) Align() uint32 { - return p.Size() -} - func (p *Primitive) Equals(other Type) bool { return IsPrimitive(other, p.Kind) } // Pointer -func (p *Pointer) Size() uint32 { - return 8 -} - -func (p *Pointer) Align() uint32 { - return 8 -} - func (p *Pointer) Equals(other Type) bool { if p2, ok := As[*Pointer](other); ok { return typesEquals(p.Pointee, p2.Pointee) @@ -74,14 +43,6 @@ func (p *Pointer) Equals(other Type) bool { // Array -func (a *Array) Size() uint32 { - return a.Base.Size() * a.Count -} - -func (a *Array) Align() uint32 { - return a.Base.Align() -} - func (a *Array) Equals(other Type) bool { if a2, ok := As[*Array](other); ok { return typesEquals(a.Base, a2.Base) && a.Count == a2.Count @@ -92,22 +53,6 @@ func (a *Array) Equals(other Type) bool { // Resolvable -func (r *Resolvable) Size() uint32 { - if r.Resolved() == nil { - panic("ast.Resolvable.Size() - Not resolved") - } - - return r.Resolved().Size() -} - -func (r *Resolvable) Align() uint32 { - if r.Resolved() == nil { - panic("ast.Resolvable.Align() - Not resolved") - } - - return r.Resolved().Align() -} - func (r *Resolvable) Equals(other Type) bool { if r.Resolved() == nil { panic("ast.Resolvable.Equals() - Not resolved") @@ -118,26 +63,6 @@ func (r *Resolvable) Equals(other Type) bool { // Struct -func (s *Struct) Size() uint32 { - layout := architecture.CLayout{} - - for _, field := range s.Fields { - layout.Add(field.Type.Size(), field.Type.Align()) - } - - return layout.Size() -} - -func (s *Struct) Align() uint32 { - align := uint32(0) - - for _, field := range s.Fields { - align = max(align, field.Type.Align()) - } - - return align -} - func (s *Struct) Equals(other Type) bool { return other != nil && s == other.Resolved() } @@ -152,14 +77,6 @@ func (s *Struct) AcceptType(visitor TypeVisitor) { // Enum -func (e *Enum) Size() uint32 { - return e.ActualType.Size() -} - -func (e *Enum) Align() uint32 { - return e.ActualType.Align() -} - func (e *Enum) Equals(other Type) bool { return e == other.Resolved() } @@ -174,14 +91,6 @@ func (e *Enum) AcceptType(visitor TypeVisitor) { // Interface -func (i *Interface) Size() uint32 { - return 8 * 2 -} - -func (i *Interface) Align() uint32 { - return 8 -} - func (i *Interface) Equals(other Type) bool { return other != nil && i == other.Resolved() } @@ -196,14 +105,6 @@ func (i *Interface) AcceptType(visitor TypeVisitor) { // Func -func (f *Func) Size() uint32 { - return 8 -} - -func (f *Func) Align() uint32 { - return 8 -} - func (f *Func) Equals(other Type) bool { if f2, ok := As[*Func](other); ok { if f.Name != nil && f2.Name != nil { diff --git a/core/checker/checker.go b/core/checker/checker.go index 5bf0207..6ad6496 100644 --- a/core/checker/checker.go +++ b/core/checker/checker.go @@ -3,6 +3,7 @@ package checker import ( "fireball/core" "fireball/core/ast" + "fireball/core/common" "fireball/core/utils" "fmt" ) @@ -152,7 +153,7 @@ func (c *checker) checkRequired(required ast.Type, expr ast.Expr) { return } - if _, ok := ast.GetImplicitCast(expr.Result().Type, required); !ok { + if _, ok := common.GetImplicitCast(expr.Result().Type, required); !ok { c.error(expr, "Expected a '%s' but got a '%s'", ast.PrintType(required), ast.PrintType(expr.Result().Type)) } } diff --git a/core/checker/expressions.go b/core/checker/expressions.go index 5274ef9..5de89e4 100644 --- a/core/checker/expressions.go +++ b/core/checker/expressions.go @@ -2,6 +2,7 @@ package checker import ( "fireball/core/ast" + "fireball/core/common" "fireball/core/scanner" "fireball/core/utils" "strconv" @@ -553,7 +554,7 @@ func (c *checker) VisitCast(expr *ast.Cast) { // Check based on the operator switch expr.Operator.Token().Kind { case scanner.As: - if _, ok := ast.GetCast(expr.Value.Result().Type, expr.Target); !ok { + if _, ok := common.GetCast(expr.Value.Result().Type, expr.Target); !ok { c.error(expr, "Cannot cast type '%s' to type '%s'", ast.PrintType(expr.Value.Result().Type), ast.PrintType(expr.Target)) } @@ -893,7 +894,7 @@ func (c *checker) checkBinary(expr, left, right ast.Expr, operator *ast.Token, a rightType := right.Result().Type castType := leftType - _, castOk := ast.GetImplicitCast(rightType, leftType) + _, castOk := common.GetImplicitCast(rightType, leftType) if !castOk { if assignment { @@ -902,7 +903,7 @@ func (c *checker) checkBinary(expr, left, right ast.Expr, operator *ast.Token, a } castType = rightType - _, castOk = ast.GetImplicitCast(leftType, rightType) + _, castOk = common.GetImplicitCast(leftType, rightType) } // Arithmetic diff --git a/core/codegen/abi.go b/core/codegen/abi.go new file mode 100644 index 0000000..f85dfe7 --- /dev/null +++ b/core/codegen/abi.go @@ -0,0 +1,372 @@ +package codegen + +import ( + "fireball/core/abi" + "fireball/core/ast" + "fireball/core/ir" +) + +// Parameters + +func (c *codegen) valueToParams(a abi.Abi, value exprValue, valueType ast.Type, values []ir.Value) []ir.Value { + args := a.Classify(valueType, nil) + if len(args) == 0 { + panic("codegen.valueToParams() - Failed to classify parameter type") + } + + // Memory + if args[0].Class == abi.Memory { + value = c.toAddressable(value, valueType) + return append(values, value.v) + } + + // Struct + if typeIsAbiStruct(valueType) { + value = c.toAddressable(value, valueType) + typ := getAbiStructType(args, false) + + for i, arg := range args { + if arg.Class == abi.None { + continue + } + + switch arg.Class { + case abi.Integer, abi.SSE: + pointer := c.block.Add(&ir.GetElementPtrInst{ + PointerTyp: &ir.PointerType{Pointee: typ.Fields[i]}, + Typ: typ, + Pointer: value.v, + Indices: []ir.Value{ + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(0)}, + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(uint64(i))}, + }, + Inbounds: true, + }) + + values = append(values, c.block.Add(&ir.LoadInst{ + Typ: typ.Fields[i], + Pointer: pointer, + })) + + default: + panic("codegen.valueToParams() - Invalid struct argument class") + } + } + + return values + } + + // Single + value = c.load(value, valueType) + return append(values, value.v) +} + +func (c *codegen) paramsToVariable(args []abi.Arg, values []*ir.Param, variable ir.Value, variableType ast.Type) { + if len(args) == 0 { + panic("codegen.paramsToVariable() - Failed to classify parameter type") + } + + if len(args) != len(values) { + panic("codegen.paramsToVariable() - Parameter count does not match abi argument count") + } + + // Memory + if args[0].Class == abi.Memory { + align := abi.GetTargetAbi().Align(variableType) + + value := c.block.Add(&ir.LoadInst{ + Typ: c.types.get(variableType), + Pointer: values[0], + Align: align, + }) + + c.block.Add(&ir.StoreInst{ + Pointer: variable, + Value: value, + Align: align, + }) + + return + } + + // Struct + if typeIsAbiStruct(variableType) { + typ := getAbiStructType(args, false) + valueI := 0 + + for i, arg := range args { + if arg.Class == abi.None { + continue + } + + switch arg.Class { + case abi.Integer, abi.SSE: + pointer := c.block.Add(&ir.GetElementPtrInst{ + PointerTyp: &ir.PointerType{Pointee: typ.Fields[i]}, + Typ: typ, + Pointer: variable, + Indices: []ir.Value{ + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(0)}, + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(uint64(i))}, + }, + Inbounds: true, + }) + + c.block.Add(&ir.StoreInst{ + Pointer: pointer, + Value: values[valueI], + }) + + default: + panic("codegen.paramsToVariable() - Invalid struct argument class") + } + + valueI++ + } + + return + } + + // Single + c.block.Add(&ir.StoreInst{ + Pointer: variable, + Value: values[0], + Align: abi.GetTargetAbi().Align(variableType), + }) +} + +// Return values + +func (c *codegen) valueToReturnValue(a abi.Abi, value exprValue, returnType ast.Type, params []*ir.Param) exprValue { + args := a.Classify(returnType, nil) + if len(args) == 0 { + panic("codegen.valueToReturnValue() - Failed to classify parameter type") + } + + // Memory + if args[0].Class == abi.Memory { + value = c.load(value, returnType) + + c.block.Add(&ir.StoreInst{ + Pointer: params[0], + Value: value.v, + Align: abi.GetTargetAbi().Align(returnType), + }) + + return exprValue{} + } + + // Struct + if typeIsAbiStruct(returnType) { + value = c.toAddressable(value, returnType) + + typWithEmpty := getAbiStructType(args, false) + typWithoutEmpty := getAbiStructType(args, true) + + var result ir.Value = &ir.ZeroInitConst{Typ: typWithoutEmpty} + elementI := 0 + + for i, arg := range args { + if arg.Class == abi.None { + continue + } + + switch arg.Class { + case abi.Integer, abi.SSE: + pointer := c.block.Add(&ir.GetElementPtrInst{ + PointerTyp: &ir.PointerType{Pointee: typWithEmpty.Fields[i]}, + Typ: typWithEmpty, + Pointer: value.v, + Indices: []ir.Value{ + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(0)}, + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(uint64(i))}, + }, + Inbounds: true, + }) + + element := c.block.Add(&ir.LoadInst{ + Typ: typWithEmpty.Fields[i], + Pointer: pointer, + }) + + result = c.block.Add(&ir.InsertValueInst{ + Value: result, + Element: element, + Indices: []uint32{uint32(elementI)}, + }) + + default: + panic("codegen.valueToReturnValue() - Invalid struct argument class") + } + + elementI++ + } + + return exprValue{v: result} + } + + // Single + return value +} + +func (c *codegen) returnValueToValue(a abi.Abi, value exprValue, returnType ast.Type) exprValue { + args := a.Classify(returnType, nil) + if len(args) == 0 { + panic("codegen.returnValueToValue() - Failed to classify parameter type") + } + + // Memory + if args[0].Class == abi.Memory { + value := c.block.Add(&ir.LoadInst{ + Typ: c.types.get(returnType), + Pointer: value.v, + Align: abi.GetTargetAbi().Align(returnType), + }) + + return exprValue{v: value} + } + + // Struct + if typeIsAbiStruct(returnType) { + result := c.allocas.get(returnType, "") + typ := getAbiStructType(args, false) + + elementI := 0 + + for i, arg := range args { + if arg.Class == abi.None { + continue + } + + switch arg.Class { + case abi.Integer, abi.SSE: + element := c.block.Add(&ir.ExtractValueInst{ + Value: value.v, + Indices: []uint32{uint32(elementI)}, + }) + + pointer := c.block.Add(&ir.GetElementPtrInst{ + PointerTyp: &ir.PointerType{Pointee: typ.Fields[i]}, + Typ: typ, + Pointer: result, + Indices: []ir.Value{ + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(0)}, + &ir.IntConst{Typ: ir.I32, Value: ir.Unsigned(uint64(i))}, + }, + Inbounds: true, + }) + + c.block.Add(&ir.StoreInst{ + Pointer: pointer, + Value: element, + }) + + default: + panic("codegen.returnValueToValue() - Invalid struct argument class") + } + + elementI++ + } + + return exprValue{ + v: result, + addressable: true, + } + } + + // Single + return value +} + +// Helpers + +func (c *codegen) toAddressable(value exprValue, valueType ast.Type) exprValue { + if !value.addressable { + pointer := c.allocas.get(valueType, "") + + c.block.Add(&ir.StoreInst{ + Pointer: pointer, + Value: value.v, + Align: abi.GetTargetAbi().Align(valueType), + }) + + return exprValue{ + v: pointer, + addressable: true, + } + } + + return value +} + +// Type helpers + +func typeIsAbiStruct(type_ ast.Type) bool { + switch type_.Resolved().(type) { + case *ast.Struct, *ast.Interface: + return true + default: + return false + } +} + +func getAbiStructType(args []abi.Arg, skipEmpty bool) *ir.StructType { + fields := make([]ir.Type, 0, len(args)) + + for _, arg := range args { + if arg.Class == abi.None && skipEmpty { + continue + } + + fields = append(fields, getAbiArgType(arg)) + } + + return &ir.StructType{ + Name: "", + Fields: fields, + } +} + +func getAbiArgType(arg abi.Arg) ir.Type { + switch arg.Class { + case abi.Integer: + return getAbiIntIrType(arg) + case abi.SSE: + return getAbiSseIrType(arg) + + default: + panic("codegen.getAbiArgType() - Invalid class") + } +} + +func getAbiIntIrType(arg abi.Arg) ir.Type { + if arg.Bits == 1 { + return ir.I1 + } + if arg.Bits <= 8 { + return ir.I8 + } + if arg.Bits <= 16 { + return ir.I16 + } + if arg.Bits <= 32 { + return ir.I32 + } + if arg.Bits <= 64 { + return ir.I64 + } + + panic("codegen.getAbiIntIrType() - Invalid size") +} + +func getAbiSseIrType(arg abi.Arg) ir.Type { + switch arg.Bits { + case 32: + return ir.F32 + case 64: + return ir.F64 + + default: + panic("codegen.getAbiSseIrType() - Invalid size") + } +} diff --git a/core/codegen/allocas.go b/core/codegen/allocas.go new file mode 100644 index 0000000..850164d --- /dev/null +++ b/core/codegen/allocas.go @@ -0,0 +1,34 @@ +package codegen + +import ( + "fireball/core/abi" + "fireball/core/ast" + "fireball/core/ir" +) + +type allocas struct { + c *codegen + + count int +} + +func (a *allocas) reset() { + a.count = 0 +} + +func (a *allocas) get(type_ ast.Type, name string) *ir.AllocaInst { + alloca := &ir.AllocaInst{ + Typ: a.c.types.get(type_), + Align: abi.GetTargetAbi().Align(type_), + } + + if name == "" { + name = "temp" + } + alloca.SetName(name) + + a.c.function.Blocks[0].Insert(a.count, alloca) + a.count++ + + return alloca +} diff --git a/core/codegen/codegen.go b/core/codegen/codegen.go index d7f0fe7..df1181c 100644 --- a/core/codegen/codegen.go +++ b/core/codegen/codegen.go @@ -1,7 +1,9 @@ package codegen import ( + "fireball/core/abi" "fireball/core/ast" + "fireball/core/common" "fireball/core/ir" "fmt" ) @@ -14,12 +16,11 @@ type codegen struct { types types vtables vtables scopes scopes + allocas allocas staticVariables map[ast.Node]exprValue functions map[*ast.Func]*ir.Func - allocas map[ast.Node]exprValue - astFunction *ast.Func function *ir.Func block *ir.Block @@ -54,9 +55,10 @@ func Emit(ctx *Context, path string, root ast.RootResolver, file *ast.File) *ir. // File c.module.Path = path - c.types.module = c.module + c.types.c = c c.vtables.c = c c.scopes.c = c + c.allocas.c = c c.scopes.pushFile(path) @@ -172,13 +174,13 @@ func (c *codegen) getMangledName(function *ast.Func) string { // IR -func (c *codegen) load(value exprValue, type_ ast.Type) exprValue { +func (c *codegen) load(value exprValue, valueType ast.Type) exprValue { if value.addressable { return exprValue{ v: c.block.Add(&ir.LoadInst{ Typ: value.v.Type().(*ir.PointerType).Pointee, Pointer: value.v, - Align: type_.Align(), + Align: abi.GetTargetAbi().Align(valueType), }), addressable: false, } @@ -192,19 +194,27 @@ func (c *codegen) loadExpr(expr ast.Expr) exprValue { } func (c *codegen) implicitCast(required ast.Type, value exprValue, valueType ast.Type) exprValue { - if kind, ok := ast.GetImplicitCast(valueType, required); ok && kind != ast.None { + if needsImplicitCast(valueType, required) { return c.cast(value, valueType, required, nil) } return value } +func needsImplicitCast(from, to ast.Type) bool { + if kind, ok := common.GetImplicitCast(from, to); ok { + return kind != common.None + } + + return false +} + func (c *codegen) implicitCastLoadExpr(required ast.Type, expr ast.Expr) exprValue { return c.implicitCast(required, c.loadExpr(expr), expr.Result().Type) } func (c *codegen) cast(value exprValue, from, to ast.Type, location ast.Node) exprValue { - kind, ok := ast.GetCast(from, to) + kind, ok := common.GetCast(from, to) if !ok { panic("codegen.convertAstCastKind() - ast.GetCast() returned false") } @@ -212,8 +222,8 @@ func (c *codegen) cast(value exprValue, from, to ast.Type, location ast.Node) ex return c.convertCast(value, kind, from, to, location) } -func (c *codegen) convertCast(value exprValue, kind ast.CastKind, from, to ast.Type, location ast.Node) exprValue { - if kind == ast.None { +func (c *codegen) convertCast(value exprValue, kind common.CastKind, from, to ast.Type, location ast.Node) exprValue { + if kind == common.None { return value } @@ -221,7 +231,7 @@ func (c *codegen) convertCast(value exprValue, kind ast.CastKind, from, to ast.T toIr := c.types.get(to) switch kind { - case ast.Truncate: + case common.Truncate: result := c.block.Add(&ir.TruncInst{ Value: value.v, Typ: toIr, @@ -230,7 +240,7 @@ func (c *codegen) convertCast(value exprValue, kind ast.CastKind, from, to ast.T c.setLocationMeta(result, location) return exprValue{v: result} - case ast.Extend: + case common.Extend: var result ir.MetaValue if ast.IsFloating(to.Resolved().(*ast.Primitive).Kind) { @@ -255,7 +265,7 @@ func (c *codegen) convertCast(value exprValue, kind ast.CastKind, from, to ast.T c.setLocationMeta(result, location) return exprValue{v: result} - case ast.Int2Float: + case common.Int2Float: result := c.block.Add(&ir.I2FInst{ Signed: ast.IsSigned(from.Resolved().(*ast.Primitive).Kind), Value: value.v, @@ -265,7 +275,7 @@ func (c *codegen) convertCast(value exprValue, kind ast.CastKind, from, to ast.T c.setLocationMeta(result, location) return exprValue{v: result} - case ast.Float2Int: + case common.Float2Int: result := c.block.Add(&ir.F2IInst{ Signed: ast.IsSigned(to.Resolved().(*ast.Primitive).Kind), Value: value.v, @@ -275,7 +285,7 @@ func (c *codegen) convertCast(value exprValue, kind ast.CastKind, from, to ast.T c.setLocationMeta(result, location) return exprValue{v: result} - case ast.Pointer2Interface: + case common.Pointer2Interface: type_ := from.Resolved().(*ast.Pointer).Pointee result := c.block.Add(&ir.InsertValueInst{ @@ -430,51 +440,6 @@ func (c *codegen) beginBlock(block *ir.Block) { c.block = block } -func (c *codegen) findAllocas(function *ast.Func) { - c.allocas = make(map[ast.Node]exprValue) - - a := &allocaFinder{c: c} - a.VisitNode(function) -} - -type allocaFinder struct { - c *codegen -} - -func (a *allocaFinder) VisitNode(node ast.Node) { - switch node := node.(type) { - case *ast.Var: - a.c.allocas[node] = exprValue{ - v: a.c.alloca(node.ActualType, node.Name.String()+".var", node), - addressable: true, - } - - case *ast.Call: - if callNeedsTempVariable(node) { - returns := node.Callee.Result().Type.(*ast.Func).Returns - - a.c.allocas[node] = exprValue{ - v: a.c.alloca(returns, "", node), - addressable: true, - } - } - - case *ast.Member: - if node.Value.Result().Kind != ast.TypeResultKind && node.Result().Kind == ast.CallableResultKind && !node.Value.Result().IsAddressable() { - type_ := node.Value.Result().Type - - if type_ != nil { - a.c.allocas[node] = exprValue{ - v: a.c.alloca(type_, "", node), - addressable: true, - } - } - } - } - - node.AcceptChildren(a) -} - func callNeedsTempVariable(expr *ast.Call) bool { function := expr.Callee.Result().Type.(*ast.Func) diff --git a/core/codegen/declarations.go b/core/codegen/declarations.go index 16bdaea..5cfa20c 100644 --- a/core/codegen/declarations.go +++ b/core/codegen/declarations.go @@ -1,6 +1,7 @@ package codegen import ( + "fireball/core/abi" "fireball/core/ast" "fireball/core/cst" "fireball/core/ir" @@ -37,36 +38,45 @@ func (c *codegen) VisitFunc(decl *ast.Func) { c.beginBlock(function.Block("entry")) c.scopes.push(function.Meta()) + c.allocas.reset() // Add this variable if struct_ := decl.Method(); struct_ != nil { name := scanner.Token{Kind: scanner.Identifier, Lexeme: "this"} node := cst.Node{Kind: cst.IdentifierNode, Token: name, Range: decl.Name.Cst().Range} - c.scopes.addVariable(ast.NewToken(node, name), struct_, exprValue{v: function.Typ.Params[0]}, 1) + c.scopes.addVariable(ast.NewToken(node, name), struct_, function.Typ.Params[0], 1) } // Copy parameters - for i, param := range decl.Params { - index := i - if decl.Method() != nil { - index++ - } + funcAbi := abi.GetFuncAbi(decl) + returnArgs := funcAbi.Classify(decl.Returns, nil) - pointer := c.alloca(param.Type, param.Name.String()+".var", param) + index := 0 + if len(returnArgs) == 1 && returnArgs[0].Class == abi.Memory { + index++ + } + if decl.Method() != nil { + index++ + } + + paramI := index - c.block.Add(&ir.StoreInst{ - Pointer: pointer, - Value: function.Typ.Params[index], - Align: param.Type.Align(), - }) + for _, param := range decl.Params { + pointer := c.allocas.get(param.Type, param.Name.String()+".var") + c.setLocationMeta(pointer, param) - c.scopes.addVariable(param.Name, param.Type, exprValue{v: pointer}, uint32(index+1)) + args := funcAbi.Classify(param.Type, nil) + params := function.Typ.Params[index : index+len(args)] + index += len(args) + + c.paramsToVariable(args, params, pointer, param.Type) + + paramI++ + c.scopes.addVariable(param.Name, param.Type, pointer, uint32(paramI)) } // Body - c.findAllocas(decl) - for _, stmt := range decl.Body { c.acceptStmt(stmt) } diff --git a/core/codegen/expressions.go b/core/codegen/expressions.go index a9c89f2..313fcab 100644 --- a/core/codegen/expressions.go +++ b/core/codegen/expressions.go @@ -1,7 +1,9 @@ package codegen import ( + "fireball/core/abi" "fireball/core/ast" + "fireball/core/common" "fireball/core/ir" "fireball/core/scanner" "log" @@ -183,14 +185,14 @@ func (c *codegen) VisitStructInitializer(expr *ast.StructInitializer) { Callee: malloc.v, Args: []ir.Value{&ir.IntConst{ Typ: c.types.get(mallocFunc.Params[0].Type), - Value: ir.Unsigned(uint64(struct_.Size())), + Value: ir.Unsigned(uint64(abi.GetTargetAbi().Size(struct_))), }}, }) c.block.Add(&ir.StoreInst{ Pointer: pointer, Value: result, - Align: struct_.Align(), + Align: abi.GetTargetAbi().Align(struct_), }) c.exprResult = exprValue{v: pointer} @@ -231,7 +233,7 @@ func (c *codegen) VisitAllocateArray(expr *ast.AllocateArray) { Callee: malloc.v, Args: []ir.Value{&ir.IntConst{ Typ: c.types.get(mallocFunc.Params[0].Type), - Value: ir.Unsigned(uint64(expr.Type.Size())), + Value: ir.Unsigned(uint64(abi.GetTargetAbi().Size(expr.Type))), }}, }) @@ -285,7 +287,7 @@ func (c *codegen) VisitUnary(expr *ast.Unary) { result = c.block.Add(&ir.LoadInst{ Typ: result.Type().(*ir.PointerType).Pointee, Pointer: result, - Align: expr.Result().Type.Align(), + Align: abi.GetTargetAbi().Align(expr.Result().Type), }) } @@ -314,7 +316,7 @@ func (c *codegen) VisitUnary(expr *ast.Unary) { c.block.Add(&ir.StoreInst{ Pointer: value.v, Value: newValue.v, - Align: expr.Value.Result().Type.Align(), + Align: abi.GetTargetAbi().Align(expr.Value.Result().Type), }) result = newValue.v @@ -356,7 +358,7 @@ func (c *codegen) VisitUnary(expr *ast.Unary) { c.block.Add(&ir.StoreInst{ Pointer: value.v, Value: newValue.v, - Align: expr.Value.Result().Type.Align(), + Align: abi.GetTargetAbi().Align(expr.Value.Result().Type), }) result = prevValue.v @@ -514,7 +516,7 @@ func (c *codegen) VisitAssignment(expr *ast.Assignment) { store := c.block.Add(&ir.StoreInst{ Pointer: assignee.v, Value: value.v, - Align: expr.Result().Type.Align(), + Align: abi.GetTargetAbi().Align(expr.Result().Type), }) c.setLocationMeta(store, expr) @@ -580,9 +582,9 @@ func (c *codegen) VisitTypeCall(expr *ast.TypeCall) { switch expr.Callee.String() { case "sizeof": - value = expr.Arg.Size() + value = abi.GetTargetAbi().Size(expr.Arg) case "alignof": - value = expr.Arg.Align() + value = abi.GetTargetAbi().Align(expr.Arg) default: panic("codegen.VisitTypeCall() - Not implemented") @@ -627,19 +629,38 @@ func (c *codegen) VisitCall(expr *ast.Call) { argCount++ } - args := make([]ir.Value, argCount) + funcAbi := abi.GetFuncAbi(function) + returnArgs := funcAbi.Classify(function.Returns, nil) + + args := make([]ir.Value, 0, argCount) + hasReturnPtr := false + + if len(returnArgs) == 1 && returnArgs[0].Class == abi.Memory { + pointer := c.allocas.get(function.Returns, "") + pointer.TypPtr.SRet = c.types.get(function.Returns) + + args = append(args, pointer) + hasReturnPtr = true + } if hasThis { - args[0] = c.this.v + args = append(args, c.this.v) } for i, arg := range expr.Args { - if hasThis { - args[i+1] = c.implicitCastLoadExpr(function.Params[i].Type, arg).v - } else if i >= len(function.Params) { - args[i] = c.loadExpr(arg).v + if i >= len(function.Params) { + args = append(args, c.loadExpr(arg).v) } else { - args[i] = c.implicitCastLoadExpr(function.Params[i].Type, arg).v + param := function.Params[i] + var value exprValue + + if needsImplicitCast(arg.Result().Type, param.Type) { + value = c.implicitCastLoadExpr(param.Type, arg) + } else { + value = c.acceptExpr(arg) + } + + args = c.valueToParams(funcAbi, value, param.Type, args) } } @@ -651,26 +672,43 @@ func (c *codegen) VisitCall(expr *ast.Call) { } // Call - result := c.block.Add(&ir.CallInst{ + call := c.block.Add(&ir.CallInst{ Typ: c.types.get(function).(*ir.FuncType), Callee: callee.v, Args: args, }) - c.setLocationMetaCst(result, expr, scanner.LeftParen) - c.exprResult = exprValue{v: result} + c.setLocationMetaCst(call, expr, scanner.LeftParen) + + if ast.IsPrimitive(function.Returns, ast.Void) { + c.exprResult = exprValue{v: call} + } else { + result := exprValue{v: call} + + if hasReturnPtr { + result = exprValue{ + v: args[0], + addressable: true, + } + } + + c.exprResult = c.returnValueToValue(funcAbi, result, function.Returns) + } // 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] + pointer := c.allocas.get(function.Returns, "") c.block.Add(&ir.StoreInst{ - Pointer: pointer.v, + Pointer: pointer, Value: c.exprResult.v, - Align: function.Returns.Align(), + Align: abi.GetTargetAbi().Align(function.Returns), }) - c.exprResult = pointer + c.exprResult = exprValue{ + v: pointer, + addressable: true, + } } } @@ -682,7 +720,7 @@ func (c *codegen) VisitIndex(expr *ast.Index) { value = exprValue{v: c.block.Add(&ir.LoadInst{ Typ: value.v.Type().(*ir.PointerType).Pointee, Pointer: value.v, - Align: pointer.Pointee.Align(), + Align: abi.GetTargetAbi().Align(pointer.Pointee), })} } @@ -829,15 +867,18 @@ func (c *codegen) VisitMember(expr *ast.Member) { } if node.Method() != nil && !value.addressable { - pointer := c.allocas[expr] + pointer := c.allocas.get(expr.Value.Result().Type, "") c.block.Add(&ir.StoreInst{ - Pointer: pointer.v, + Pointer: pointer, Value: value.v, - Align: node.Align(), + Align: abi.GetTargetAbi().Align(node), }) - value = pointer + value = exprValue{ + v: pointer, + addressable: true, + } } c.exprResult = c.getFunction(node) @@ -865,7 +906,7 @@ func (c *codegen) memberLoad(type_ ast.Type, value exprValue) (exprValue, *ast.S load := c.block.Add(&ir.LoadInst{ Typ: value.v.Type().(*ir.PointerType).Pointee, Pointer: value.v, - Align: v.Align(), + Align: abi.GetTargetAbi().Align(v), }) return exprValue{ @@ -910,7 +951,7 @@ func (c *codegen) binaryLoad(left, right ast.Expr, operator *ast.Token) exprValu } // Left -> Right - cast, castOk := ast.GetImplicitCast(left.Result().Type, right.Result().Type) + cast, castOk := common.GetImplicitCast(left.Result().Type, right.Result().Type) if castOk { to := right.Result().Type @@ -922,7 +963,7 @@ func (c *codegen) binaryLoad(left, right ast.Expr, operator *ast.Token) exprValu } // Right -> Left - cast, castOk = ast.GetImplicitCast(right.Result().Type, left.Result().Type) + cast, castOk = common.GetImplicitCast(right.Result().Type, left.Result().Type) if castOk { to := left.Result().Type diff --git a/core/codegen/instructions.go b/core/codegen/instructions.go index b965b53..9f8dcda 100644 --- a/core/codegen/instructions.go +++ b/core/codegen/instructions.go @@ -6,23 +6,6 @@ import ( "fireball/core/scanner" ) -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(), - }) - - if name != "" { - pointer.SetName(name) - } - - if node != nil { - c.setLocationMeta(pointer, node) - } - - return pointer -} - func (c *codegen) setLocationMeta(value ir.MetaValue, node ast.Node) { if node == nil { return diff --git a/core/codegen/scopes.go b/core/codegen/scopes.go index 48882bb..50e04eb 100644 --- a/core/codegen/scopes.go +++ b/core/codegen/scopes.go @@ -147,7 +147,7 @@ func (s *scopes) getVariable(name scanner.Token) *variable { return nil } -func (s *scopes) addVariable(name ast.Node, type_ ast.Type, value exprValue, arg uint32) *variable { +func (s *scopes) addVariable(name ast.Node, type_ ast.Type, value ir.Value, arg uint32) *variable { if s.declare == nil { s.declare = s.c.module.Declare( "llvm.dbg.declare", @@ -177,7 +177,7 @@ func (s *scopes) addVariable(name ast.Node, type_ ast.Type, value exprValue, arg declare := s.c.block.Add(&ir.CallInst{ Callee: s.declare, Args: []ir.Value{ - value.v, + value, s.c.module.Meta(meta), s.c.module.Meta(&ir.ExpressionMeta{}), }, @@ -185,11 +185,12 @@ func (s *scopes) addVariable(name ast.Node, type_ ast.Type, value exprValue, arg s.c.setLocationMeta(declare, name) - value.addressable = true - s.variables = append(s.variables, variable{ - name: name, - value: value, + name: name, + value: exprValue{ + v: value, + addressable: true, + }, }) s.get().variableCount++ diff --git a/core/codegen/statements.go b/core/codegen/statements.go index d2fe25d..72b6641 100644 --- a/core/codegen/statements.go +++ b/core/codegen/statements.go @@ -1,6 +1,7 @@ package codegen import ( + "fireball/core/abi" "fireball/core/ast" "fireball/core/ir" ) @@ -21,7 +22,9 @@ func (c *codegen) VisitExpression(stmt *ast.Expression) { func (c *codegen) VisitVar(stmt *ast.Var) { // Variable - pointer := c.allocas[stmt] + pointer := c.allocas.get(stmt.ActualType, stmt.Name.String()+".var") + c.setLocationMeta(pointer, stmt) + c.scopes.addVariable(stmt.Name, stmt.ActualType, pointer, 0) // Initializer @@ -34,9 +37,9 @@ func (c *codegen) VisitVar(stmt *ast.Var) { } store := c.block.Add(&ir.StoreInst{ - Pointer: pointer.v, + Pointer: pointer, Value: initializer, - Align: stmt.ActualType.Align(), + Align: abi.GetTargetAbi().Align(stmt.ActualType), }) c.setLocationMeta(store, stmt) @@ -170,12 +173,22 @@ func (c *codegen) VisitReturn(stmt *ast.Return) { ) } else { // Other - value := c.implicitCastLoadExpr(c.astFunction.Returns, stmt.Value) + funcAbi := abi.GetFuncAbi(c.astFunction) - c.setLocationMeta( - c.block.Add(&ir.RetInst{Value: value.v}), - stmt, - ) + value := c.implicitCastLoadExpr(c.astFunction.Returns, stmt.Value) + value = c.valueToReturnValue(funcAbi, value, c.astFunction.Returns, c.function.Typ.Params) + + if value.v == nil { + c.setLocationMeta( + c.block.Add(&ir.RetInst{}), + stmt, + ) + } else { + c.setLocationMeta( + c.block.Add(&ir.RetInst{Value: value.v}), + stmt, + ) + } } c.block = nil diff --git a/core/codegen/types.go b/core/codegen/types.go index 1c6fd4b..1d31ce2 100644 --- a/core/codegen/types.go +++ b/core/codegen/types.go @@ -1,10 +1,11 @@ package codegen import ( - "fireball/core/architecture" + "fireball/core/abi" "fireball/core/ast" "fireball/core/ir" "fmt" + "strconv" ) type cachedType struct { @@ -18,7 +19,7 @@ type cachedTypeMeta struct { } type types struct { - module *ir.Module + c *codegen interfaceType ir.Type types []cachedType @@ -86,7 +87,7 @@ func (t *types) get(type_ ast.Type) ir.Type { Fields: fields, } - t.module.Struct(typ) + t.c.module.Struct(typ) return t.cacheType(type_, typ) case *ast.Enum: @@ -105,7 +106,7 @@ func (t *types) get(type_ ast.Type) ir.Type { }, } - t.module.Struct(typ) + t.c.module.Struct(typ) t.interfaceType = typ } @@ -116,74 +117,147 @@ func (t *types) get(type_ ast.Type) ir.Type { return typ } - // Intrinsic - intrinsicName := type_.IntrinsicName() + typ := t.createFuncType(type_) + return t.cacheType(type_, typ) - if intrinsicName != "" { - intrinsic := t.getIntrinsic(type_, intrinsicName) + default: + panic("codegen.types.get() - Not implemented") + } +} - params := make([]*ir.Param, len(intrinsic[1:])) +func (t *types) createFuncType(f *ast.Func) *ir.FuncType { + // Intrinsic + if f.IntrinsicName() != "" { + return t.createIntrinsicFuncType(f) + } - for i, param := range intrinsic[1:] { - params[i] = &ir.Param{ - Typ: param, - Name_: fmt.Sprintf("_%d", i), - } - } + // Normal + this := f.Method() - typ := &ir.FuncType{ - Returns: intrinsic[0], - Params: params, - } + parameterCount := len(f.Params) + if this != nil { + parameterCount++ + } - return t.cacheType(type_, typ) - } + params := make([]*ir.Param, 0, parameterCount) - // Normal - this := type_.Method() + funcAbi := abi.GetFuncAbi(f) + returnArgs := funcAbi.Classify(f.Returns, nil) - parameterCount := len(type_.Params) - if this != nil { - parameterCount++ - } + if len(returnArgs) == 1 && returnArgs[0].Class == abi.Memory { + params = append(params, &ir.Param{ + Typ: &ir.PointerType{ + Pointee: ir.Void, + SRet: t.get(f.Returns), + }, + Name_: "__return", + }) - params := make([]*ir.Param, parameterCount) + returnArgs = nil + } - if this != nil { - type_ := ast.Pointer{Pointee: this} + if this != nil { + type_ := ast.Pointer{Pointee: this} - params[0] = &ir.Param{ - Typ: t.get(&type_), - Name_: "this", - } + params = append(params, &ir.Param{ + Typ: t.get(&type_), + Name_: "this", + }) + } + + for _, param := range f.Params { + args := funcAbi.Classify(param.Type, nil) + if len(args) == 0 { + panic("codegen.types.createFuncType() - Failed to classify parameter type") } - for index, param := range type_.Params { - i := index - if this != nil { - i++ - } + for i, arg := range args { + typ := t.getAbiArgType(arg, param.Type) + + if typ != nil { + name := param.Name.String() + + if len(args) > 1 { + name += "." + strconv.Itoa(i) + } - params[i] = &ir.Param{ - Typ: t.get(param.Type), - Name_: param.Name.String(), + params = append(params, &ir.Param{ + Typ: typ, + Name_: name, + }) } } + } + + return &ir.FuncType{ + Returns: t.createReturnType(returnArgs, f.Returns), + Params: params, + Variadic: f.IsVariadic(), + } +} + +func (t *types) createReturnType(args []abi.Arg, type_ ast.Type) ir.Type { + // Void + if len(args) == 0 { + return ir.Void + } + + // Struct + if typeIsAbiStruct(type_) { + return getAbiStructType(args, true) + } - typ := &ir.FuncType{ - Returns: t.get(type_.Returns), - Params: params, - Variadic: type_.IsVariadic(), + // Single + return t.getAbiArgType(args[0], type_) +} + +func (t *types) getAbiArgType(arg abi.Arg, type_ ast.Type) ir.Type { + switch arg.Class { + case abi.Integer: + if _, ok := ast.As[*ast.Pointer](type_); ok { + void := ast.Primitive{Kind: ast.Void} + ptr := ast.Pointer{Pointee: &void} + + return t.get(&ptr) + } else { + return getAbiIntIrType(arg) } - return t.cacheType(type_, typ) + case abi.SSE: + return getAbiSseIrType(arg) + + case abi.Memory: + return &ir.PointerType{ + Pointee: ir.Void, + ByVal: t.get(type_), + } default: - panic("codegen.types.get() - Not implemented") + panic("codegen.types.getAbiArgType() - ABI class not implemented") + } +} + +func (t *types) createIntrinsicFuncType(f *ast.Func) *ir.FuncType { + intrinsicName := f.IntrinsicName() + + intrinsic := t.getIntrinsicTypes(f, 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), + } + } + + return &ir.FuncType{ + Returns: intrinsic[0], + Params: params, } } -func (t *types) getIntrinsic(function *ast.Func, intrinsicName string) []ir.Type { +func (t *types) getIntrinsicTypes(function *ast.Func, intrinsicName string) []ir.Type { param := t.get(function.Params[0].Type) switch intrinsicName { @@ -261,23 +335,23 @@ func (t *types) getMeta(type_ ast.Type) ir.MetaID { void := ast.Primitive{Kind: ast.Void} ptr := ast.Pointer{Pointee: &void} - t.interfaceMetadata = t.module.Meta(&ir.CompositeTypeMeta{ + t.interfaceMetadata = t.c.module.Meta(&ir.CompositeTypeMeta{ Tag: ir.StructureTypeTag, Name: "__interface", - Size: type_.Size() * 8, - Align: type_.Align() * 8, + Size: abi.GetTargetAbi().Size(type_) * 8, + Align: abi.GetTargetAbi().Align(type_) * 8, Elements: []ir.MetaID{ - t.module.Meta(&ir.DerivedTypeMeta{ + t.c.module.Meta(&ir.DerivedTypeMeta{ Tag: ir.MemberTag, Name: "vtable", BaseType: t.getMeta(&ptr), Offset: 0, }), - t.module.Meta(&ir.DerivedTypeMeta{ + t.c.module.Meta(&ir.DerivedTypeMeta{ Tag: ir.MemberTag, Name: "data", BaseType: t.getMeta(&ptr), - Offset: ptr.Size() * 8, + Offset: abi.GetTargetAbi().Size(&ptr) * 8, }), }, }) @@ -301,8 +375,8 @@ func (t *types) getMeta(type_ ast.Type) ir.MetaID { } typ := &ir.BasicTypeMeta{ - Size: type_.Size() * 8, - Align: type_.Align() * 8, + Size: abi.GetTargetAbi().Size(type_) * 8, + Align: abi.GetTargetAbi().Align(type_) * 8, } switch type_.Kind { @@ -353,8 +427,8 @@ func (t *types) getMeta(type_ ast.Type) ir.MetaID { typ := &ir.DerivedTypeMeta{ Tag: ir.PointerTypeTag, BaseType: t.getMeta(type_.Pointee), - Size: type_.Size() * 8, - Align: type_.Align() * 8, + Size: abi.GetTargetAbi().Size(type_) * 8, + Align: abi.GetTargetAbi().Align(type_) * 8, } return t.cacheMeta(type_, typ) @@ -362,10 +436,10 @@ func (t *types) getMeta(type_ ast.Type) ir.MetaID { case *ast.Array: typ := &ir.CompositeTypeMeta{ Tag: ir.ArrayTypeTag, - Size: type_.Size() * 8, - Align: type_.Align() * 8, + Size: abi.GetTargetAbi().Size(type_) * 8, + Align: abi.GetTargetAbi().Align(type_) * 8, BaseType: t.getMeta(type_.Base), - Elements: []ir.MetaID{t.module.Meta(&ir.SubrangeMeta{ + Elements: []ir.MetaID{t.c.module.Meta(&ir.SubrangeMeta{ LowerBound: 0, Count: type_.Count, })}, @@ -374,26 +448,24 @@ func (t *types) getMeta(type_ ast.Type) ir.MetaID { 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, offsets := abi.GetTargetAbi().Fields(type_) + fieldsMeta := make([]ir.MetaID, len(fields)) - fields[i] = t.module.Meta(&ir.DerivedTypeMeta{ + for i, field := range fields { + fieldsMeta[i] = t.c.module.Meta(&ir.DerivedTypeMeta{ Tag: ir.MemberTag, Name: field.Name.String(), BaseType: t.getMeta(field.Type), - Offset: offset * 8, + Offset: offsets[i] * 8, }) } typ := &ir.CompositeTypeMeta{ Tag: ir.StructureTypeTag, Name: type_.Name.String(), - Size: layout.Size() * 8, - Align: type_.Align() * 8, - Elements: fields, + Size: abi.GetTargetAbi().Size(type_) * 8, + Align: abi.GetTargetAbi().Align(type_) * 8, + Elements: fieldsMeta, } return t.cacheMeta(type_, typ) @@ -402,7 +474,7 @@ func (t *types) getMeta(type_ ast.Type) ir.MetaID { cases := make([]ir.MetaID, len(type_.Cases)) for i, case_ := range type_.Cases { - cases[i] = t.module.Meta(&ir.EnumeratorMeta{ + cases[i] = t.c.module.Meta(&ir.EnumeratorMeta{ Name: case_.Name.String(), Value: ir.Signed(case_.ActualValue), }) @@ -411,8 +483,8 @@ func (t *types) getMeta(type_ ast.Type) ir.MetaID { typ := &ir.CompositeTypeMeta{ Tag: ir.EnumerationTypeTag, Name: type_.Name.String(), - Size: type_.Size() * 8, - Align: type_.Align() * 8, + Size: abi.GetTargetAbi().Size(type_) * 8, + Align: abi.GetTargetAbi().Align(type_) * 8, BaseType: t.getMeta(type_.ActualType), Elements: cases, } @@ -456,7 +528,7 @@ func (t *types) getMeta(type_ ast.Type) ir.MetaID { } func (t *types) cacheMeta(type_ ast.Type, meta ir.Meta) ir.MetaID { - id := t.module.Meta(meta) + id := t.c.module.Meta(meta) t.metadata = append(t.metadata, cachedTypeMeta{ type_: type_, diff --git a/core/common/casts.go b/core/common/casts.go new file mode 100644 index 0000000..0434b1c --- /dev/null +++ b/core/common/casts.go @@ -0,0 +1,160 @@ +package common + +import ( + "fireball/core/abi" + "fireball/core/ast" +) + +type CastKind uint8 + +const ( + None CastKind = iota + + Truncate + Extend + + Int2Float + Float2Int + + Pointer2Interface +) + +func GetCast(from, to ast.Type) (CastKind, bool) { + if from.Equals(to) { + return None, true + } + + switch from := from.Resolved().(type) { + // Primitive -> ... + case *ast.Primitive: + switch to := to.Resolved().(type) { + // Primitive -> Primitive + case *ast.Primitive: + if (ast.IsInteger(from.Kind) && ast.IsInteger(to.Kind)) || (ast.IsFloating(from.Kind) && ast.IsFloating(to.Kind)) { + fromSize := abi.GetTargetAbi().Size(from) + toSize := abi.GetTargetAbi().Size(to) + + if toSize > fromSize { + return Extend, true + } else if toSize < fromSize { + return Truncate, true + } else { + return None, true + } + } else if ast.IsInteger(from.Kind) && ast.IsFloating(to.Kind) { + return Int2Float, true + } else if ast.IsFloating(from.Kind) && ast.IsInteger(to.Kind) { + return Float2Int, true + } + + // Primitive (Integer) -> Enum + case *ast.Enum: + if ast.IsInteger(from.Kind) { + fromSize := abi.GetTargetAbi().Size(from) + toSize := abi.GetTargetAbi().Size(to) + + if toSize > fromSize { + return Extend, true + } else if toSize < fromSize { + return Truncate, true + } else { + return None, true + } + } + } + + // Pointer -> Interface, Pointer, Func + case *ast.Pointer: + switch to := to.Resolved().(type) { + case *ast.Interface: + if implements(from, to) { + return Pointer2Interface, true + } + + case *ast.Pointer, *ast.Func: + return None, true + } + + // Enum -> Primitive (Integer) + case *ast.Enum: + if to, ok := ast.As[*ast.Primitive](to); ok && ast.IsInteger(to.Kind) { + fromSize := abi.GetTargetAbi().Size(from) + toSize := abi.GetTargetAbi().Size(to) + + if toSize > fromSize { + return Extend, true + } else if toSize < fromSize { + return Truncate, true + } else { + return None, true + } + } + } + + return None, false +} + +func GetImplicitCast(from, to ast.Type) (CastKind, bool) { + if from.Equals(to) { + return None, true + } + + switch from := from.Resolved().(type) { + // Primitive -> Primitive + case *ast.Primitive: + if to, ok := ast.As[*ast.Primitive](to); ok { + fromSize := abi.GetTargetAbi().Size(from) + toSize := abi.GetTargetAbi().Size(to) + + // Primitive (smaller integer / floating) -> Primitive (bigger integer / floating) + if ((ast.IsInteger(from.Kind) && ast.IsInteger(to.Kind)) || (ast.IsFloating(from.Kind) && ast.IsFloating(to.Kind))) && toSize > fromSize { + return Extend, true + } + + // Primitive (same integer) -> Primitive (same floating) + // TODO: Allow converting a smaller integer to the next bigger floating (eg. i16 -> f32) + if ast.IsInteger(from.Kind) && ast.IsFloating(to.Kind) && toSize == fromSize { + return Int2Float, true + } + } + + // Pointer -> Interface, Pointer (*void) + case *ast.Pointer: + switch to := to.Resolved().(type) { + case *ast.Interface: + if implements(from, to) { + return Pointer2Interface, true + } + + case *ast.Pointer: + if ast.IsPrimitive(to.Pointee, ast.Void) { + return None, true + } + } + } + + return None, false +} + +func implements(type_ ast.Type, inter *ast.Interface) bool { + // Check struct pointee + if pointer, ok := type_.(*ast.Pointer); ok { + if s, ok := ast.As[*ast.Struct](pointer.Pointee); ok { + type_ = s + } else { + return false + } + } else { + return false + } + + // Get resolver + resolver := ast.GetParent[*ast.File](type_).Resolver + + // Check impl + if resolver.GetImpl(type_, inter) != nil { + return true + } + + return false +} diff --git a/core/ir/block.go b/core/ir/block.go index 9318115..5c05921 100644 --- a/core/ir/block.go +++ b/core/ir/block.go @@ -1,5 +1,7 @@ package ir +import "slices" + type Block struct { name string @@ -11,6 +13,16 @@ func (b *Block) Add(inst Inst) Inst { return inst } +func (b *Block) Insert(index int, inst Inst) Inst { + if len(b.Instructions) == 0 { + b.Instructions = append(b.Instructions, inst) + return inst + } + + b.Instructions = slices.Insert(b.Instructions, index, inst) + return inst +} + // Value func (b *Block) Type() Type { diff --git a/core/ir/types.go b/core/ir/types.go index 2fbe664..f36c53e 100644 --- a/core/ir/types.go +++ b/core/ir/types.go @@ -1,7 +1,11 @@ package ir +import "slices" + type Type interface { isType() + + Equals(other Type) bool } // Void @@ -12,6 +16,14 @@ type VoidType struct{} func (v *VoidType) isType() {} +func (v *VoidType) Equals(other Type) bool { + if _, ok := other.(*VoidType); ok { + return true + } + + return false +} + // Int var I1 = &IntType{BitSize: 1} @@ -26,6 +38,14 @@ type IntType struct { func (i *IntType) isType() {} +func (i *IntType) Equals(other Type) bool { + if other, ok := other.(*IntType); ok { + return i.BitSize == other.BitSize + } + + return false +} + // Float var F32 = &FloatType{BitSize: 32} @@ -37,14 +57,33 @@ type FloatType struct { func (f *FloatType) isType() {} +func (f *FloatType) Equals(other Type) bool { + if other, ok := other.(*FloatType); ok { + return f.BitSize == other.BitSize + } + + return false +} + // Pointer type PointerType struct { Pointee Type + + ByVal Type + SRet Type } func (p *PointerType) isType() {} +func (p *PointerType) Equals(other Type) bool { + if other, ok := other.(*PointerType); ok { + return p.Pointee.Equals(other.Pointee) + } + + return false +} + // Array type ArrayType struct { @@ -54,6 +93,14 @@ type ArrayType struct { func (a *ArrayType) isType() {} +func (a *ArrayType) Equals(other Type) bool { + if other, ok := other.(*ArrayType); ok { + return a.Count == other.Count && a.Base.Equals(other.Base) + } + + return false +} + // Struct type StructType struct { @@ -63,6 +110,16 @@ type StructType struct { func (s *StructType) isType() {} +func (s *StructType) Equals(other Type) bool { + if other, ok := other.(*StructType); ok { + return slices.EqualFunc(s.Fields, other.Fields, func(t Type, t2 Type) bool { + return t.Equals(t2) + }) + } + + return false +} + // Function type Param struct { @@ -90,6 +147,16 @@ type FuncType struct { func (f *FuncType) isType() {} +func (f *FuncType) Equals(other Type) bool { + if other, ok := other.(*FuncType); ok { + return f.Returns.Equals(other.Returns) && f.Variadic == other.Variadic && slices.EqualFunc(f.Params, other.Params, func(param *Param, param2 *Param) bool { + return param.Typ.Equals(param2.Typ) + }) + } + + return false +} + // Meta var MetaT = &MetaType{} @@ -97,3 +164,11 @@ var MetaT = &MetaType{} type MetaType struct{} func (m *MetaType) isType() {} + +func (m *MetaType) Equals(other Type) bool { + if _, ok := other.(*MetaType); ok { + return true + } + + return false +} diff --git a/core/llvm/function.go b/core/llvm/function.go index 7eb5673..d3152be 100644 --- a/core/llvm/function.go +++ b/core/llvm/function.go @@ -74,6 +74,7 @@ func (w *textWriter) writeFunction(function *ir.Func) { // Parameters w.writeRune('(') + w.isArgument = true for i, param := range function.Typ.Params { if i > 0 { @@ -93,6 +94,7 @@ func (w *textWriter) writeFunction(function *ir.Func) { } } + w.isArgument = false w.writeRune(')') // Flags diff --git a/core/llvm/instructions.go b/core/llvm/instructions.go index 73d4848..4e46830 100644 --- a/core/llvm/instructions.go +++ b/core/llvm/instructions.go @@ -1,6 +1,9 @@ package llvm -import "fireball/core/ir" +import ( + "fireball/core/ir" + "strings" +) func (w *textWriter) writeInstruction(inst ir.Inst) { // Value @@ -111,9 +114,9 @@ func (w *textWriter) writeInstruction(inst ir.Inst) { case *ir.ShrInst: if inst.SignExtend { - w.writeString("ashr") + w.writeString("ashr ") } else { - w.writeString("lshr") + w.writeString("lshr ") } w.writeValue(inst.Left) @@ -374,6 +377,10 @@ func (w *textWriter) writeInstruction(inst ir.Inst) { w.writeName(inst.Callee) w.writeRune('(') + if !strings.HasPrefix(inst.Callee.Name(), "llvm.dbg") { + w.isArgument = true + } + for i, arg := range inst.Args { if i > 0 { w.writeString(", ") @@ -392,6 +399,8 @@ func (w *textWriter) writeInstruction(inst ir.Inst) { w.writeValue(arg) } + w.isArgument = false + w.writeRune(')') default: diff --git a/core/llvm/types.go b/core/llvm/types.go index d64aa00..c08e724 100644 --- a/core/llvm/types.go +++ b/core/llvm/types.go @@ -46,6 +46,16 @@ func (w *textWriter) writeType(t ir.Type) { case *ir.PointerType: w.writeString("ptr") + if t.ByVal != nil && w.isArgument { + w.writeString(" byval(") + w.writeType(t.ByVal) + w.writeRune(')') + } else if t.SRet != nil && w.isArgument { + w.writeString(" sret(") + w.writeType(t.SRet) + w.writeRune(')') + } + case *ir.ArrayType: w.writeRune('[') w.writeInt(int64(t.Count)) diff --git a/core/llvm/value.go b/core/llvm/value.go index 4d8058a..bf8b96e 100644 --- a/core/llvm/value.go +++ b/core/llvm/value.go @@ -149,6 +149,7 @@ func (w *textWriter) writeNameImpl(name Name) { w.writeString(name.named) if name.namedSuffix > 0 { + w.writeRune('.') w.writeUint(name.namedSuffix, 10) } } diff --git a/core/llvm/writer.go b/core/llvm/writer.go index a2d375c..ee9d7bc 100644 --- a/core/llvm/writer.go +++ b/core/llvm/writer.go @@ -14,6 +14,8 @@ type textWriter struct { w *bufio.Writer buffer [65]byte + isArgument bool + // Names globalUnnamedCount int64 diff --git a/gen/ast.go b/gen/ast.go index 2dd3487..54394ac 100644 --- a/gen/ast.go +++ b/gen/ast.go @@ -141,7 +141,7 @@ var statements = Group{ field("increment", type_("Expr")), field("body", type_("Stmt")), ), - node( + nodeAllowEmpty( "Return", field("value", type_("Expr")), ), @@ -249,7 +249,7 @@ var other = Group{ name: "", nodes: []Node{ - node( + nodeAllowEmpty( "File", field("path", type_("string")), field("namespace", type_("Namespace")), diff --git a/gen/main.go b/gen/main.go index 28c8a03..6831e5f 100644 --- a/gen/main.go +++ b/gen/main.go @@ -70,9 +70,6 @@ func genVisitor(w *Writer, group Group) { w.write("") if group.name == "Type" { - w.write("Size() uint32") - w.write("Align() uint32") - w.write("") w.write("Equals(other Type) bool") w.write("") w.write("Resolved() Type") @@ -313,7 +310,7 @@ func genConstructor(w *Writer, node Node, this string) { // Check if the node will be empty and return nil - if node.name != "File" { + if !node.allowEmpty { sb.Reset() i := 0 diff --git a/gen/uhh.go b/gen/uhh.go index 97c88fe..ffbb94d 100644 --- a/gen/uhh.go +++ b/gen/uhh.go @@ -11,14 +11,19 @@ type Group struct { } type Node struct { - name string - fields []Field + name string + fields []Field + allowEmpty bool } func node(name string, fields ...Field) Node { return Node{name: name, fields: fields} } +func nodeAllowEmpty(name string, fields ...Field) Node { + return Node{name: name, fields: fields, allowEmpty: true} +} + func (n *Node) tokenField() *Field { for i := 0; i < len(n.fields); i++ { field := &n.fields[i] diff --git a/tests/src/abi.fb b/tests/src/abi.fb new file mode 100644 index 0000000..37ab737 --- /dev/null +++ b/tests/src/abi.fb @@ -0,0 +1,130 @@ +namespace Tests.Abi; + +struct Color { + r u8, + g u8, + b u8, + a u8, +} + +struct Registers { + a i32, + b i8, + c i64, +} + +struct Memory { + a i32, + b i64, + c f64, +} + + + +func checkColor(color Color, r i32, g i32, b i32, a i32) bool { + return color.r == r && color.g == g && color.b == b && color.a == a; +} + +func checkPointer(pointer *void) bool { + return pointer == nil; +} + +func checkRegisters(reg Registers, a i32, b i8, c i64) bool { + return reg.a == a && reg.b == b && reg.c == c; +} + +func checkMemory(mem Memory, a i32, b i64, c f64) bool { + return mem.a == a && mem.b == b && mem.c == c; +} + + + +func getColor(r i32, g i32, b i32, a i32) Color { + return Color { r: r as u8, g: g as u8, b: b as u8, a: a as u8 }; +} + +func getPointer() *void { + return nil; +} + +func getRegisters(a i32, b i8, c i64) Registers { + return Registers { a: a, b: b, c: c }; +} + +func getMemory(a i32, b i64, c f64) Memory { + return Memory { a: a, b: b, c: c }; +} + + + +#[Test] +func passColor() bool { + var color = Color { r: 1 as u8, g: 2 as u8, b: 3 as u8, a: 4 as u8 }; + return checkColor(color, 1, 2, 3, 4); +} + +#[Test] +func passPointer() bool { + var pointer = nil; + return checkPointer(pointer); +} + +#[Test] +func passRegisters() bool { + var reg = Registers { a: 2, b: 5 as i8, c: 9 }; + return checkRegisters(reg, 2, 5 as i8, 9); +} + +#[Test] +func passMemory() bool { + var mem = Memory { a: 2, b: 5, c: 9.0 }; + return checkMemory(mem, 2, 5, 9.0); +} + + + +#[Test] +func returnColor() bool { + var color = getColor(1, 2, 3, 4); + return color.r == 1 && color.g == 2 && color.b == 3 && color.a == 4; +} + +#[Test] +func returnPointer() bool { + var pointer = getPointer(); + return pointer == nil; +} + +#[Test] +func returnRegisters() bool { + var reg = getRegisters(2, 5 as i8, 9); + return reg.a == 2 && reg.b == 5 && reg.c == 9; +} + +#[Test] +func returnMemory() bool { + var mem = getMemory(2, 5, 9.0); + return mem.a == 2 && mem.b == 5 && mem.c == 9.0; +} + + + +#[Test] +func returnColorDirect() bool { + return getColor(1, 2, 3, 4).g == 2; +} + +#[Test] +func returnPointerDirect() bool { + return getPointer() == nil; +} + +#[Test] +func returnRegistersDirect() bool { + return getRegisters(2, 5 as i8, 9).c == 9; +} + +#[Test] +func returnMemoryDirect() bool { + return getMemory(2, 5, 9.0).a == 2; +} diff --git a/tests/src/statements.fb b/tests/src/statements.fb index b14dbce..21ee0cd 100644 --- a/tests/src/statements.fb +++ b/tests/src/statements.fb @@ -113,3 +113,19 @@ func _continue() bool { return count == 4; } + +#[Test("return")] +func _return() bool { + var ok = true; + setFalse(&ok); + + return ok; +} + +func setFalse(ptr *bool) { + if (true) { + return; + } + + *ptr = false; +} diff --git a/vscode/src/extension.ts b/vscode/src/extension.ts index 647e2b1..eb3d9c3 100644 --- a/vscode/src/extension.ts +++ b/vscode/src/extension.ts @@ -9,7 +9,7 @@ import { } from "vscode-languageclient/node"; import {ChildProcess, exec} from "child_process"; -const DEV = false; +const DEV = true; let process: ChildProcess; let client: LanguageClient;