From 3418ba055d97413cd0d46e06bbab6aa61b8e21dd Mon Sep 17 00:00:00 2001 From: MineGame159 Date: Wed, 7 Feb 2024 20:41:58 +0100 Subject: [PATCH] CORE: Add windows support --- .gitignore | 1 + cmd/build/build.go | 18 ++++--- cmd/build/compiler.go | 49 ++++------------- cmd/build/linker.go | 24 +++++++++ cmd/build/linux_linker.go | 52 ++++++++++++++++++ cmd/build/windows_linker.go | 103 ++++++++++++++++++++++++++++++++++++ core/abi/abi.go | 2 + core/abi/win64.go | 96 +++++++++++++++++++++++++++++++++ go.mod | 2 +- go.sum | 4 +- 10 files changed, 301 insertions(+), 50 deletions(-) create mode 100644 cmd/build/linker.go create mode 100644 cmd/build/linux_linker.go create mode 100644 cmd/build/windows_linker.go create mode 100644 core/abi/win64.go diff --git a/.gitignore b/.gitignore index e396430..742067e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ example/build tests/build /fireball +/fireball.exe diff --git a/cmd/build/build.go b/cmd/build/build.go index 1a1b29a..6955408 100644 --- a/cmd/build/build.go +++ b/cmd/build/build.go @@ -19,7 +19,7 @@ func Build(project *workspace.Project, entrypoint *ir.Module, optimizationLevel irPaths := make([]string, 0, len(project.Files)) for _, file := range project.Files { - path := strings.ReplaceAll(file.Path, "/", "-") + path := strings.ReplaceAll(file.Path, string(os.PathSeparator), "-") path = filepath.Join(project.Path, "build", path[:len(path)-3]+".ll") irFile, err := os.Create(path) @@ -56,18 +56,12 @@ func Build(project *workspace.Project, entrypoint *ir.Module, optimizationLevel c.AddInput(irPath) } - if runtime.GOOS == "darwin" { - c.AddLibrary("System") - } else { - c.AddLibrary("m") - c.AddLibrary("c") - } - for _, library := range project.Config.LinkLibraries { c.AddLibrary(library) } output := filepath.Join(project.Path, "build", outputName) + output = withExecutableExtension(output) err = c.Compile(output) if err != nil { @@ -76,3 +70,11 @@ func Build(project *workspace.Project, entrypoint *ir.Module, optimizationLevel return output, nil } + +func withExecutableExtension(path string) string { + if runtime.GOOS == "windows" { + return path + ".exe" + } + + return path +} diff --git a/cmd/build/compiler.go b/cmd/build/compiler.go index c83a140..b829d16 100644 --- a/cmd/build/compiler.go +++ b/cmd/build/compiler.go @@ -6,7 +6,6 @@ import ( "fmt" "os/exec" "path/filepath" - "runtime" "strings" ) @@ -101,42 +100,23 @@ func (c *Compiler) compileIr(input string) error { return execute(cmd) } -//goland:noinspection GoBoolExpressions func (c *Compiler) linkExecutable(inputs []string, output string) error { - // Create command - cmd := exec.Command(getLinker(), "-L/usr/lib") - - switch runtime.GOOS { - case "linux": - cmd.Args = append(cmd.Args, "-dynamic-linker") - cmd.Args = append(cmd.Args, "/lib64/ld-linux-x86-64.so.2") - - cmd.Args = append(cmd.Args, "/usr/lib/crt1.o") - cmd.Args = append(cmd.Args, "/usr/lib/crti.o") + l := GetLinker() - case "darwin": - cmd.Args = append(cmd.Args, "-dynamic") - cmd.Args = append(cmd.Args, "-syslibroot") - cmd.Args = append(cmd.Args, "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk") + err := l.Check() + if err != nil { + return err } for _, library := range c.libraries { - cmd.Args = append(cmd.Args, "-l"+library) + l.AddLibrary(library) } for _, input := range inputs { - cmd.Args = append(cmd.Args, withExtension(input, "o")) + l.AddInput(withExtension(input, "o")) } - if runtime.GOOS == "linux" { - cmd.Args = append(cmd.Args, "/usr/lib/crtn.o") - } - - cmd.Args = append(cmd.Args, "-o") - cmd.Args = append(cmd.Args, output) - - // Execute - return execute(cmd) + return l.Link(output) } func execute(cmd *exec.Cmd) error { @@ -144,6 +124,9 @@ func execute(cmd *exec.Cmd) error { cmd.Stderr = &stderr err := cmd.Run() + if err != nil { + return errors.New(err.Error() + " : " + stderr.String()) + } if !cmd.ProcessState.Success() { return errors.New(stderr.String()) @@ -152,18 +135,6 @@ func execute(cmd *exec.Cmd) error { return err } -func getLinker() string { - switch runtime.GOOS { - case "linux": - return "ld.lld" - case "darwin": - return "ld" - - default: - panic("Unknown operating system: " + runtime.GOOS) - } -} - func withExtension(path, extension string) string { dot := strings.LastIndexByte(path, '.') return path[:dot+1] + extension diff --git a/cmd/build/linker.go b/cmd/build/linker.go new file mode 100644 index 0000000..8a4fedd --- /dev/null +++ b/cmd/build/linker.go @@ -0,0 +1,24 @@ +package build + +import "runtime" + +type Linker interface { + Check() error + + AddLibrary(library string) + AddInput(input string) + + Link(output string) error +} + +func GetLinker() Linker { + switch runtime.GOOS { + case "windows": + return &windowsLinker{} + case "linux": + return &linuxLinker{} + + default: + panic("Operating system not implemented") + } +} diff --git a/cmd/build/linux_linker.go b/cmd/build/linux_linker.go new file mode 100644 index 0000000..2748084 --- /dev/null +++ b/cmd/build/linux_linker.go @@ -0,0 +1,52 @@ +package build + +import ( + "os/exec" +) + +type linuxLinker struct { + libraries []string + inputs []string +} + +func (l *linuxLinker) Check() error { + l.libraries = append(l.libraries, "c") + l.libraries = append(l.libraries, "m") + + return nil +} + +func (l *linuxLinker) AddLibrary(library string) { + l.libraries = append(l.libraries, library) +} + +func (l *linuxLinker) AddInput(input string) { + l.inputs = append(l.inputs, input) +} + +func (l *linuxLinker) Link(output string) error { + cmd := exec.Command("ld.lld") + + cmd.Args = append(cmd.Args, "-L/usr/lib") + + cmd.Args = append(cmd.Args, "-dynamic-linker") + cmd.Args = append(cmd.Args, "/lib64/ld-linux-x86-64.so.2") + + cmd.Args = append(cmd.Args, "/usr/lib/crt1.o") + cmd.Args = append(cmd.Args, "/usr/lib/crti.o") + + for _, library := range l.libraries { + cmd.Args = append(cmd.Args, "-l"+library) + } + + for _, input := range l.inputs { + cmd.Args = append(cmd.Args, withExtension(input, "o")) + } + + cmd.Args = append(cmd.Args, "/usr/lib/crtn.o") + + cmd.Args = append(cmd.Args, "-o") + cmd.Args = append(cmd.Args, output) + + return execute(cmd) +} diff --git a/cmd/build/windows_linker.go b/cmd/build/windows_linker.go new file mode 100644 index 0000000..489fc4f --- /dev/null +++ b/cmd/build/windows_linker.go @@ -0,0 +1,103 @@ +package build + +import ( + "errors" + "fmt" + "os" + "os/exec" + "path/filepath" +) + +type windowsLinker struct { + ucrtPath string + umPath string + msvcPath string + + libraries []string + inputs []string +} + +func (w *windowsLinker) Check() error { + if err := w.findKit(); err != nil { + return err + } + if err := w.findMsvc(); err != nil { + return err + } + + w.libraries = append(w.libraries, "libucrt.lib") + w.libraries = append(w.libraries, "libcmt.lib") + + return nil +} + +func (w *windowsLinker) findKit() error { + base := "C:\\Program Files (x86)\\Windows Kits\\10\\Lib" + + entries, err := os.ReadDir(base) + if err != nil { + return errors.New("failed to find a valid windows development kit") + } + + w.ucrtPath = filepath.Join(base, entries[0].Name(), "ucrt", "x64") + _, err = os.Stat(w.ucrtPath) + if err != nil { + return errors.New("failed to find a valid windows development kit, ucrt") + } + + w.umPath = filepath.Join(base, entries[0].Name(), "um", "x64") + _, err = os.Stat(w.umPath) + if err != nil { + return errors.New("failed to find a valid windows development kit, um") + } + + return nil +} + +func (w *windowsLinker) findMsvc() error { + base := "C:\\Program Files (x86)\\Microsoft Visual Studio\\2022\\BuildTools\\VC\\Tools\\MSVC" + + entries, err := os.ReadDir(base) + if err != nil { + return errors.New("failed to find a MSVC installation") + } + + w.msvcPath = filepath.Join(base, entries[0].Name(), "lib", "x64") + _, err = os.Stat(w.msvcPath) + if err != nil { + return errors.New("failed to find a MSVC installation, lib") + } + + return nil +} + +func (w *windowsLinker) AddLibrary(library string) { + w.libraries = append(w.libraries, library) +} + +func (w *windowsLinker) AddInput(input string) { + w.inputs = append(w.inputs, input) +} + +func (w *windowsLinker) Link(output string) error { + cmd := exec.Command("lld-link") + + cmd.Args = append(cmd.Args, fmt.Sprintf("/libpath:%s", w.ucrtPath)) + cmd.Args = append(cmd.Args, fmt.Sprintf("/libpath:%s", w.umPath)) + cmd.Args = append(cmd.Args, fmt.Sprintf("/libpath:%s", w.msvcPath)) + + for _, library := range w.libraries { + cmd.Args = append(cmd.Args, library) + } + + for _, input := range w.inputs { + cmd.Args = append(cmd.Args, fmt.Sprintf("%s", input)) + } + + cmd.Args = append(cmd.Args, "/machine:x64") + cmd.Args = append(cmd.Args, "/subsystem:console") + cmd.Args = append(cmd.Args, "/debug") + cmd.Args = append(cmd.Args, fmt.Sprintf("/out:%s", output)) + + return execute(cmd) +} diff --git a/core/abi/abi.go b/core/abi/abi.go index b8325d6..1f0ab98 100644 --- a/core/abi/abi.go +++ b/core/abi/abi.go @@ -34,6 +34,8 @@ type Abi interface { func GetTargetAbi() Abi { switch runtime.GOOS { + case "windows": + return WIN64 case "linux", "darwin": return AMD64 diff --git a/core/abi/win64.go b/core/abi/win64.go new file mode 100644 index 0000000..3f76a36 --- /dev/null +++ b/core/abi/win64.go @@ -0,0 +1,96 @@ +package abi + +import ( + "fireball/core/ast" +) + +var WIN64 Abi = &win64{} + +type win64 struct{} + +func (w *win64) Size(type_ ast.Type) uint32 { + if type_, ok := ast.As[*ast.Struct](type_); ok { + layout := cLayout{} + + for _, field := range type_.Fields { + layout.add(w.Size(field.Type), w.Align(field.Type)) + } + + return layout.size() + } + + return getX64Size(w, type_) +} + +func (w *win64) Align(type_ ast.Type) uint32 { + return getX64Align(type_) +} + +func (w *win64) 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(w.Size(field.Type), w.Align(field.Type)) + } + + return decl.Fields, offsets +} + +func (w *win64) 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, *ast.Func: + return append(args, ptr) + + case *ast.Array, *ast.Struct, *ast.Interface: + var arg Arg + + switch w.Size(type_) { + case 1: + arg = i8 + case 2: + arg = i16 + case 4: + arg = i32 + case 8: + arg = i64 + default: + arg = memory + } + + return append(args, arg) + + case *ast.Enum: + return w.Classify(type_.ActualType, args) + + default: + return args + } +} diff --git a/go.mod b/go.mod index 664b56e..2b3793e 100644 --- a/go.mod +++ b/go.mod @@ -22,5 +22,5 @@ require ( github.com/spf13/pflag v1.0.5 // indirect go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/sys v0.16.0 // indirect ) diff --git a/go.sum b/go.sum index 3d1cdbe..5c5d9cc 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=