This commit is contained in:
parent
c9c6b94c18
commit
3301cf5542
49 changed files with 690 additions and 262 deletions
|
@ -45,4 +45,5 @@ var CPU = cpu.CPU{
|
|||
Call: []cpu.Register{X0, X1, X2, X3, X4, X5, X6},
|
||||
Syscall: []cpu.Register{X8, X0, X1, X2, X3, X4, X5},
|
||||
ExternCall: []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7},
|
||||
Return: []cpu.Register{X0, X1, X2},
|
||||
}
|
|
@ -7,4 +7,18 @@ const (
|
|||
UnknownArch Arch = iota
|
||||
ARM
|
||||
X86
|
||||
)
|
||||
)
|
||||
|
||||
// SetArch sets the architecture which also influences the default alignment.
|
||||
func (build *Build) SetArch(arch Arch) {
|
||||
build.Arch = arch
|
||||
|
||||
switch arch {
|
||||
case ARM:
|
||||
build.MemoryAlign = 0x4000
|
||||
default:
|
||||
build.MemoryAlign = 0x1000
|
||||
}
|
||||
|
||||
build.FileAlign = build.MemoryAlign
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
package build
|
||||
|
||||
// SetArch sets the architecture which also influences the default alignment.
|
||||
func (build *Build) SetArch(arch Arch) {
|
||||
build.Arch = arch
|
||||
|
||||
switch arch {
|
||||
case ARM:
|
||||
build.MemoryAlign = 0x4000
|
||||
default:
|
||||
build.MemoryAlign = 0x1000
|
||||
}
|
||||
|
||||
build.FileAlign = build.MemoryAlign
|
||||
}
|
|
@ -5,7 +5,6 @@ Usage:
|
|||
Commands:
|
||||
|
||||
build [directory | file] build an executable
|
||||
|
||||
--verbose, -v show everything
|
||||
|
||||
run [directory | file] build and run the executable
|
||||
|
|
|
@ -2,6 +2,7 @@ package compiler
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"git.urbach.dev/cli/q/src/core"
|
||||
"git.urbach.dev/go/color/ansi"
|
||||
|
@ -10,18 +11,27 @@ import (
|
|||
// showSSA shows the SSA IR.
|
||||
func showSSA(root *core.Function) {
|
||||
root.EachDependency(make(map[*core.Function]bool), func(f *core.Function) {
|
||||
ansi.Yellow.Printf("%s:\n", f.UniqueName)
|
||||
fmt.Print("# ")
|
||||
ansi.Green.Print(f.UniqueName)
|
||||
fmt.Print("\n\n")
|
||||
|
||||
for i, block := range f.Blocks {
|
||||
if i != 0 {
|
||||
fmt.Println("---")
|
||||
}
|
||||
for _, block := range f.Blocks {
|
||||
ansi.Dim.Printf("| %-3s | %-30s | %-30s | %-4s |\n", "ID", "Raw", "Type", "Uses")
|
||||
ansi.Dim.Printf("| %s | %s | %s | %s |\n", strings.Repeat("-", 3), strings.Repeat("-", 30), strings.Repeat("-", 30), strings.Repeat("-", 4))
|
||||
|
||||
for i, instr := range block.Instructions {
|
||||
ansi.Dim.Printf("%-4d", i)
|
||||
fmt.Printf("%-40s", instr.String())
|
||||
ansi.Cyan.Printf("%-30s", instr.Type().Name())
|
||||
ansi.Dim.Printf("%s\n", f.File.Bytes[instr.Start():instr.End()])
|
||||
ansi.Dim.Printf("| %%%-2d | ", i)
|
||||
|
||||
if instr.IsConst() {
|
||||
fmt.Printf("%-30s ", instr.Debug())
|
||||
} else {
|
||||
ansi.Yellow.Printf("%-30s ", instr.Debug())
|
||||
}
|
||||
|
||||
ansi.Dim.Print("|")
|
||||
ansi.Dim.Printf(" %-30s |", instr.Type().Name())
|
||||
ansi.Dim.Printf(" %-4d |", instr.CountUsers())
|
||||
fmt.Println()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import (
|
|||
// CheckDeadCode checks for dead values.
|
||||
func (f *Function) CheckDeadCode() error {
|
||||
for instr := range f.Values {
|
||||
if instr.IsConst() && instr.Alive() == 0 {
|
||||
if instr.IsConst() && instr.CountUsers() == 0 {
|
||||
return errors.New(&UnusedValue{Value: instr.String()}, f.File, instr.Start())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,48 +1,24 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
import "git.urbach.dev/cli/q/src/types"
|
||||
|
||||
// Compile turns a function into machine code.
|
||||
func (f *Function) Compile() {
|
||||
extra := 0
|
||||
offset := 0
|
||||
|
||||
for i, input := range f.Input {
|
||||
if input.Name == "_" {
|
||||
continue
|
||||
}
|
||||
|
||||
array, isArray := input.Typ.(*types.Array)
|
||||
|
||||
if isArray {
|
||||
pointer := &ssa.Parameter{
|
||||
Index: uint8(i + extra),
|
||||
Name: input.Name,
|
||||
Typ: &types.Pointer{To: array.Of},
|
||||
Source: input.Source,
|
||||
}
|
||||
|
||||
f.Append(pointer)
|
||||
f.Identifiers[pointer.Name] = pointer
|
||||
extra++
|
||||
|
||||
length := &ssa.Parameter{
|
||||
Index: uint8(i + extra),
|
||||
Name: input.Name + ".len",
|
||||
Typ: types.AnyInt,
|
||||
Source: input.Source,
|
||||
}
|
||||
|
||||
f.Append(length)
|
||||
f.Identifiers[length.Name] = length
|
||||
continue
|
||||
}
|
||||
|
||||
input.Index = uint8(i + extra)
|
||||
input.Index = uint8(offset + i)
|
||||
f.Append(input)
|
||||
f.Identifiers[input.Name] = input
|
||||
structure, isStruct := input.Typ.(*types.Struct)
|
||||
|
||||
if isStruct {
|
||||
offset += len(structure.Fields) - 1
|
||||
}
|
||||
}
|
||||
|
||||
for instr := range f.Body.Instructions {
|
||||
|
@ -59,5 +35,5 @@ func (f *Function) Compile() {
|
|||
return
|
||||
}
|
||||
|
||||
f.ssaToAsm()
|
||||
f.GenerateAssembly(f.IR, f.IsLeaf())
|
||||
}
|
|
@ -27,8 +27,8 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
}
|
||||
|
||||
f.Dependencies.Add(function)
|
||||
v := f.AppendFunction(function.UniqueName, function.Type)
|
||||
v.Source = ssa.Source(expr.Source)
|
||||
v := f.AppendFunction(function.UniqueName, function.Type, function.IsExtern())
|
||||
v.SetSource(expr.Source)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
@ -42,14 +42,25 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
}
|
||||
|
||||
v := f.AppendInt(number)
|
||||
v.Source = ssa.Source(expr.Source)
|
||||
v.SetSource(expr.Source)
|
||||
return v, nil
|
||||
|
||||
case token.String:
|
||||
data := expr.Token.Bytes(f.File.Bytes)
|
||||
data = Unescape(data)
|
||||
v := f.AppendBytes(data)
|
||||
v.Source = ssa.Source(expr.Source)
|
||||
|
||||
length := f.AppendInt(len(data))
|
||||
length.SetSource(expr.Source)
|
||||
|
||||
pointer := f.AppendBytes(data)
|
||||
pointer.SetSource(expr.Source)
|
||||
|
||||
v := f.Append(&ssa.Struct{
|
||||
Arguments: []ssa.Value{pointer, length},
|
||||
Typ: types.String,
|
||||
})
|
||||
|
||||
v.SetSource(expr.Source)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
|
@ -109,7 +120,7 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
return nil, errors.New(&ParameterCountMismatch{Function: name, Count: len(parameters), ExpectedCount: len(fn.Input)}, f.File, expr.Source[0].Position)
|
||||
}
|
||||
|
||||
for i, param := range parameters {
|
||||
for i, param := range slices.Backward(parameters) {
|
||||
if !types.Is(param.Type(), fn.Input[i].Typ) {
|
||||
return nil, errors.New(&TypeMismatch{
|
||||
Encountered: param.Type().Name(),
|
||||
|
@ -130,22 +141,39 @@ func (f *Function) Evaluate(expr *expression.Expression) (ssa.Value, error) {
|
|||
case token.Dot:
|
||||
left := expr.Children[0]
|
||||
right := expr.Children[1]
|
||||
label := fmt.Sprintf("%s.%s", left.String(f.File.Bytes), right.String(f.File.Bytes))
|
||||
leftText := left.String(f.File.Bytes)
|
||||
rightText := right.String(f.File.Bytes)
|
||||
identifier, exists := f.Identifiers[leftText]
|
||||
|
||||
if exists {
|
||||
structure := identifier.Type().(*types.Struct)
|
||||
field := structure.FieldByName(rightText)
|
||||
|
||||
if field == nil {
|
||||
return nil, errors.New(&UnknownStructField{StructName: structure.Name(), FieldName: rightText}, f.File, right.Token.Position)
|
||||
}
|
||||
|
||||
v := f.Append(&ssa.StructField{Struct: identifier, Field: field})
|
||||
v.SetSource(expr.Source)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
label := fmt.Sprintf("%s.%s", leftText, rightText)
|
||||
function, exists := f.All.Functions[label]
|
||||
|
||||
if !exists {
|
||||
return nil, errors.New(&UnknownIdentifier{Name: label}, f.File, left.Token.Position)
|
||||
if exists {
|
||||
if function.IsExtern() {
|
||||
f.Assembler.Libraries = f.Assembler.Libraries.Append(function.Package, function.Name)
|
||||
} else {
|
||||
f.Dependencies.Add(function)
|
||||
}
|
||||
|
||||
v := f.AppendFunction(function.UniqueName, function.Type, function.IsExtern())
|
||||
v.SetSource(expr.Source)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
if function.IsExtern() {
|
||||
f.Assembler.Libraries = f.Assembler.Libraries.Append(function.Package, function.Name)
|
||||
} else {
|
||||
f.Dependencies.Add(function)
|
||||
}
|
||||
|
||||
v := f.AppendFunction(function.UniqueName, function.Type)
|
||||
v.Source = ssa.Source(expr.Source)
|
||||
return v, nil
|
||||
return nil, errors.New(&UnknownIdentifier{Name: label}, f.File, left.Token.Position)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
|
|
@ -4,10 +4,10 @@ import (
|
|||
"fmt"
|
||||
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
"git.urbach.dev/cli/q/src/fs"
|
||||
"git.urbach.dev/cli/q/src/set"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
"git.urbach.dev/cli/q/src/ssa2asm"
|
||||
"git.urbach.dev/cli/q/src/token"
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
@ -15,9 +15,9 @@ import (
|
|||
// Function is the smallest unit of code.
|
||||
type Function struct {
|
||||
ssa.IR
|
||||
ssa2asm.Compiler
|
||||
Name string
|
||||
Package string
|
||||
UniqueName string
|
||||
File *fs.File
|
||||
Input []*ssa.Parameter
|
||||
Output []*ssa.Parameter
|
||||
|
@ -25,19 +25,16 @@ type Function struct {
|
|||
Identifiers map[string]ssa.Value
|
||||
All *Environment
|
||||
Dependencies set.Ordered[*Function]
|
||||
Assembler asm.Assembler
|
||||
CPU *cpu.CPU
|
||||
Type *types.Function
|
||||
Err error
|
||||
count count
|
||||
}
|
||||
|
||||
// NewFunction creates a new function.
|
||||
func NewFunction(name string, pkg string, file *fs.File) *Function {
|
||||
return &Function{
|
||||
Name: name,
|
||||
Package: pkg,
|
||||
UniqueName: fmt.Sprintf("%s.%s", pkg, name),
|
||||
Name: name,
|
||||
Package: pkg,
|
||||
|
||||
File: file,
|
||||
Identifiers: make(map[string]ssa.Value, 8),
|
||||
IR: ssa.IR{
|
||||
|
@ -45,8 +42,11 @@ func NewFunction(name string, pkg string, file *fs.File) *Function {
|
|||
{Instructions: make([]ssa.Value, 0, 8)},
|
||||
},
|
||||
},
|
||||
Assembler: asm.Assembler{
|
||||
Instructions: make([]asm.Instruction, 0, 8),
|
||||
Compiler: ssa2asm.Compiler{
|
||||
UniqueName: fmt.Sprintf("%s.%s", pkg, name),
|
||||
Assembler: asm.Assembler{
|
||||
Instructions: make([]asm.Instruction, 0, 8),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
package core
|
||||
|
||||
type counter = uint8
|
||||
|
||||
// count stores how often a certain statement appeared so we can generate a unique label from it.
|
||||
type count struct {
|
||||
data counter
|
||||
}
|
|
@ -63,6 +63,21 @@ func (err *UnknownIdentifier) Error() string {
|
|||
return fmt.Sprintf("Unknown identifier '%s'", err.Name)
|
||||
}
|
||||
|
||||
// UnknownStructField represents unknown struct fields.
|
||||
type UnknownStructField struct {
|
||||
StructName string
|
||||
FieldName string
|
||||
CorrectFieldName string
|
||||
}
|
||||
|
||||
func (err *UnknownStructField) Error() string {
|
||||
if err.CorrectFieldName != "" {
|
||||
return fmt.Sprintf("Unknown struct field '%s' in '%s', did you mean '%s'?", err.FieldName, err.StructName, err.CorrectFieldName)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Unknown struct field '%s' in '%s'", err.FieldName, err.StructName)
|
||||
}
|
||||
|
||||
// UnusedValue error is created when a value is never used.
|
||||
type UnusedValue struct {
|
||||
Value string
|
||||
|
|
|
@ -1,46 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
||||
// ssaToAsm converts the SSA IR to assembler instructions.
|
||||
func (f *Function) ssaToAsm() {
|
||||
f.Assembler.Append(&asm.Label{Name: f.UniqueName})
|
||||
|
||||
if !f.IsLeaf() && f.UniqueName != "core.init" {
|
||||
f.Assembler.Append(&asm.FunctionStart{})
|
||||
}
|
||||
|
||||
for instr := range f.Values {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Call:
|
||||
arg := instr.Arguments[0].(*ssa.Function)
|
||||
fn := f.All.Functions[arg.UniqueName]
|
||||
|
||||
if fn.IsExtern() {
|
||||
f.ssaValuesToRegisters(instr.Arguments[1:], f.CPU.ExternCall)
|
||||
f.Assembler.Append(&asm.CallExtern{Library: fn.Package, Function: fn.Name})
|
||||
} else {
|
||||
f.ssaValuesToRegisters(instr.Arguments[1:], f.CPU.Call)
|
||||
f.Assembler.Append(&asm.Call{Label: fn.UniqueName})
|
||||
}
|
||||
|
||||
case *ssa.Syscall:
|
||||
f.ssaValuesToRegisters(instr.Arguments, f.CPU.Syscall)
|
||||
f.Assembler.Append(&asm.Syscall{})
|
||||
|
||||
case *ssa.Return:
|
||||
f.Assembler.Append(&asm.Return{})
|
||||
}
|
||||
}
|
||||
|
||||
if !f.IsLeaf() && f.UniqueName != "core.init" {
|
||||
f.Assembler.Append(&asm.FunctionEnd{})
|
||||
}
|
||||
|
||||
if f.UniqueName != "core.exit" {
|
||||
f.Assembler.Append(&asm.Return{})
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
||||
// ssaValuesToRegisters generates assembler instructions to move the SSA values to the given registers.
|
||||
func (f *Function) ssaValuesToRegisters(args []ssa.Value, registers []cpu.Register) {
|
||||
extra := 0
|
||||
|
||||
for _, arg := range args {
|
||||
switch arg.(type) {
|
||||
case *ssa.Bytes:
|
||||
extra++
|
||||
}
|
||||
}
|
||||
|
||||
for i, arg := range slices.Backward(args) {
|
||||
switch arg := arg.(type) {
|
||||
case *ssa.Int:
|
||||
f.Assembler.Append(&asm.MoveRegisterNumber{
|
||||
Destination: registers[i+extra],
|
||||
Number: arg.Int,
|
||||
})
|
||||
case *ssa.Parameter:
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: registers[i+extra],
|
||||
Source: f.CPU.Call[arg.Index],
|
||||
})
|
||||
case *ssa.Bytes:
|
||||
f.count.data++
|
||||
label := f.CreateLabel("data", f.count.data)
|
||||
f.Assembler.SetData(label.Name, arg.Bytes)
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterNumber{
|
||||
Destination: registers[i+extra],
|
||||
Number: len(arg.Bytes),
|
||||
})
|
||||
|
||||
extra--
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterLabel{
|
||||
Destination: registers[i+extra],
|
||||
Label: label.Name,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,4 +5,5 @@ type CPU struct {
|
|||
Call []Register
|
||||
Syscall []Register
|
||||
ExternCall []Register
|
||||
Return []Register
|
||||
}
|
|
@ -3,7 +3,7 @@ package cpu
|
|||
import "fmt"
|
||||
|
||||
// Register represents the number of the register.
|
||||
type Register uint8
|
||||
type Register int8
|
||||
|
||||
// String returns the human readable name of the register.
|
||||
func (r Register) String() string {
|
||||
|
|
|
@ -12,6 +12,7 @@ type BinaryOp struct {
|
|||
Left Value
|
||||
Right Value
|
||||
Op token.Kind
|
||||
Id
|
||||
Liveness
|
||||
Source
|
||||
}
|
||||
|
@ -42,6 +43,10 @@ func (v *BinaryOp) IsConst() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (v *BinaryOp) Debug() string {
|
||||
return fmt.Sprintf("%%%d %s %%%d", v.Left.ID(), expression.Operators[v.Op].Symbol, v.Right.ID())
|
||||
}
|
||||
|
||||
func (v *BinaryOp) String() string {
|
||||
return fmt.Sprintf("%s %s %s", v.Left, expression.Operators[v.Op].Symbol, v.Right)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ type Block struct {
|
|||
// Append adds a new instruction to the block.
|
||||
func (block *Block) Append(instr Value) Value {
|
||||
for _, dep := range instr.Dependencies() {
|
||||
dep.AddUse(instr)
|
||||
dep.AddUser(instr)
|
||||
}
|
||||
|
||||
block.Instructions = append(block.Instructions, instr)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
)
|
||||
|
||||
type Bytes struct {
|
||||
Id
|
||||
Bytes []byte
|
||||
Liveness
|
||||
Source
|
||||
|
@ -31,10 +32,14 @@ func (v *Bytes) IsConst() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (v *Bytes) Debug() string {
|
||||
return v.String()
|
||||
}
|
||||
|
||||
func (v *Bytes) String() string {
|
||||
return strconv.Quote(string(v.Bytes))
|
||||
}
|
||||
|
||||
func (v *Bytes) Type() types.Type {
|
||||
return types.String
|
||||
return types.CString
|
||||
}
|
|
@ -19,5 +19,5 @@ func TestBytes(t *testing.T) {
|
|||
assert.False(t, hello.Equals(one))
|
||||
assert.True(t, hello.Equals(helloDup))
|
||||
assert.Equal(t, hello.String(), "\"Hello\"")
|
||||
assert.True(t, types.Is(hello.Type(), types.String))
|
||||
assert.True(t, types.Is(hello.Type(), types.CString))
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
package ssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Call struct {
|
||||
Id
|
||||
Arguments
|
||||
Liveness
|
||||
Source
|
||||
|
@ -27,14 +28,42 @@ func (v *Call) IsConst() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (v *Call) String() string {
|
||||
args := make([]string, 0, len(v.Arguments)-1)
|
||||
func (v *Call) Debug() string {
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString("%")
|
||||
tmp.WriteString(strconv.Itoa(v.Arguments[0].ID()))
|
||||
tmp.WriteString("(")
|
||||
args := v.Arguments[1:]
|
||||
|
||||
for _, arg := range v.Arguments[1:] {
|
||||
args = append(args, arg.String())
|
||||
for i, arg := range args {
|
||||
tmp.WriteString("%")
|
||||
tmp.WriteString(strconv.Itoa(arg.ID()))
|
||||
|
||||
if i != len(args)-1 {
|
||||
tmp.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s(%s)", v.Arguments[0].String(), strings.Join(args, ", "))
|
||||
tmp.WriteString(")")
|
||||
return tmp.String()
|
||||
}
|
||||
|
||||
func (v *Call) String() string {
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString(v.Arguments[0].String())
|
||||
tmp.WriteString("(")
|
||||
args := v.Arguments[1:]
|
||||
|
||||
for i, arg := range args {
|
||||
tmp.WriteString(arg.String())
|
||||
|
||||
if i != len(args)-1 {
|
||||
tmp.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
tmp.WriteString(")")
|
||||
return tmp.String()
|
||||
}
|
||||
|
||||
func (v *Call) Type() types.Type {
|
||||
|
|
|
@ -5,6 +5,8 @@ import "git.urbach.dev/cli/q/src/types"
|
|||
type Function struct {
|
||||
UniqueName string
|
||||
Typ *types.Function
|
||||
IsExtern bool
|
||||
Id
|
||||
Liveness
|
||||
Source
|
||||
}
|
||||
|
@ -27,6 +29,10 @@ func (v *Function) IsConst() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (v *Function) Debug() string {
|
||||
return v.String()
|
||||
}
|
||||
|
||||
func (v *Function) String() string {
|
||||
return v.UniqueName
|
||||
}
|
||||
|
|
11
src/ssa/ID.go
Normal file
11
src/ssa/ID.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package ssa
|
||||
|
||||
type Id int
|
||||
|
||||
func (id Id) ID() int {
|
||||
return int(id)
|
||||
}
|
||||
|
||||
func (id *Id) SetID(newId int) {
|
||||
*id = Id(newId)
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
package ssa
|
||||
|
||||
import "git.urbach.dev/cli/q/src/types"
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
// IR is a list of basic blocks.
|
||||
type IR struct {
|
||||
Blocks []*Block
|
||||
nextId int
|
||||
}
|
||||
|
||||
// AddBlock adds a new block to the function.
|
||||
|
@ -31,35 +34,29 @@ func (f *IR) Append(instr Value) Value {
|
|||
}
|
||||
}
|
||||
|
||||
instr.SetID(f.nextId)
|
||||
f.nextId++
|
||||
return f.Blocks[len(f.Blocks)-1].Append(instr)
|
||||
}
|
||||
|
||||
// AppendInt adds a new integer value to the last block.
|
||||
func (f *IR) AppendInt(x int) *Int {
|
||||
v := &Int{Int: x}
|
||||
f.Append(v)
|
||||
return v
|
||||
func (f *IR) AppendInt(x int) Value {
|
||||
return f.Append(&Int{Int: x})
|
||||
}
|
||||
|
||||
// AppendFunction adds a new function value to the last block.
|
||||
func (f *IR) AppendFunction(name string, typ *types.Function) *Function {
|
||||
v := &Function{UniqueName: name, Typ: typ}
|
||||
f.Append(v)
|
||||
return v
|
||||
func (f *IR) AppendFunction(name string, typ *types.Function, extern bool) Value {
|
||||
return f.Append(&Function{UniqueName: name, Typ: typ, IsExtern: extern})
|
||||
}
|
||||
|
||||
// AppendBytes adds a new byte slice value to the last block.
|
||||
func (f *IR) AppendBytes(s []byte) *Bytes {
|
||||
v := &Bytes{Bytes: s}
|
||||
f.Append(v)
|
||||
return v
|
||||
func (f *IR) AppendBytes(s []byte) Value {
|
||||
return f.Append(&Bytes{Bytes: s})
|
||||
}
|
||||
|
||||
// AppendString adds a new string value to the last block.
|
||||
func (f *IR) AppendString(s string) *Bytes {
|
||||
v := &Bytes{Bytes: []byte(s)}
|
||||
f.Append(v)
|
||||
return v
|
||||
func (f *IR) AppendString(s string) Value {
|
||||
return f.Append(&Bytes{Bytes: []byte(s)})
|
||||
}
|
||||
|
||||
// Values yields on each value.
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
type Int struct {
|
||||
Int int
|
||||
Id
|
||||
Liveness
|
||||
Source
|
||||
}
|
||||
|
@ -30,6 +31,10 @@ func (v *Int) IsConst() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (v *Int) Debug() string {
|
||||
return v.String()
|
||||
}
|
||||
|
||||
func (v *Int) String() string {
|
||||
return fmt.Sprintf("%d", v.Int)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package ssa
|
||||
|
||||
type Liveness struct {
|
||||
alive int
|
||||
users []Value
|
||||
}
|
||||
|
||||
func (v *Liveness) AddUse(user Value) {
|
||||
v.alive++
|
||||
func (v *Liveness) AddUser(user Value) {
|
||||
v.users = append(v.users, user)
|
||||
}
|
||||
|
||||
func (v *Liveness) Alive() int {
|
||||
return v.alive
|
||||
func (v *Liveness) CountUsers() int {
|
||||
return len(v.users)
|
||||
}
|
6
src/ssa/NoLiveness.go
Normal file
6
src/ssa/NoLiveness.go
Normal file
|
@ -0,0 +1,6 @@
|
|||
package ssa
|
||||
|
||||
type NoLiveness struct{}
|
||||
|
||||
func (a *NoLiveness) AddUser(user Value) { panic("value does not have liveness") }
|
||||
func (a *NoLiveness) CountUsers() int { return 0 }
|
|
@ -10,6 +10,7 @@ type Parameter struct {
|
|||
Index uint8
|
||||
Name string
|
||||
Typ types.Type
|
||||
Id
|
||||
Liveness
|
||||
Source
|
||||
}
|
||||
|
@ -32,6 +33,10 @@ func (v *Parameter) IsConst() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (v *Parameter) Debug() string {
|
||||
return v.String()
|
||||
}
|
||||
|
||||
func (v *Parameter) String() string {
|
||||
return fmt.Sprintf("in[%d]", v.Index)
|
||||
}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
package ssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Return struct {
|
||||
Id
|
||||
Arguments
|
||||
Source
|
||||
NoLiveness
|
||||
}
|
||||
|
||||
func (a *Return) AddUse(user Value) { panic("return is not a value") }
|
||||
func (a *Return) Alive() int { return 0 }
|
||||
|
||||
func (a *Return) Equals(v Value) bool {
|
||||
b, sameType := v.(*Return)
|
||||
|
||||
|
@ -39,18 +38,43 @@ func (v *Return) IsConst() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (v *Return) Debug() string {
|
||||
if len(v.Arguments) == 0 {
|
||||
return "return"
|
||||
}
|
||||
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString("return ")
|
||||
|
||||
for i, arg := range v.Arguments {
|
||||
tmp.WriteString("%")
|
||||
tmp.WriteString(strconv.Itoa(arg.ID()))
|
||||
|
||||
if i != len(v.Arguments)-1 {
|
||||
tmp.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
return tmp.String()
|
||||
}
|
||||
|
||||
func (v *Return) String() string {
|
||||
if len(v.Arguments) == 0 {
|
||||
return "return"
|
||||
}
|
||||
|
||||
args := make([]string, 0, len(v.Arguments))
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString("return ")
|
||||
|
||||
for _, arg := range v.Arguments {
|
||||
args = append(args, arg.String())
|
||||
for i, arg := range v.Arguments {
|
||||
tmp.WriteString(arg.String())
|
||||
|
||||
if i != len(v.Arguments)-1 {
|
||||
tmp.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("return %s", strings.Join(args, ", "))
|
||||
return tmp.String()
|
||||
}
|
||||
|
||||
func (v *Return) Type() types.Type {
|
||||
|
|
|
@ -4,10 +4,14 @@ import "git.urbach.dev/cli/q/src/token"
|
|||
|
||||
type Source token.List
|
||||
|
||||
func (v Source) Start() token.Position {
|
||||
return v[0].Position
|
||||
}
|
||||
|
||||
func (v Source) End() token.Position {
|
||||
return v[len(v)-1].End()
|
||||
}
|
||||
|
||||
func (v *Source) SetSource(source token.List) {
|
||||
*v = Source(source)
|
||||
}
|
||||
|
||||
func (v Source) Start() token.Position {
|
||||
return v[0].Position
|
||||
}
|
69
src/ssa/Struct.go
Normal file
69
src/ssa/Struct.go
Normal file
|
@ -0,0 +1,69 @@
|
|||
package ssa
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Struct struct {
|
||||
Typ *types.Struct
|
||||
Id
|
||||
Arguments
|
||||
Liveness
|
||||
Source
|
||||
}
|
||||
|
||||
func (a *Struct) Equals(v Value) bool {
|
||||
b, sameType := v.(*Struct)
|
||||
|
||||
if !sameType {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.Arguments.Equals(b.Arguments)
|
||||
}
|
||||
|
||||
func (v *Struct) IsConst() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *Struct) Debug() string {
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString(v.Typ.Name())
|
||||
tmp.WriteString("{")
|
||||
|
||||
for i, arg := range v.Arguments {
|
||||
tmp.WriteString("%")
|
||||
tmp.WriteString(strconv.Itoa(arg.ID()))
|
||||
|
||||
if i != len(v.Arguments)-1 {
|
||||
tmp.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
tmp.WriteString("}")
|
||||
return tmp.String()
|
||||
}
|
||||
|
||||
func (v *Struct) String() string {
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString(v.Typ.Name())
|
||||
tmp.WriteString("{")
|
||||
|
||||
for i, arg := range v.Arguments {
|
||||
tmp.WriteString(arg.String())
|
||||
|
||||
if i != len(v.Arguments)-1 {
|
||||
tmp.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
tmp.WriteString("}")
|
||||
return tmp.String()
|
||||
}
|
||||
|
||||
func (v *Struct) Type() types.Type {
|
||||
return v.Typ
|
||||
}
|
45
src/ssa/StructField.go
Normal file
45
src/ssa/StructField.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package ssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type StructField struct {
|
||||
Struct Value
|
||||
Field *types.Field
|
||||
Id
|
||||
Liveness
|
||||
Source
|
||||
}
|
||||
|
||||
func (v *StructField) Dependencies() []Value {
|
||||
return []Value{v.Struct}
|
||||
}
|
||||
|
||||
func (a *StructField) Equals(v Value) bool {
|
||||
b, sameType := v.(*StructField)
|
||||
|
||||
if !sameType {
|
||||
return false
|
||||
}
|
||||
|
||||
return a.Field == b.Field
|
||||
}
|
||||
|
||||
func (v *StructField) IsConst() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (v *StructField) Debug() string {
|
||||
return fmt.Sprintf("%%%d.%s", v.Struct.ID(), v.Field)
|
||||
}
|
||||
|
||||
func (v *StructField) String() string {
|
||||
return fmt.Sprintf("%s.%s", v.Struct, v.Field)
|
||||
}
|
||||
|
||||
func (v *StructField) Type() types.Type {
|
||||
return v.Field.Type
|
||||
}
|
|
@ -1,13 +1,14 @@
|
|||
package ssa
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.urbach.dev/cli/q/src/types"
|
||||
)
|
||||
|
||||
type Syscall struct {
|
||||
Id
|
||||
Arguments
|
||||
Liveness
|
||||
Source
|
||||
|
@ -27,14 +28,37 @@ func (v *Syscall) IsConst() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (v *Syscall) String() string {
|
||||
args := make([]string, 0, len(v.Arguments))
|
||||
func (v *Syscall) Debug() string {
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString("syscall(")
|
||||
|
||||
for _, arg := range v.Arguments {
|
||||
args = append(args, arg.String())
|
||||
for i, arg := range v.Arguments {
|
||||
tmp.WriteString("%")
|
||||
tmp.WriteString(strconv.Itoa(arg.ID()))
|
||||
|
||||
if i != len(v.Arguments)-1 {
|
||||
tmp.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("syscall(%s)", strings.Join(args, ", "))
|
||||
tmp.WriteString(")")
|
||||
return tmp.String()
|
||||
}
|
||||
|
||||
func (v *Syscall) String() string {
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString("syscall(")
|
||||
|
||||
for i, arg := range v.Arguments {
|
||||
tmp.WriteString(arg.String())
|
||||
|
||||
if i != len(v.Arguments)-1 {
|
||||
tmp.WriteString(", ")
|
||||
}
|
||||
}
|
||||
|
||||
tmp.WriteString(")")
|
||||
return tmp.String()
|
||||
}
|
||||
|
||||
func (v *Syscall) Type() types.Type {
|
||||
|
|
|
@ -6,13 +6,24 @@ import (
|
|||
)
|
||||
|
||||
type Value interface {
|
||||
AddUse(Value)
|
||||
Alive() int
|
||||
Dependencies() []Value
|
||||
End() token.Position
|
||||
Equals(Value) bool
|
||||
// Essentials
|
||||
Debug() string
|
||||
ID() int
|
||||
IsConst() bool
|
||||
SetID(int)
|
||||
String() string
|
||||
Start() token.Position
|
||||
Type() types.Type
|
||||
|
||||
// Arguments
|
||||
Dependencies() []Value
|
||||
Equals(Value) bool
|
||||
|
||||
// Liveness
|
||||
AddUser(Value)
|
||||
CountUsers() int
|
||||
|
||||
// Source
|
||||
SetSource(token.List)
|
||||
Start() token.Position
|
||||
End() token.Position
|
||||
}
|
13
src/ssa2asm/Compiler.go
Normal file
13
src/ssa2asm/Compiler.go
Normal file
|
@ -0,0 +1,13 @@
|
|||
package ssa2asm
|
||||
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
)
|
||||
|
||||
type Compiler struct {
|
||||
UniqueName string
|
||||
Assembler asm.Assembler
|
||||
CPU *cpu.CPU
|
||||
Count Count
|
||||
}
|
25
src/ssa2asm/Compiler_test.go
Normal file
25
src/ssa2asm/Compiler_test.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package ssa2asm_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.urbach.dev/cli/q/src/build"
|
||||
"git.urbach.dev/cli/q/src/compiler"
|
||||
"git.urbach.dev/go/assert"
|
||||
)
|
||||
|
||||
func TestHelloExample(t *testing.T) {
|
||||
b := build.New("../../examples/hello")
|
||||
systems := []build.OS{build.Linux, build.Mac, build.Windows}
|
||||
architectures := []build.Arch{build.ARM, build.X86}
|
||||
|
||||
for _, os := range systems {
|
||||
b.OS = os
|
||||
|
||||
for _, arch := range architectures {
|
||||
b.SetArch(arch)
|
||||
_, err := compiler.Compile(b)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
}
|
||||
}
|
9
src/ssa2asm/Count.go
Normal file
9
src/ssa2asm/Count.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
package ssa2asm
|
||||
|
||||
// Counter is the data type for counters.
|
||||
type Counter uint16
|
||||
|
||||
// Count stores how often a certain statement appeared so we can generate a unique label from it.
|
||||
type Count struct {
|
||||
Data Counter
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package core
|
||||
package ssa2asm
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
// CreateLabel creates a label that is tied to this function by using a suffix.
|
||||
func (f *Function) CreateLabel(prefix string, count counter) *asm.Label {
|
||||
func (f *Compiler) CreateLabel(prefix string, count Counter) *asm.Label {
|
||||
tmp := strings.Builder{}
|
||||
tmp.WriteString(prefix)
|
||||
tmp.WriteString(" ")
|
76
src/ssa2asm/GenerateAssembly.go
Normal file
76
src/ssa2asm/GenerateAssembly.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
package ssa2asm
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
||||
// GenerateAssembly converts the SSA IR to assembler instructions.
|
||||
func (f *Compiler) GenerateAssembly(ir ssa.IR, isLeaf bool) {
|
||||
f.Assembler.Append(&asm.Label{Name: f.UniqueName})
|
||||
|
||||
if !isLeaf && f.UniqueName != "core.init" {
|
||||
f.Assembler.Append(&asm.FunctionStart{})
|
||||
}
|
||||
|
||||
for instr := range ir.Values {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Call:
|
||||
fn := instr.Arguments[0].(*ssa.Function)
|
||||
args := instr.Arguments[1:]
|
||||
|
||||
if fn.IsExtern {
|
||||
for i := range slices.Backward(args) {
|
||||
f.ValueToRegister(args[i], f.CPU.ExternCall[i])
|
||||
}
|
||||
|
||||
dot := strings.IndexByte(fn.UniqueName, '.')
|
||||
library := fn.UniqueName[:dot]
|
||||
function := fn.UniqueName[dot+1:]
|
||||
f.Assembler.Append(&asm.CallExtern{Library: library, Function: function})
|
||||
} else {
|
||||
offset := 0
|
||||
|
||||
for i := range slices.Backward(args) {
|
||||
structure, isStruct := args[i].(*ssa.Struct)
|
||||
|
||||
if isStruct {
|
||||
for _, field := range structure.Arguments {
|
||||
f.ValueToRegister(field, f.CPU.Call[offset+i])
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
f.ValueToRegister(args[i], f.CPU.Call[offset+i])
|
||||
}
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.Call{Label: fn.UniqueName})
|
||||
}
|
||||
|
||||
case *ssa.Return:
|
||||
for i := range slices.Backward(instr.Arguments) {
|
||||
f.ValueToRegister(instr.Arguments[i], f.CPU.Return[i])
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.Return{})
|
||||
|
||||
case *ssa.Syscall:
|
||||
for i := range slices.Backward(instr.Arguments) {
|
||||
f.ValueToRegister(instr.Arguments[i], f.CPU.Syscall[i])
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.Syscall{})
|
||||
}
|
||||
}
|
||||
|
||||
if !isLeaf && f.UniqueName != "core.init" {
|
||||
f.Assembler.Append(&asm.FunctionEnd{})
|
||||
}
|
||||
|
||||
if f.UniqueName != "core.exit" {
|
||||
f.Assembler.Append(&asm.Return{})
|
||||
}
|
||||
}
|
54
src/ssa2asm/ValueToRegister.go
Normal file
54
src/ssa2asm/ValueToRegister.go
Normal file
|
@ -0,0 +1,54 @@
|
|||
package ssa2asm
|
||||
|
||||
import (
|
||||
"git.urbach.dev/cli/q/src/asm"
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
"git.urbach.dev/cli/q/src/ssa"
|
||||
)
|
||||
|
||||
// ValueToRegister moves a value into the given `destination` register.
|
||||
func (f *Compiler) ValueToRegister(instr ssa.Value, destination cpu.Register) {
|
||||
switch instr := instr.(type) {
|
||||
case *ssa.Bytes:
|
||||
f.Count.Data++
|
||||
label := f.CreateLabel("data", f.Count.Data)
|
||||
f.Assembler.SetData(label.Name, instr.Bytes)
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterLabel{
|
||||
Destination: destination,
|
||||
Label: label.Name,
|
||||
})
|
||||
|
||||
case *ssa.Int:
|
||||
f.Assembler.Append(&asm.MoveRegisterNumber{
|
||||
Destination: destination,
|
||||
Number: instr.Int,
|
||||
})
|
||||
|
||||
case *ssa.Parameter:
|
||||
source := f.CPU.Call[instr.Index]
|
||||
|
||||
if source == destination {
|
||||
return
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: destination,
|
||||
Source: source,
|
||||
})
|
||||
|
||||
case *ssa.StructField:
|
||||
parameter := instr.Struct.(*ssa.Parameter)
|
||||
field := instr.Field
|
||||
source := f.CPU.Call[parameter.Index+field.Index]
|
||||
|
||||
if source == destination {
|
||||
return
|
||||
}
|
||||
|
||||
f.Assembler.Append(&asm.MoveRegisterRegister{
|
||||
Destination: destination,
|
||||
Source: source,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ var (
|
|||
Int8 = &Base{name: "int8", size: 1}
|
||||
Float64 = &Base{name: "float64", size: 8}
|
||||
Float32 = &Base{name: "float32", size: 4}
|
||||
String = &Array{Of: Byte}
|
||||
UInt64 = &Base{name: "uint64", size: 8}
|
||||
UInt32 = &Base{name: "uint32", size: 4}
|
||||
UInt16 = &Base{name: "uint16", size: 2}
|
||||
|
@ -20,6 +19,19 @@ var (
|
|||
Void = &Base{name: "void", size: 0}
|
||||
)
|
||||
|
||||
var (
|
||||
CString = &Pointer{To: Byte}
|
||||
String = &Struct{
|
||||
Package: "",
|
||||
UniqueName: "string",
|
||||
name: "string",
|
||||
Fields: []*Field{
|
||||
{Name: "ptr", Type: CString, Index: 0, Offset: 0},
|
||||
{Name: "len", Type: Int, Index: 1, Offset: 8},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
Byte = UInt8
|
||||
Float = Float64
|
||||
|
|
17
src/types/Field.go
Normal file
17
src/types/Field.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package types
|
||||
|
||||
import "git.urbach.dev/cli/q/src/token"
|
||||
|
||||
// Field is a memory region in a data structure.
|
||||
type Field struct {
|
||||
Name string
|
||||
Type Type
|
||||
Position token.Position
|
||||
Index uint8
|
||||
Offset uint8
|
||||
}
|
||||
|
||||
// String returns the name of the struct.
|
||||
func (f *Field) String() string {
|
||||
return f.Name
|
||||
}
|
|
@ -45,6 +45,8 @@ func Parse[T ~[]token.Token](tokens T, source []byte) Type {
|
|||
}
|
||||
|
||||
switch tokens[0].String(source) {
|
||||
case "string":
|
||||
return String
|
||||
case "int":
|
||||
return Int
|
||||
case "int64":
|
||||
|
|
50
src/types/Struct.go
Normal file
50
src/types/Struct.go
Normal file
|
@ -0,0 +1,50 @@
|
|||
package types
|
||||
|
||||
// Struct is a structure in memory whose regions are addressable with named fields.
|
||||
type Struct struct {
|
||||
Package string
|
||||
UniqueName string
|
||||
Fields []*Field
|
||||
name string
|
||||
}
|
||||
|
||||
// NewStruct creates a new struct.
|
||||
func NewStruct(pkg string, name string) *Struct {
|
||||
return &Struct{
|
||||
Package: pkg,
|
||||
UniqueName: pkg + "." + name,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
// AddField adds a new field to the end of the struct.
|
||||
func (s *Struct) AddField(field *Field) {
|
||||
s.Fields = append(s.Fields, field)
|
||||
}
|
||||
|
||||
// FieldByName returns the field with the given name if it exists.
|
||||
func (s *Struct) FieldByName(name string) *Field {
|
||||
for _, field := range s.Fields {
|
||||
if field.Name == name {
|
||||
return field
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Name returns the name of the struct.
|
||||
func (s *Struct) Name() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
// Size returns the total size in bytes.
|
||||
func (s *Struct) Size() int {
|
||||
sum := 0
|
||||
|
||||
for _, field := range s.Fields {
|
||||
sum += field.Type.Size()
|
||||
}
|
||||
|
||||
return sum
|
||||
}
|
|
@ -13,7 +13,7 @@ func TestName(t *testing.T) {
|
|||
assert.Equal(t, types.AnyPointer.Name(), "*any")
|
||||
assert.Equal(t, (&types.Pointer{To: types.Int}).Name(), "*int64")
|
||||
assert.Equal(t, (&types.Array{Of: types.Int}).Name(), "[]int64")
|
||||
assert.Equal(t, types.String.Name(), "[]uint8")
|
||||
assert.Equal(t, types.String.Name(), "string")
|
||||
}
|
||||
|
||||
func TestSize(t *testing.T) {
|
||||
|
@ -24,7 +24,7 @@ func TestSize(t *testing.T) {
|
|||
assert.Equal(t, types.Int64.Size(), 8)
|
||||
assert.Equal(t, types.AnyArray.Size(), 8)
|
||||
assert.Equal(t, types.AnyPointer.Size(), 8)
|
||||
assert.Equal(t, types.String.Size(), 8)
|
||||
assert.Equal(t, types.String.Size(), 16)
|
||||
assert.Equal(t, (&types.Pointer{To: types.Int}).Size(), 8)
|
||||
}
|
||||
|
||||
|
|
|
@ -25,4 +25,5 @@ var CPU = cpu.CPU{
|
|||
Call: []cpu.Register{R0, R7, R6, R2, R10, R8, R9},
|
||||
Syscall: []cpu.Register{R0, R7, R6, R2, R10, R8, R9},
|
||||
ExternCall: []cpu.Register{R1, R2, R8, R9},
|
||||
Return: []cpu.Register{R0, R7, R6},
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue