Skip to content

Commit

Permalink
CORE: Add abstraction for different ABIs
Browse files Browse the repository at this point in the history
CORE: Add the System V AMD64 ABI
  • Loading branch information
MineGame159 committed Feb 3, 2024
1 parent 792b674 commit 96d2714
Show file tree
Hide file tree
Showing 38 changed files with 1,560 additions and 516 deletions.
1 change: 0 additions & 1 deletion cmd/build/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ func getLinker() string {
switch runtime.GOOS {
case "linux":
return "ld.lld"

case "darwin":
return "ld"

Expand Down
5 changes: 3 additions & 2 deletions cmd/lsp/hover.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package lsp

import (
"fireball/core"
"fireball/core/abi"
"fireball/core/ast"
"github.com/MineGame159/protocol"
"strconv"
Expand Down Expand Up @@ -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))
Expand Down
59 changes: 59 additions & 0 deletions core/abi/abi.go
Original file line number Diff line number Diff line change
@@ -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*/
}
168 changes: 168 additions & 0 deletions core/abi/amd64.go
Original file line number Diff line number Diff line change
@@ -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
}
}
23 changes: 23 additions & 0 deletions core/abi/c_layout.go
Original file line number Diff line number Diff line change
@@ -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)
}
43 changes: 43 additions & 0 deletions core/abi/fb.go
Original file line number Diff line number Diff line change
@@ -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")
}
}
44 changes: 44 additions & 0 deletions core/abi/utils.go
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 96d2714

Please sign in to comment.