Compare commits
2 Commits
5d38a4980a
...
37afbde0da
Author | SHA1 | Date | |
---|---|---|---|
37afbde0da
|
|||
12894dbcc5
|
@ -7,15 +7,15 @@ struct Point {
|
||||
}
|
||||
|
||||
main() {
|
||||
p := construct()
|
||||
p := construct(1, 2)
|
||||
print(p)
|
||||
delete(p)
|
||||
}
|
||||
|
||||
construct() -> *Point {
|
||||
construct(x Int, y Int) -> *Point {
|
||||
p := new(Point)
|
||||
p.x = 1
|
||||
p.y = 2
|
||||
p.x = x
|
||||
p.y = y
|
||||
return p
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,12 @@ func buildWithArgs(args []string) (*build.Build, error) {
|
||||
case "-d", "--dry":
|
||||
config.Dry = true
|
||||
|
||||
case "-s", "--statistics":
|
||||
config.Statistics = true
|
||||
|
||||
case "-v", "--verbose":
|
||||
config.Assembler = true
|
||||
config.Statistics = true
|
||||
|
||||
case "--arch":
|
||||
i++
|
||||
@ -105,6 +109,10 @@ func makeExecutable(b *build.Build) error {
|
||||
result.PrintInstructions()
|
||||
}
|
||||
|
||||
if config.Statistics {
|
||||
result.PrintStatistics()
|
||||
}
|
||||
|
||||
if config.Dry {
|
||||
return nil
|
||||
}
|
||||
|
@ -7,19 +7,22 @@ import (
|
||||
|
||||
// Help shows the command line argument usage.
|
||||
func Help(w io.Writer, code int) int {
|
||||
fmt.Fprintln(w, `Usage: q [command] [options]
|
||||
fmt.Fprintln(w, `Usage:
|
||||
|
||||
commands:
|
||||
q [command] [options]
|
||||
|
||||
build [directory | file]
|
||||
run [directory | file]
|
||||
help
|
||||
system
|
||||
Commands:
|
||||
|
||||
build options:
|
||||
build [directory | file] build an executable from a file or directory
|
||||
--arch [arch] cross-compile for a different CPU architecture (x86, arm or riscv)
|
||||
--assembler, -a show assembler instructions
|
||||
--dry, -d skip writing the executable to disk
|
||||
--os [os] cross-compile for a different OS (linux, mac or windows)
|
||||
--statistics, -s show statistics
|
||||
--verbose, -v show everything
|
||||
|
||||
--assembler, -a Show assembler instructions.
|
||||
--comments, -c Show assembler comments.
|
||||
--verbose, -v Show everything.`)
|
||||
run [directory | file] build and run the executable
|
||||
system show system information
|
||||
help show this help`)
|
||||
return code
|
||||
}
|
||||
|
@ -105,6 +105,7 @@ func Compile(files <-chan *fs.File, functions <-chan *core.Function, structs <-c
|
||||
|
||||
result.Main = main
|
||||
result.Functions = allFunctions
|
||||
result.finalize()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package compiler
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
@ -13,6 +14,7 @@ import (
|
||||
"git.akyoto.dev/cli/q/src/macho"
|
||||
"git.akyoto.dev/cli/q/src/pe"
|
||||
"git.akyoto.dev/cli/q/src/x64"
|
||||
"git.akyoto.dev/go/color/ansi"
|
||||
)
|
||||
|
||||
// Result contains all the compiled functions in a build.
|
||||
@ -21,10 +23,13 @@ type Result struct {
|
||||
Functions map[string]*core.Function
|
||||
InstructionCount int
|
||||
DataCount int
|
||||
Code []byte
|
||||
Data []byte
|
||||
DLLs dll.List
|
||||
}
|
||||
|
||||
// finalize generates the final machine code.
|
||||
func (r *Result) finalize() ([]byte, []byte, dll.List) {
|
||||
func (r *Result) finalize() {
|
||||
// This will be the entry point of the executable.
|
||||
// The only job of the entry function is to call `main` and exit cleanly.
|
||||
// The reason we call `main` instead of using `main` itself is to place
|
||||
@ -50,7 +55,7 @@ func (r *Result) finalize() ([]byte, []byte, dll.List) {
|
||||
final.DLLCall("kernel32.ExitProcess")
|
||||
}
|
||||
|
||||
dlls := dll.List{
|
||||
r.DLLs = dll.List{
|
||||
{Name: "kernel32", Functions: []string{"ExitProcess"}},
|
||||
}
|
||||
|
||||
@ -61,7 +66,7 @@ func (r *Result) finalize() ([]byte, []byte, dll.List) {
|
||||
|
||||
for _, library := range f.DLLs {
|
||||
for _, fn := range library.Functions {
|
||||
dlls = dlls.Append(library.Name, fn)
|
||||
r.DLLs = r.DLLs.Append(library.Name, fn)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -82,8 +87,7 @@ func (r *Result) finalize() ([]byte, []byte, dll.List) {
|
||||
final.DLLCall("kernel32.ExitProcess")
|
||||
}
|
||||
|
||||
code, data := final.Finalize(dlls)
|
||||
return code, data, dlls
|
||||
r.Code, r.Data = final.Finalize(r.DLLs)
|
||||
}
|
||||
|
||||
// eachFunction recursively finds all the calls to external functions.
|
||||
@ -119,10 +123,17 @@ func (r *Result) PrintInstructions() {
|
||||
})
|
||||
}
|
||||
|
||||
// PrintStatistics shows the statistics.
|
||||
func (r *Result) PrintStatistics() {
|
||||
ansi.Dim.Println("╭──────────────────────────────────────────────────────────────────────────────╮")
|
||||
ansi.Dim.Printf("│ %-44s%-32s │\n", "Code:", fmt.Sprintf("%d bytes", len(r.Code)))
|
||||
ansi.Dim.Printf("│ %-44s%-32s │\n", "Data:", fmt.Sprintf("%d bytes", len(r.Data)))
|
||||
ansi.Dim.Println("╰──────────────────────────────────────────────────────────────────────────────╯")
|
||||
}
|
||||
|
||||
// Write writes the executable to the given writer.
|
||||
func (r *Result) Write(writer io.Writer) error {
|
||||
code, data, dlls := r.finalize()
|
||||
return write(writer, code, data, dlls)
|
||||
return write(writer, r.Code, r.Data, r.DLLs)
|
||||
}
|
||||
|
||||
// Write writes an executable file to disk.
|
||||
|
@ -14,9 +14,12 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// Shows the assembly instructions at the end of the compilation.
|
||||
// Shows the assembly instructions at the end.
|
||||
Assembler bool
|
||||
|
||||
// Shows statistics at the end.
|
||||
Statistics bool
|
||||
|
||||
// Calculates the result of operations on constants at compile time.
|
||||
ConstantFold bool
|
||||
|
||||
@ -33,6 +36,7 @@ var (
|
||||
// Reset resets the configuration to its default values.
|
||||
func Reset() {
|
||||
Assembler = false
|
||||
Statistics = false
|
||||
ConstantFold = true
|
||||
Dry = false
|
||||
TargetArch = runtime.GOARCH
|
||||
|
33
src/core/CallSafe.go
Normal file
33
src/core/CallSafe.go
Normal file
@ -0,0 +1,33 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/asm"
|
||||
"git.akyoto.dev/cli/q/src/cpu"
|
||||
)
|
||||
|
||||
// CallSafe pushes used registers to the stack, executes the call and restores the original register value.
|
||||
func (f *Function) CallSafe(fn *Function, registers []cpu.Register) {
|
||||
for _, register := range f.CPU.Output {
|
||||
f.SaveRegister(register)
|
||||
}
|
||||
|
||||
for _, register := range f.CPU.General {
|
||||
if f.RegisterIsUsed(register) {
|
||||
f.Register(asm.PUSH, register)
|
||||
}
|
||||
}
|
||||
|
||||
f.Call(fn.UniqueName)
|
||||
|
||||
for _, register := range slices.Backward(f.CPU.General) {
|
||||
if f.RegisterIsUsed(register) {
|
||||
f.Register(asm.POP, register)
|
||||
}
|
||||
}
|
||||
|
||||
for _, register := range registers {
|
||||
f.FreeRegister(register)
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package core
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/asm"
|
||||
"git.akyoto.dev/cli/q/src/errors"
|
||||
"git.akyoto.dev/cli/q/src/expression"
|
||||
"git.akyoto.dev/cli/q/src/types"
|
||||
@ -103,33 +102,6 @@ func (f *Function) CompileCall(root *expression.Expression) (*Function, error) {
|
||||
}
|
||||
}
|
||||
|
||||
for _, register := range f.CPU.Output[:len(fn.Output)] {
|
||||
f.SaveRegister(register)
|
||||
}
|
||||
|
||||
for _, register := range f.CPU.General {
|
||||
if f.RegisterIsUsed(register) {
|
||||
f.Register(asm.PUSH, register)
|
||||
}
|
||||
}
|
||||
|
||||
f.Call(fn.UniqueName)
|
||||
|
||||
for _, register := range registers {
|
||||
if register == f.CPU.Output[0] && root.Parent != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
f.FreeRegister(register)
|
||||
}
|
||||
|
||||
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
||||
register := f.CPU.General[i]
|
||||
|
||||
if f.RegisterIsUsed(register) {
|
||||
f.Register(asm.POP, register)
|
||||
}
|
||||
}
|
||||
|
||||
f.CallSafe(fn, registers)
|
||||
return fn, nil
|
||||
}
|
||||
|
@ -16,22 +16,6 @@ func (f *Function) CompileDelete(root *expression.Expression) error {
|
||||
f.SaveRegister(f.CPU.Input[1])
|
||||
f.RegisterRegister(asm.MOVE, f.CPU.Input[0], variable.Register)
|
||||
f.RegisterNumber(asm.MOVE, f.CPU.Input[1], int(variable.Type.(*types.Pointer).To.Size()))
|
||||
|
||||
for _, register := range f.CPU.General {
|
||||
if f.RegisterIsUsed(register) {
|
||||
f.Register(asm.PUSH, register)
|
||||
}
|
||||
}
|
||||
|
||||
f.Call("mem.free")
|
||||
|
||||
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
||||
register := f.CPU.General[i]
|
||||
|
||||
if f.RegisterIsUsed(register) {
|
||||
f.Register(asm.POP, register)
|
||||
}
|
||||
}
|
||||
|
||||
f.CallSafe(f.Functions["mem.free"], f.CPU.Input[:2])
|
||||
return nil
|
||||
}
|
||||
|
@ -12,22 +12,6 @@ func (f *Function) CompileNew(root *expression.Expression) error {
|
||||
typ := f.Types[structName]
|
||||
f.SaveRegister(f.CPU.Input[0])
|
||||
f.RegisterNumber(asm.MOVE, f.CPU.Input[0], typ.Size())
|
||||
|
||||
for _, register := range f.CPU.General {
|
||||
if f.RegisterIsUsed(register) {
|
||||
f.Register(asm.PUSH, register)
|
||||
}
|
||||
}
|
||||
|
||||
f.Call("mem.alloc")
|
||||
|
||||
for i := len(f.CPU.General) - 1; i >= 0; i-- {
|
||||
register := f.CPU.General[i]
|
||||
|
||||
if f.RegisterIsUsed(register) {
|
||||
f.Register(asm.POP, register)
|
||||
}
|
||||
}
|
||||
|
||||
f.CallSafe(f.Functions["mem.alloc"], f.CPU.Input[:1])
|
||||
return nil
|
||||
}
|
||||
|
@ -12,6 +12,15 @@ import (
|
||||
func (f *Function) PrintInstructions() {
|
||||
ansi.Dim.Println("╭──────────────────────────────────────────────────────────────────────────────╮")
|
||||
|
||||
if len(f.Input) > 0 {
|
||||
for i, input := range f.Input {
|
||||
ansi.Dim.Printf("│ %-44s%-32s", input.Name, f.CPU.Input[i])
|
||||
ansi.Dim.Print(" │\n")
|
||||
}
|
||||
|
||||
ansi.Dim.Println("├──────────────────────────────────────────────────────────────────────────────┤")
|
||||
}
|
||||
|
||||
for i, x := range f.Assembler.Instructions {
|
||||
ansi.Dim.Print("│ ")
|
||||
|
||||
|
Reference in New Issue
Block a user