mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-24 06:10:12 +00:00
Initial commit of llgo.
llvm-svn: 222857
This commit is contained in:
parent
5468e86469
commit
ad9841e8ac
170
llgo/CMakeLists.txt
Normal file
170
llgo/CMakeLists.txt
Normal file
@ -0,0 +1,170 @@
|
||||
include(ExternalProject)
|
||||
include(ProcessorCount)
|
||||
|
||||
llvm_add_go_executable(llgo llvm.org/llgo/cmd/gllgo ALL DEPENDS
|
||||
build/context.go
|
||||
cmd/gllgo/gllgo.go
|
||||
debug/debug.go
|
||||
irgen/annotations.go
|
||||
irgen/attribute.go
|
||||
irgen/builtins.go
|
||||
irgen/cabi.go
|
||||
irgen/call.go
|
||||
irgen/channels.go
|
||||
irgen/closures.go
|
||||
irgen/compiler.go
|
||||
irgen/errors.go
|
||||
irgen/indirect.go
|
||||
irgen/interfaces.go
|
||||
irgen/maps.go
|
||||
irgen/parser.go
|
||||
irgen/predicates.go
|
||||
irgen/println.go
|
||||
irgen/runtime.go
|
||||
irgen/slice.go
|
||||
irgen/ssa.go
|
||||
irgen/strings.go
|
||||
irgen/targets.go
|
||||
irgen/typemap.go
|
||||
irgen/types.go
|
||||
irgen/utils.go
|
||||
irgen/value.go
|
||||
irgen/version.go
|
||||
ssaopt/esc.go
|
||||
)
|
||||
|
||||
install(FILES ${CMAKE_BINARY_DIR}/bin/llgo${CMAKE_EXECUTABLE_SUFFIX}
|
||||
DESTINATION bin
|
||||
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE)
|
||||
|
||||
llvm_add_go_executable(llgo-stage2 llvm.org/llgo/cmd/gllgo
|
||||
DEPENDS libgo ${CMAKE_BINARY_DIR}/bin/llgo${CMAKE_EXECUTABLE_SUFFIX}
|
||||
GOFLAGS "cc=${CMAKE_BINARY_DIR}/bin/clang"
|
||||
"cxx=${CMAKE_BINARY_DIR}/bin/clang++"
|
||||
"llgo=${CMAKE_BINARY_DIR}/bin/llgo${CMAKE_EXECUTABLE_SUFFIX}"
|
||||
)
|
||||
|
||||
llvm_add_go_executable(llgo-stage3 llvm.org/llgo/cmd/gllgo
|
||||
DEPENDS libgo ${CMAKE_BINARY_DIR}/bin/llgo-stage2${CMAKE_EXECUTABLE_SUFFIX}
|
||||
GOFLAGS "cc=${CMAKE_BINARY_DIR}/bin/clang"
|
||||
"cxx=${CMAKE_BINARY_DIR}/bin/clang++"
|
||||
"llgo=${CMAKE_BINARY_DIR}/bin/llgo-stage2${CMAKE_EXECUTABLE_SUFFIX}"
|
||||
)
|
||||
|
||||
llvm_add_go_executable(cc-wrapper llvm.org/llgo/cmd/cc-wrapper DEPENDS
|
||||
cmd/cc-wrapper/main.go
|
||||
)
|
||||
|
||||
function(add_clobber_steps name)
|
||||
ExternalProject_Add_Step(${name} force-reconfigure
|
||||
DEPENDERS configure
|
||||
ALWAYS 1
|
||||
)
|
||||
|
||||
ExternalProject_Add_Step(${name} clobber
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory <BINARY_DIR>
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory <BINARY_DIR>
|
||||
COMMENT "Clobbering ${name} build directory..."
|
||||
DEPENDERS configure
|
||||
DEPENDS ${ARGN}
|
||||
)
|
||||
endfunction()
|
||||
|
||||
processorcount(PROCESSOR_COUNT)
|
||||
|
||||
function(add_libgo_variant suffix cflags gocflags deps exclude_from_all)
|
||||
externalproject_add(libbacktrace${suffix}
|
||||
DEPENDS clang ${deps}
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libbacktrace
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${suffix}/libbacktrace
|
||||
CONFIGURE_COMMAND <SOURCE_DIR>/configure --disable-multilib --enable-host-shared "CC=${CMAKE_BINARY_DIR}/bin/clang ${cflags}"
|
||||
BUILD_COMMAND make -j${PROCESSOR_COUNT}
|
||||
INSTALL_COMMAND ""
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
set_property(TARGET libbacktrace${suffix}
|
||||
PROPERTY EXCLUDE_FROM_ALL ${exclude_from_all})
|
||||
|
||||
add_clobber_steps(libbacktrace${suffix} clang ${deps})
|
||||
|
||||
externalproject_add(libffi${suffix}
|
||||
DEPENDS clang ${deps}
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libffi
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${suffix}/libffi
|
||||
CONFIGURE_COMMAND <SOURCE_DIR>/configure --disable-multilib "CC=${CMAKE_BINARY_DIR}/bin/clang ${cflags}"
|
||||
BUILD_COMMAND make -j${PROCESSOR_COUNT}
|
||||
INSTALL_COMMAND ""
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
)
|
||||
set_property(TARGET libffi${suffix}
|
||||
PROPERTY EXCLUDE_FROM_ALL ${exclude_from_all})
|
||||
|
||||
add_clobber_steps(libffi${suffix} clang ${deps})
|
||||
|
||||
externalproject_add(libgo${suffix}
|
||||
DEPENDS clang llgo cc-wrapper libbacktrace${suffix} libffi${suffix} ${deps}
|
||||
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party/gofrontend/libgo
|
||||
BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${suffix}/libgo
|
||||
INSTALL_DIR ${CMAKE_BINARY_DIR}
|
||||
CONFIGURE_COMMAND <SOURCE_DIR>/configure --disable-multilib --without-libatomic --prefix=<INSTALL_DIR> "CC=env REAL_CC=${CMAKE_BINARY_DIR}/bin/clang@SPACE@${cflags} ${CMAKE_BINARY_DIR}/bin/cc-wrapper" "GOC=${CMAKE_BINARY_DIR}/bin/llgo -no-prefix -fcompilerrt-prefix=${CMAKE_BINARY_DIR} ${gocflags}"
|
||||
BUILD_COMMAND make -j${PROCESSOR_COUNT}
|
||||
LOG_CONFIGURE 1
|
||||
LOG_BUILD 1
|
||||
LOG_INSTALL 1
|
||||
)
|
||||
set_property(TARGET libgo${suffix}
|
||||
PROPERTY EXCLUDE_FROM_ALL ${exclude_from_all})
|
||||
|
||||
add_clobber_steps(libgo${suffix} clang
|
||||
${CMAKE_BINARY_DIR}/bin/llgo${CMAKE_EXECUTABLE_SUFFIX}
|
||||
${CMAKE_BINARY_DIR}/bin/cc-wrapper${CMAKE_EXECUTABLE_SUFFIX})
|
||||
endfunction()
|
||||
|
||||
add_libgo_variant("" "" "" "" FALSE)
|
||||
|
||||
if(TARGET asan)
|
||||
add_libgo_variant("_asan" "-fsanitize=address" "-fsanitize=address" asan TRUE)
|
||||
endif()
|
||||
|
||||
if(TARGET tsan)
|
||||
add_libgo_variant("_tsan" "-fsanitize=thread" "-fsanitize=thread" tsan TRUE)
|
||||
endif()
|
||||
|
||||
if(TARGET msan)
|
||||
add_libgo_variant("_msan" "-fsanitize=memory" "-fsanitize=memory" msan TRUE)
|
||||
endif()
|
||||
|
||||
if(TARGET dfsan)
|
||||
add_libgo_variant("_dfsan" "-fsanitize=dataflow" "-fsanitize=dataflow" dfsan TRUE)
|
||||
endif()
|
||||
|
||||
install(FILES ${CMAKE_BINARY_DIR}/lib/libgo-llgo.a
|
||||
${CMAKE_BINARY_DIR}/lib/libgo-llgo.so
|
||||
${CMAKE_BINARY_DIR}/lib/libgo-llgo.so.6
|
||||
${CMAKE_BINARY_DIR}/lib/libgo-llgo.so.6.0.0
|
||||
${CMAKE_BINARY_DIR}/lib/libgobegin-llgo.a
|
||||
DESTINATION lib)
|
||||
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/lib/go
|
||||
DESTINATION lib)
|
||||
|
||||
add_custom_target(check-libgo
|
||||
COMMAND make -C ${CMAKE_CURRENT_BINARY_DIR}/libgo -j${PROCESSOR_COUNT} check
|
||||
DEPENDS libgo
|
||||
COMMENT "Running libgo tests")
|
||||
|
||||
add_custom_target(check-llgo-bootstrap
|
||||
COMMAND strip -R .note.gnu.build-id -o ${CMAKE_CURRENT_BINARY_DIR}/llgo-stage2.stripped
|
||||
${CMAKE_BINARY_DIR}/bin/llgo-stage2${CMAKE_EXECUTABLE_SUFFIX}
|
||||
COMMAND strip -R .note.gnu.build-id -o ${CMAKE_CURRENT_BINARY_DIR}/llgo-stage3.stripped
|
||||
${CMAKE_BINARY_DIR}/bin/llgo-stage3${CMAKE_EXECUTABLE_SUFFIX}
|
||||
COMMAND cmp ${CMAKE_CURRENT_BINARY_DIR}/llgo-stage2.stripped
|
||||
${CMAKE_CURRENT_BINARY_DIR}/llgo-stage3.stripped
|
||||
DEPENDS llgo-stage2 llgo-stage3
|
||||
COMMENT "Checking llgo bootstrap")
|
||||
|
||||
add_subdirectory(test)
|
93
llgo/LICENSE.TXT
Normal file
93
llgo/LICENSE.TXT
Normal file
@ -0,0 +1,93 @@
|
||||
The llgo distribution, excluding the contents of the 'include' and
|
||||
'third_party' directories, is licensed under the University of Illinois
|
||||
"BSD-Like" license.
|
||||
|
||||
The contents of the 'include' directory are dual licensed under both the
|
||||
University of Illinois "BSD-Like" license and the MIT license. As a user of
|
||||
this code you may choose to use it under either license. As a contributor,
|
||||
you agree to allow your code to be used under both.
|
||||
|
||||
Full text of the relevant licenses is included below.
|
||||
|
||||
==============================================================================
|
||||
LLVM Release License
|
||||
==============================================================================
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2007-2014 University of Illinois at Urbana-Champaign.
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
The LLVM software contains code written by third parties. Such software will
|
||||
have its own individual LICENSE.TXT file in the directory in which it appears.
|
||||
This file will describe the copyrights, license, and restrictions which apply
|
||||
to that code.
|
||||
|
||||
The disclaimer of warranty in the University of Illinois Open Source License
|
||||
applies to all code in the LLVM Distribution, and nothing in any of the
|
||||
other licenses gives permission to use the names of the LLVM Team or the
|
||||
University of Illinois to endorse or promote products derived from this
|
||||
Software.
|
||||
|
||||
The following pieces of software have additional or alternate copyrights,
|
||||
licenses, and/or restrictions:
|
||||
|
||||
Program Directory
|
||||
------- ---------
|
||||
go.tools third_party/go.tools
|
||||
gofrontend third_party/gofrontend
|
||||
|
74
llgo/README.TXT
Normal file
74
llgo/README.TXT
Normal file
@ -0,0 +1,74 @@
|
||||
llgo
|
||||
====
|
||||
|
||||
llgo is a Go (http://golang.org) frontend for LLVM, written in Go.
|
||||
|
||||
llgo is under active development. It compiles and passes most of the
|
||||
standard library test suite and a substantial portion of the gc test suite,
|
||||
but there are some corner cases that are known not to be handled correctly
|
||||
yet. Nevertheless it can compile modestly substantial programs (including
|
||||
itself; it is self hosting on x86-64 Linux).
|
||||
|
||||
Mailing list: https://groups.google.com/d/forum/llgo-dev
|
||||
|
||||
Supported platforms
|
||||
-------------------
|
||||
|
||||
llgo is currently only supported on the x86-64 Linux platform. Contributions
|
||||
that add support for other platforms are welcome.
|
||||
|
||||
There are two components which would need to be ported to new platforms: the
|
||||
compiler and the runtime library. The compiler has little platform-specific
|
||||
code; the most significant is in irgen/cabi.go. The main limiting factor
|
||||
for new platforms is the runtime library in third_party/gofrontend/libgo,
|
||||
which inherits some support for other platforms from the gc compiler's
|
||||
runtime library, but this support tends to be incomplete.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
llgo requires:
|
||||
* Go 1.3 or later.
|
||||
* CMake 2.8.8 or later (to build LLVM).
|
||||
* A modern C++ toolchain (to build LLVM).
|
||||
http://llvm.org/docs/GettingStarted.html#getting-a-modern-host-c-toolchain
|
||||
|
||||
Note that Ubuntu Precise is one Linux distribution which does not package
|
||||
a sufficiently new CMake or C++ toolchain.
|
||||
|
||||
To build and install llgo:
|
||||
|
||||
# Checkout LLVM:
|
||||
svn co http://llvm.org/svn/llvm-project/llvm/trunk /path/to/llvm
|
||||
|
||||
# Checkout Clang:
|
||||
cd /path/to/llvm/tools
|
||||
svn co http://llvm.org/svn/llvm-project/cfe/trunk clang
|
||||
|
||||
# Checkout llgo:
|
||||
svn co http://llvm.org/svn/llvm-project/llgo/trunk llgo
|
||||
|
||||
# Build LLVM, Clang and llgo: (see also http://llvm.org/docs/CMake.html)
|
||||
mkdir /path/to/llvm-build
|
||||
cd /path/to/llvm-build
|
||||
cmake /path/to/llvm -DCMAKE_INSTALL_PREFIX=/path/to/llvm-inst
|
||||
make install
|
||||
|
||||
Running
|
||||
-------
|
||||
|
||||
llgo is the compiler binary. It has a command line interface that is intended
|
||||
to be compatible to a large extent with gccgo. It can be used with the go
|
||||
command shipped with Go 1.4 or later by setting $GCCGO to the path to the
|
||||
llgo executable and supplying the "-compiler gccgo" flags to "go build".
|
||||
|
||||
Contributing
|
||||
------------
|
||||
|
||||
Changes to code outside the third_party directory should be contributed in
|
||||
the normal way by sending patches to <llvm-commits@cs.uiuc.edu>.
|
||||
|
||||
Changes to code in the third_party directory must first be made in the
|
||||
respective upstream project, from which they will be mirrored into the llgo
|
||||
repository. See the script update_third_party.sh for the locations of the
|
||||
upstream projects and details of how the mirroring works.
|
95
llgo/build/context.go
Normal file
95
llgo/build/context.go
Normal file
@ -0,0 +1,95 @@
|
||||
//===- context.go - Build context utilities for llgo ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Build context utilities for llgo.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package build
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"go/build"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
build.Context
|
||||
|
||||
// LLVM triple
|
||||
Triple string
|
||||
}
|
||||
|
||||
// ContextFromTriple returns a new go/build.Context with GOOS and GOARCH
|
||||
// configured from the given triple.
|
||||
func ContextFromTriple(triple string) (*Context, error) {
|
||||
goos, goarch, err := parseTriple(triple)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx := &Context{Context: build.Default, Triple: triple}
|
||||
ctx.GOOS = goos
|
||||
ctx.GOARCH = goarch
|
||||
ctx.BuildTags = append(ctx.BuildTags, "llgo")
|
||||
if triple == "pnacl" {
|
||||
ctx.BuildTags = append(ctx.BuildTags, "pnacl")
|
||||
}
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
func parseTriple(triple string) (goos string, goarch string, err error) {
|
||||
if strings.ToLower(triple) == "pnacl" {
|
||||
return "nacl", "le32", nil
|
||||
}
|
||||
|
||||
type REs struct{ re, out string }
|
||||
// reference: http://llvm.org/docs/doxygen/html/Triple_8cpp_source.html
|
||||
goarchREs := []REs{
|
||||
{"amd64|x86_64", "amd64"},
|
||||
{"i[3-9]86", "386"},
|
||||
{"xscale|((arm|thumb)(v.*)?)", "arm"},
|
||||
}
|
||||
goosREs := []REs{
|
||||
{"linux.*", "linux"},
|
||||
{"(darwin|macosx|ios).*", "darwin"},
|
||||
{"k?freebsd.*", "freebsd"},
|
||||
{"netbsd.*", "netbsd"},
|
||||
{"openbsd.*", "openbsd"},
|
||||
}
|
||||
match := func(list []REs, s string) string {
|
||||
for _, t := range list {
|
||||
if matched, _ := regexp.MatchString(t.re, s); matched {
|
||||
return t.out
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
s := strings.Split(triple, "-")
|
||||
switch l := len(s); l {
|
||||
default:
|
||||
return "", "", errors.New("triple should be made up of 2, 3, or 4 parts.")
|
||||
case 2, 3: // ARCHITECTURE-(VENDOR-)OPERATING_SYSTEM
|
||||
goarch = s[0]
|
||||
goos = s[l-1]
|
||||
case 4: // ARCHITECTURE-VENDOR-OPERATING_SYSTEM-ENVIRONMENT
|
||||
goarch = s[0]
|
||||
goos = s[2]
|
||||
}
|
||||
goarch = match(goarchREs, goarch)
|
||||
if goarch == "" {
|
||||
return "", "", errors.New("unknown architecture in triple")
|
||||
}
|
||||
goos = match(goosREs, goos)
|
||||
if goos == "" {
|
||||
return "", "", errors.New("unknown OS in triple")
|
||||
}
|
||||
return goos, goarch, nil
|
||||
}
|
71
llgo/cmd/cc-wrapper/main.go
Normal file
71
llgo/cmd/cc-wrapper/main.go
Normal file
@ -0,0 +1,71 @@
|
||||
//===- main.go - Clang compiler wrapper for building libgo ----------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is a wrapper for Clang that passes invocations with -fdump-go-spec to
|
||||
// GCC, and rewrites -fplan9-extensions to -fms-extensions. It is intended to
|
||||
// go away once libgo's build no longer uses these flags.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func runproc(name string, argv []string) {
|
||||
path, err := exec.LookPath(name)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "cc-wrapper: could not find %s: %v\n", name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
proc, err := os.StartProcess(path, append([]string{name}, argv...), &os.ProcAttr{
|
||||
Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "cc-wrapper: could not start %s: %v\n", name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
state, err := proc.Wait()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "cc-wrapper: could not wait for %s: %v\n", name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if state.Success() {
|
||||
os.Exit(0)
|
||||
} else {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
newargs := make([]string, len(os.Args)-1)
|
||||
for i, arg := range os.Args[1:] {
|
||||
switch {
|
||||
case strings.HasPrefix(arg, "-fdump-go-spec"):
|
||||
runproc("gcc", os.Args[1:])
|
||||
|
||||
case arg == "-fplan9-extensions":
|
||||
newargs[i] = "-fms-extensions"
|
||||
newargs = append(newargs, "-Wno-microsoft")
|
||||
|
||||
default:
|
||||
newargs[i] = arg
|
||||
}
|
||||
}
|
||||
|
||||
ccargs := strings.Split(os.Getenv("REAL_CC"), "@SPACE@")
|
||||
runproc(ccargs[0], append(ccargs[1:], newargs...))
|
||||
}
|
811
llgo/cmd/gllgo/gllgo.go
Normal file
811
llgo/cmd/gllgo/gllgo.go
Normal file
@ -0,0 +1,811 @@
|
||||
//===- gllgo.go - gccgo-like driver for llgo ------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This is llgo's driver. It has a gccgo-like interface in order to easily
|
||||
// interoperate with the "go" command and the libgo build system.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/scanner"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"llvm.org/llgo/debug"
|
||||
"llvm.org/llgo/irgen"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
func report(err error) {
|
||||
if list, ok := err.(scanner.ErrorList); ok {
|
||||
for _, e := range list {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", e)
|
||||
}
|
||||
} else if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "gllgo: error: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func llvmVersion() string {
|
||||
return strings.Replace(llvm.Version, "svn", "", 1)
|
||||
}
|
||||
|
||||
func displayVersion() {
|
||||
fmt.Printf("llgo version %s (%s)\n\n", llvmVersion(), irgen.GoVersion())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func initCompiler(opts *driverOptions) (*irgen.Compiler, error) {
|
||||
importPaths := make([]string, len(opts.importPaths)+len(opts.libPaths))
|
||||
copy(importPaths, opts.importPaths)
|
||||
copy(importPaths[len(opts.importPaths):], opts.libPaths)
|
||||
if opts.prefix != "" {
|
||||
importPaths = append(importPaths, filepath.Join(opts.prefix, "lib", "go", "llgo-"+llvmVersion()))
|
||||
}
|
||||
copts := irgen.CompilerOptions{
|
||||
TargetTriple: opts.triple,
|
||||
GenerateDebug: opts.generateDebug,
|
||||
DebugPrefixMaps: opts.debugPrefixMaps,
|
||||
DumpSSA: opts.dumpSSA,
|
||||
GccgoPath: opts.gccgoPath,
|
||||
ImportPaths: importPaths,
|
||||
SanitizerAttribute: opts.sanitizer.getAttribute(),
|
||||
}
|
||||
if opts.dumpTrace {
|
||||
copts.Logger = log.New(os.Stderr, "", 0)
|
||||
}
|
||||
return irgen.NewCompiler(copts)
|
||||
}
|
||||
|
||||
type actionKind int
|
||||
|
||||
const (
|
||||
actionAssemble = actionKind(iota)
|
||||
actionCompile
|
||||
actionLink
|
||||
actionPrint
|
||||
)
|
||||
|
||||
type action struct {
|
||||
kind actionKind
|
||||
inputs []string
|
||||
}
|
||||
|
||||
type sanitizerOptions struct {
|
||||
blacklist string
|
||||
crtPrefix string
|
||||
|
||||
address, thread, memory, dataflow bool
|
||||
}
|
||||
|
||||
func (san *sanitizerOptions) resourcePath() string {
|
||||
return filepath.Join(san.crtPrefix, "lib", "clang", llvmVersion())
|
||||
}
|
||||
|
||||
func (san *sanitizerOptions) isPIEDefault() bool {
|
||||
return san.thread || san.memory || san.dataflow
|
||||
}
|
||||
|
||||
func (san *sanitizerOptions) addPasses(mpm, fpm llvm.PassManager) {
|
||||
switch {
|
||||
case san.address:
|
||||
mpm.AddAddressSanitizerModulePass()
|
||||
fpm.AddAddressSanitizerFunctionPass()
|
||||
case san.thread:
|
||||
mpm.AddThreadSanitizerPass()
|
||||
case san.memory:
|
||||
mpm.AddMemorySanitizerPass()
|
||||
case san.dataflow:
|
||||
blacklist := san.blacklist
|
||||
if blacklist == "" {
|
||||
blacklist = filepath.Join(san.resourcePath(), "dfsan_abilist.txt")
|
||||
}
|
||||
mpm.AddDataFlowSanitizerPass(blacklist)
|
||||
}
|
||||
}
|
||||
|
||||
func (san *sanitizerOptions) libPath(triple, sanitizerName string) string {
|
||||
s := strings.Split(triple, "-")
|
||||
return filepath.Join(san.resourcePath(), "lib", s[2], "libclang_rt."+sanitizerName+"-"+s[0]+".a")
|
||||
}
|
||||
|
||||
func (san *sanitizerOptions) addLibsForSanitizer(flags []string, triple, sanitizerName string) []string {
|
||||
return append(flags, san.libPath(triple, sanitizerName),
|
||||
"-Wl,--no-as-needed", "-lpthread", "-lrt", "-lm", "-ldl")
|
||||
}
|
||||
|
||||
func (san *sanitizerOptions) addLibs(triple string, flags []string) []string {
|
||||
switch {
|
||||
case san.address:
|
||||
flags = san.addLibsForSanitizer(flags, triple, "asan")
|
||||
case san.thread:
|
||||
flags = san.addLibsForSanitizer(flags, triple, "tsan")
|
||||
case san.memory:
|
||||
flags = san.addLibsForSanitizer(flags, triple, "msan")
|
||||
case san.dataflow:
|
||||
flags = san.addLibsForSanitizer(flags, triple, "dfsan")
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
|
||||
func (san *sanitizerOptions) getAttribute() llvm.Attribute {
|
||||
switch {
|
||||
case san.address:
|
||||
return llvm.SanitizeAddressAttribute
|
||||
case san.thread:
|
||||
return llvm.SanitizeThreadAttribute
|
||||
case san.memory:
|
||||
return llvm.SanitizeMemoryAttribute
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
type driverOptions struct {
|
||||
actions []action
|
||||
output string
|
||||
|
||||
bprefix string
|
||||
debugPrefixMaps []debug.PrefixMap
|
||||
dumpSSA bool
|
||||
dumpTrace bool
|
||||
emitIR bool
|
||||
gccgoPath string
|
||||
generateDebug bool
|
||||
importPaths []string
|
||||
libPaths []string
|
||||
llvmArgs []string
|
||||
lto bool
|
||||
optLevel int
|
||||
pic bool
|
||||
pieLink bool
|
||||
pkgpath string
|
||||
plugins []string
|
||||
prefix string
|
||||
sanitizer sanitizerOptions
|
||||
sizeLevel int
|
||||
staticLibgcc bool
|
||||
staticLibgo bool
|
||||
staticLink bool
|
||||
triple string
|
||||
}
|
||||
|
||||
func getInstPrefix() (string, error) {
|
||||
path, err := exec.LookPath(os.Args[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
path, err = filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
prefix := filepath.Join(path, "..", "..")
|
||||
return prefix, nil
|
||||
}
|
||||
|
||||
func parseArguments(args []string) (opts driverOptions, err error) {
|
||||
var goInputs, otherInputs []string
|
||||
hasOtherNonFlagInputs := false
|
||||
noPrefix := false
|
||||
actionKind := actionLink
|
||||
opts.triple = llvm.DefaultTargetTriple()
|
||||
|
||||
for len(args) > 0 {
|
||||
consumedArgs := 1
|
||||
|
||||
switch {
|
||||
case !strings.HasPrefix(args[0], "-"):
|
||||
if strings.HasSuffix(args[0], ".go") {
|
||||
goInputs = append(goInputs, args[0])
|
||||
} else {
|
||||
hasOtherNonFlagInputs = true
|
||||
otherInputs = append(otherInputs, args[0])
|
||||
}
|
||||
|
||||
case strings.HasPrefix(args[0], "-Wl,"), strings.HasPrefix(args[0], "-l"), strings.HasPrefix(args[0], "--sysroot="):
|
||||
// TODO(pcc): Handle these correctly.
|
||||
otherInputs = append(otherInputs, args[0])
|
||||
|
||||
case args[0] == "-B":
|
||||
if len(args) == 1 {
|
||||
return opts, errors.New("missing argument after '-B'")
|
||||
}
|
||||
opts.bprefix = args[1]
|
||||
consumedArgs = 2
|
||||
|
||||
case args[0] == "-D":
|
||||
if len(args) == 1 {
|
||||
return opts, errors.New("missing argument after '-D'")
|
||||
}
|
||||
otherInputs = append(otherInputs, args[0], args[1])
|
||||
consumedArgs = 2
|
||||
|
||||
case strings.HasPrefix(args[0], "-D"):
|
||||
otherInputs = append(otherInputs, args[0])
|
||||
|
||||
case args[0] == "-I":
|
||||
if len(args) == 1 {
|
||||
return opts, errors.New("missing argument after '-I'")
|
||||
}
|
||||
opts.importPaths = append(opts.importPaths, args[1])
|
||||
consumedArgs = 2
|
||||
|
||||
case strings.HasPrefix(args[0], "-I"):
|
||||
opts.importPaths = append(opts.importPaths, args[0][2:])
|
||||
|
||||
case args[0] == "-isystem":
|
||||
if len(args) == 1 {
|
||||
return opts, errors.New("missing argument after '-isystem'")
|
||||
}
|
||||
otherInputs = append(otherInputs, args[0], args[1])
|
||||
consumedArgs = 2
|
||||
|
||||
case args[0] == "-L":
|
||||
if len(args) == 1 {
|
||||
return opts, errors.New("missing argument after '-L'")
|
||||
}
|
||||
opts.libPaths = append(opts.libPaths, args[1])
|
||||
consumedArgs = 2
|
||||
|
||||
case strings.HasPrefix(args[0], "-L"):
|
||||
opts.libPaths = append(opts.libPaths, args[0][2:])
|
||||
|
||||
case args[0] == "-O0":
|
||||
opts.optLevel = 0
|
||||
|
||||
case args[0] == "-O1", args[0] == "-O":
|
||||
opts.optLevel = 1
|
||||
|
||||
case args[0] == "-O2":
|
||||
opts.optLevel = 2
|
||||
|
||||
case args[0] == "-Os":
|
||||
opts.optLevel = 2
|
||||
opts.sizeLevel = 1
|
||||
|
||||
case args[0] == "-O3":
|
||||
opts.optLevel = 3
|
||||
|
||||
case args[0] == "-S":
|
||||
actionKind = actionAssemble
|
||||
|
||||
case args[0] == "-c":
|
||||
actionKind = actionCompile
|
||||
|
||||
case strings.HasPrefix(args[0], "-fcompilerrt-prefix="):
|
||||
opts.sanitizer.crtPrefix = args[0][20:]
|
||||
|
||||
case strings.HasPrefix(args[0], "-fdebug-prefix-map="):
|
||||
split := strings.SplitN(args[0][19:], "=", 2)
|
||||
if len(split) < 2 {
|
||||
return opts, fmt.Errorf("argument '%s' must be of form '-fdebug-prefix-map=SOURCE=REPLACEMENT'", args[0])
|
||||
}
|
||||
opts.debugPrefixMaps = append(opts.debugPrefixMaps, debug.PrefixMap{split[0], split[1]})
|
||||
|
||||
case args[0] == "-fdump-ssa":
|
||||
opts.dumpSSA = true
|
||||
|
||||
case args[0] == "-fdump-trace":
|
||||
opts.dumpTrace = true
|
||||
|
||||
case strings.HasPrefix(args[0], "-fgccgo-path="):
|
||||
opts.gccgoPath = args[0][13:]
|
||||
|
||||
case strings.HasPrefix(args[0], "-fgo-pkgpath="):
|
||||
opts.pkgpath = args[0][13:]
|
||||
|
||||
case strings.HasPrefix(args[0], "-fgo-relative-import-path="):
|
||||
// TODO(pcc): Handle this.
|
||||
|
||||
case args[0] == "-fload-plugin":
|
||||
if len(args) == 1 {
|
||||
return opts, errors.New("missing argument after '-fload-plugin'")
|
||||
}
|
||||
opts.plugins = append(opts.plugins, args[1])
|
||||
consumedArgs = 2
|
||||
|
||||
case args[0] == "-fno-toplevel-reorder":
|
||||
// This is a GCC-specific code generation option. Ignore.
|
||||
|
||||
case args[0] == "-emit-llvm":
|
||||
opts.emitIR = true
|
||||
|
||||
case args[0] == "-flto":
|
||||
opts.lto = true
|
||||
|
||||
case args[0] == "-fPIC":
|
||||
opts.pic = true
|
||||
|
||||
case strings.HasPrefix(args[0], "-fsanitize-blacklist="):
|
||||
opts.sanitizer.blacklist = args[0][21:]
|
||||
|
||||
// TODO(pcc): Enforce mutual exclusion between sanitizers.
|
||||
|
||||
case args[0] == "-fsanitize=address":
|
||||
opts.sanitizer.address = true
|
||||
|
||||
case args[0] == "-fsanitize=thread":
|
||||
opts.sanitizer.thread = true
|
||||
|
||||
case args[0] == "-fsanitize=memory":
|
||||
opts.sanitizer.memory = true
|
||||
|
||||
case args[0] == "-fsanitize=dataflow":
|
||||
opts.sanitizer.dataflow = true
|
||||
|
||||
case args[0] == "-g":
|
||||
opts.generateDebug = true
|
||||
|
||||
case args[0] == "-mllvm":
|
||||
if len(args) == 1 {
|
||||
return opts, errors.New("missing argument after '-mllvm'")
|
||||
}
|
||||
opts.llvmArgs = append(opts.llvmArgs, args[1])
|
||||
consumedArgs = 2
|
||||
|
||||
case strings.HasPrefix(args[0], "-m"), args[0] == "-funsafe-math-optimizations", args[0] == "-ffp-contract=off":
|
||||
// TODO(pcc): Handle code generation options.
|
||||
|
||||
case args[0] == "-no-prefix":
|
||||
noPrefix = true
|
||||
|
||||
case args[0] == "-o":
|
||||
if len(args) == 1 {
|
||||
return opts, errors.New("missing argument after '-o'")
|
||||
}
|
||||
opts.output = args[1]
|
||||
consumedArgs = 2
|
||||
|
||||
case args[0] == "-pie":
|
||||
opts.pieLink = true
|
||||
|
||||
case args[0] == "-dumpversion",
|
||||
args[0] == "-print-libgcc-file-name",
|
||||
args[0] == "-print-multi-os-directory",
|
||||
args[0] == "--version":
|
||||
actionKind = actionPrint
|
||||
opts.output = args[0]
|
||||
|
||||
case args[0] == "-static":
|
||||
opts.staticLink = true
|
||||
|
||||
case args[0] == "-static-libgcc":
|
||||
opts.staticLibgcc = true
|
||||
|
||||
case args[0] == "-static-libgo":
|
||||
opts.staticLibgo = true
|
||||
|
||||
default:
|
||||
return opts, fmt.Errorf("unrecognized command line option '%s'", args[0])
|
||||
}
|
||||
|
||||
args = args[consumedArgs:]
|
||||
}
|
||||
|
||||
if actionKind != actionPrint && len(goInputs) == 0 && !hasOtherNonFlagInputs {
|
||||
return opts, errors.New("no input files")
|
||||
}
|
||||
|
||||
if !noPrefix {
|
||||
opts.prefix, err = getInstPrefix()
|
||||
if err != nil {
|
||||
return opts, err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.sanitizer.crtPrefix == "" {
|
||||
opts.sanitizer.crtPrefix = opts.prefix
|
||||
}
|
||||
|
||||
if opts.sanitizer.isPIEDefault() {
|
||||
// This should really only be turning on -fPIE, but this isn't
|
||||
// easy to do from Go, and -fPIC is a superset of it anyway.
|
||||
opts.pic = true
|
||||
opts.pieLink = true
|
||||
}
|
||||
|
||||
switch actionKind {
|
||||
case actionLink:
|
||||
if len(goInputs) != 0 {
|
||||
opts.actions = []action{action{actionCompile, goInputs}}
|
||||
}
|
||||
opts.actions = append(opts.actions, action{actionLink, otherInputs})
|
||||
|
||||
case actionCompile, actionAssemble:
|
||||
if len(goInputs) != 0 {
|
||||
opts.actions = []action{action{actionKind, goInputs}}
|
||||
}
|
||||
|
||||
case actionPrint:
|
||||
opts.actions = []action{action{actionKind, nil}}
|
||||
}
|
||||
|
||||
if opts.output == "" && len(opts.actions) != 0 {
|
||||
switch actionKind {
|
||||
case actionCompile, actionAssemble:
|
||||
base := filepath.Base(goInputs[0])
|
||||
base = base[0 : len(base)-3]
|
||||
if actionKind == actionCompile {
|
||||
opts.output = base + ".o"
|
||||
} else {
|
||||
opts.output = base + ".s"
|
||||
}
|
||||
|
||||
case actionLink:
|
||||
opts.output = "a.out"
|
||||
}
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func runPasses(opts *driverOptions, tm llvm.TargetMachine, m llvm.Module) {
|
||||
fpm := llvm.NewFunctionPassManagerForModule(m)
|
||||
defer fpm.Dispose()
|
||||
|
||||
mpm := llvm.NewPassManager()
|
||||
defer mpm.Dispose()
|
||||
|
||||
pmb := llvm.NewPassManagerBuilder()
|
||||
defer pmb.Dispose()
|
||||
|
||||
pmb.SetOptLevel(opts.optLevel)
|
||||
pmb.SetSizeLevel(opts.sizeLevel)
|
||||
|
||||
target := tm.TargetData()
|
||||
mpm.Add(target)
|
||||
fpm.Add(target)
|
||||
tm.AddAnalysisPasses(mpm)
|
||||
tm.AddAnalysisPasses(fpm)
|
||||
|
||||
mpm.AddVerifierPass()
|
||||
fpm.AddVerifierPass()
|
||||
|
||||
pmb.Populate(mpm)
|
||||
pmb.PopulateFunc(fpm)
|
||||
|
||||
if opts.optLevel == 0 {
|
||||
// Remove references (via the descriptor) to dead functions,
|
||||
// for compatibility with other compilers.
|
||||
mpm.AddGlobalDCEPass()
|
||||
}
|
||||
|
||||
opts.sanitizer.addPasses(mpm, fpm)
|
||||
|
||||
fpm.InitializeFunc()
|
||||
for fn := m.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
fpm.RunFunc(fn)
|
||||
}
|
||||
fpm.FinalizeFunc()
|
||||
|
||||
mpm.Run(m)
|
||||
}
|
||||
|
||||
func getMetadataSectionInlineAsm(name string) string {
|
||||
// ELF: creates a non-allocated excluded section.
|
||||
return ".section \"" + name + "\", \"e\"\n"
|
||||
}
|
||||
|
||||
func getDataInlineAsm(data []byte) string {
|
||||
edata := make([]byte, 0, len(data)*4+10)
|
||||
|
||||
edata = append(edata, ".ascii \""...)
|
||||
for i := range data {
|
||||
switch data[i] {
|
||||
case '\000':
|
||||
edata = append(edata, "\\000"...)
|
||||
continue
|
||||
case '\n':
|
||||
edata = append(edata, "\\n"...)
|
||||
continue
|
||||
case '"', '\\':
|
||||
edata = append(edata, '\\')
|
||||
}
|
||||
edata = append(edata, data[i])
|
||||
}
|
||||
edata = append(edata, "\"\n"...)
|
||||
return string(edata)
|
||||
}
|
||||
|
||||
// Get the lib-relative path to the standard libraries for the given driver
|
||||
// options. This is normally '.' but can vary for cross compilation, LTO,
|
||||
// sanitizers etc.
|
||||
func getVariantDir(opts *driverOptions) string {
|
||||
switch {
|
||||
case opts.lto:
|
||||
return "llvm-lto.0"
|
||||
case opts.sanitizer.address:
|
||||
return "llvm-asan.0"
|
||||
case opts.sanitizer.thread:
|
||||
return "llvm-tsan.0"
|
||||
case opts.sanitizer.memory:
|
||||
return "llvm-msan.0"
|
||||
case opts.sanitizer.dataflow:
|
||||
return "llvm-dfsan.0"
|
||||
default:
|
||||
return "."
|
||||
}
|
||||
}
|
||||
|
||||
func performAction(opts *driverOptions, kind actionKind, inputs []string, output string) error {
|
||||
switch kind {
|
||||
case actionPrint:
|
||||
switch opts.output {
|
||||
case "-dumpversion":
|
||||
fmt.Println("llgo-"+llvmVersion())
|
||||
return nil
|
||||
case "-print-libgcc-file-name":
|
||||
cmd := exec.Command(opts.bprefix+"gcc", "-print-libgcc-file-name")
|
||||
out, err := cmd.CombinedOutput()
|
||||
os.Stdout.Write(out)
|
||||
return err
|
||||
case "-print-multi-os-directory":
|
||||
fmt.Println(getVariantDir(opts))
|
||||
return nil
|
||||
case "--version":
|
||||
displayVersion()
|
||||
return nil
|
||||
default:
|
||||
panic("unexpected print command")
|
||||
}
|
||||
|
||||
case actionCompile, actionAssemble:
|
||||
compiler, err := initCompiler(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
module, err := compiler.Compile(inputs, opts.pkgpath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer module.Dispose()
|
||||
|
||||
target, err := llvm.GetTargetFromTriple(opts.triple)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
optLevel := [...]llvm.CodeGenOptLevel{
|
||||
llvm.CodeGenLevelNone,
|
||||
llvm.CodeGenLevelLess,
|
||||
llvm.CodeGenLevelDefault,
|
||||
llvm.CodeGenLevelAggressive,
|
||||
}[opts.optLevel]
|
||||
|
||||
relocMode := llvm.RelocStatic
|
||||
if opts.pic {
|
||||
relocMode = llvm.RelocPIC
|
||||
}
|
||||
|
||||
tm := target.CreateTargetMachine(opts.triple, "", "", optLevel,
|
||||
relocMode, llvm.CodeModelDefault)
|
||||
defer tm.Dispose()
|
||||
|
||||
runPasses(opts, tm, module.Module)
|
||||
|
||||
var file *os.File
|
||||
if output == "-" {
|
||||
file = os.Stdout
|
||||
} else {
|
||||
file, err = os.Create(output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
}
|
||||
|
||||
switch {
|
||||
case !opts.lto && !opts.emitIR:
|
||||
if module.ExportData != nil {
|
||||
asm := getMetadataSectionInlineAsm(".go_export")
|
||||
asm += getDataInlineAsm(module.ExportData)
|
||||
module.Module.SetInlineAsm(asm)
|
||||
}
|
||||
|
||||
fileType := llvm.AssemblyFile
|
||||
if kind == actionCompile {
|
||||
fileType = llvm.ObjectFile
|
||||
}
|
||||
mb, err := tm.EmitToMemoryBuffer(module.Module, fileType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer mb.Dispose()
|
||||
|
||||
bytes := mb.Bytes()
|
||||
_, err = file.Write(bytes)
|
||||
return err
|
||||
|
||||
case opts.lto:
|
||||
bcmb := llvm.WriteBitcodeToMemoryBuffer(module.Module)
|
||||
defer bcmb.Dispose()
|
||||
|
||||
// This is a bit of a hack. We just want an object file
|
||||
// containing some metadata sections. This might be simpler
|
||||
// if we had bindings for the MC library, but for now we create
|
||||
// a fresh module containing only inline asm that creates the
|
||||
// sections.
|
||||
outmodule := llvm.NewModule("")
|
||||
defer outmodule.Dispose()
|
||||
asm := getMetadataSectionInlineAsm(".llvmbc")
|
||||
asm += getDataInlineAsm(bcmb.Bytes())
|
||||
if module.ExportData != nil {
|
||||
asm += getMetadataSectionInlineAsm(".go_export")
|
||||
asm += getDataInlineAsm(module.ExportData)
|
||||
}
|
||||
outmodule.SetInlineAsm(asm)
|
||||
|
||||
fileType := llvm.AssemblyFile
|
||||
if kind == actionCompile {
|
||||
fileType = llvm.ObjectFile
|
||||
}
|
||||
mb, err := tm.EmitToMemoryBuffer(outmodule, fileType)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer mb.Dispose()
|
||||
|
||||
bytes := mb.Bytes()
|
||||
_, err = file.Write(bytes)
|
||||
return err
|
||||
|
||||
case kind == actionCompile:
|
||||
err := llvm.WriteBitcodeToFile(module.Module, file)
|
||||
return err
|
||||
|
||||
case kind == actionAssemble:
|
||||
_, err := file.WriteString(module.Module.String())
|
||||
return err
|
||||
|
||||
default:
|
||||
panic("unexpected action kind")
|
||||
}
|
||||
|
||||
case actionLink:
|
||||
// TODO(pcc): Teach this to do LTO.
|
||||
args := []string{"-o", output}
|
||||
if opts.pic {
|
||||
args = append(args, "-fPIC")
|
||||
}
|
||||
if opts.pieLink {
|
||||
args = append(args, "-pie")
|
||||
}
|
||||
if opts.staticLink {
|
||||
args = append(args, "-static")
|
||||
}
|
||||
if opts.staticLibgcc {
|
||||
args = append(args, "-static-libgcc")
|
||||
}
|
||||
for _, p := range opts.libPaths {
|
||||
args = append(args, "-L", p)
|
||||
}
|
||||
for _, p := range opts.importPaths {
|
||||
args = append(args, "-I", p)
|
||||
}
|
||||
args = append(args, inputs...)
|
||||
var linkerPath string
|
||||
if opts.gccgoPath == "" {
|
||||
// TODO(pcc): See if we can avoid calling gcc here.
|
||||
// We currently rely on it to find crt*.o and compile
|
||||
// any C source files passed as arguments.
|
||||
linkerPath = opts.bprefix + "gcc"
|
||||
|
||||
if opts.prefix != "" {
|
||||
libdir := filepath.Join(opts.prefix, "lib", getVariantDir(opts))
|
||||
args = append(args, "-L", libdir)
|
||||
if !opts.staticLibgo {
|
||||
args = append(args, "-Wl,-rpath,"+libdir)
|
||||
}
|
||||
}
|
||||
|
||||
args = append(args, "-lgobegin-llgo")
|
||||
if opts.staticLibgo {
|
||||
args = append(args, "-Wl,-Bstatic", "-lgo-llgo", "-Wl,-Bdynamic", "-lpthread", "-lm")
|
||||
} else {
|
||||
args = append(args, "-lgo-llgo")
|
||||
}
|
||||
} else {
|
||||
linkerPath = opts.gccgoPath
|
||||
if opts.staticLibgo {
|
||||
args = append(args, "-static-libgo")
|
||||
}
|
||||
}
|
||||
|
||||
args = opts.sanitizer.addLibs(opts.triple, args)
|
||||
|
||||
cmd := exec.Command(linkerPath, args...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
os.Stderr.Write(out)
|
||||
}
|
||||
return err
|
||||
|
||||
default:
|
||||
panic("unexpected action kind")
|
||||
}
|
||||
}
|
||||
|
||||
func performActions(opts *driverOptions) error {
|
||||
var extraInput string
|
||||
|
||||
for _, plugin := range opts.plugins {
|
||||
err := llvm.LoadLibraryPermanently(plugin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
llvm.ParseCommandLineOptions(append([]string{"llgo"}, opts.llvmArgs...), "llgo (LLVM option parsing)\n")
|
||||
|
||||
for i, action := range opts.actions {
|
||||
var output string
|
||||
if i == len(opts.actions)-1 {
|
||||
output = opts.output
|
||||
} else {
|
||||
tmpfile, err := ioutil.TempFile("", "llgo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
output = tmpfile.Name() + ".o"
|
||||
tmpfile.Close()
|
||||
err = os.Remove(tmpfile.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(output)
|
||||
}
|
||||
|
||||
inputs := action.inputs
|
||||
if extraInput != "" {
|
||||
inputs = append([]string{extraInput}, inputs...)
|
||||
}
|
||||
|
||||
err := performAction(opts, action.kind, inputs, output)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
extraInput = output
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
llvm.InitializeAllTargets()
|
||||
llvm.InitializeAllTargetMCs()
|
||||
llvm.InitializeAllTargetInfos()
|
||||
llvm.InitializeAllAsmParsers()
|
||||
llvm.InitializeAllAsmPrinters()
|
||||
|
||||
opts, err := parseArguments(os.Args[1:])
|
||||
if err != nil {
|
||||
report(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = performActions(&opts)
|
||||
if err != nil {
|
||||
report(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
460
llgo/debug/debug.go
Normal file
460
llgo/debug/debug.go
Normal file
@ -0,0 +1,460 @@
|
||||
//===- debug.go - debug info builder --------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This package builds LLVM debug info from go/* data structures.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package debug
|
||||
|
||||
import (
|
||||
"debug/dwarf"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"llvm.org/llgo/third_party/go.tools/go/ssa"
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llgo/third_party/go.tools/go/types/typeutil"
|
||||
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
const (
|
||||
// non-standard debug metadata tags
|
||||
tagAutoVariable dwarf.Tag = 0x100
|
||||
tagArgVariable dwarf.Tag = 0x101
|
||||
)
|
||||
|
||||
type PrefixMap struct {
|
||||
Source, Replacement string
|
||||
}
|
||||
|
||||
// DIBuilder builds debug metadata for Go programs.
|
||||
type DIBuilder struct {
|
||||
// builder is the current builder; there is one per CU.
|
||||
builder *llvm.DIBuilder
|
||||
module llvm.Module
|
||||
files map[*token.File]llvm.Value
|
||||
cu, fn, lb llvm.Value
|
||||
fnFile string
|
||||
debugScope []llvm.Value
|
||||
sizes types.Sizes
|
||||
fset *token.FileSet
|
||||
prefixMaps []PrefixMap
|
||||
types typeutil.Map
|
||||
voidType llvm.Value
|
||||
}
|
||||
|
||||
// NewDIBuilder creates a new debug information builder.
|
||||
func NewDIBuilder(sizes types.Sizes, module llvm.Module, fset *token.FileSet, prefixMaps []PrefixMap) *DIBuilder {
|
||||
var d DIBuilder
|
||||
d.module = module
|
||||
d.files = make(map[*token.File]llvm.Value)
|
||||
d.sizes = sizes
|
||||
d.fset = fset
|
||||
d.prefixMaps = prefixMaps
|
||||
d.builder = llvm.NewDIBuilder(d.module)
|
||||
d.cu = d.createCompileUnit()
|
||||
return &d
|
||||
}
|
||||
|
||||
// Destroy destroys the DIBuilder.
|
||||
func (d *DIBuilder) Destroy() {
|
||||
d.builder.Destroy()
|
||||
}
|
||||
|
||||
func (d *DIBuilder) scope() llvm.Value {
|
||||
if d.lb.C != nil {
|
||||
return d.lb
|
||||
}
|
||||
if d.fn.C != nil {
|
||||
return d.fn
|
||||
}
|
||||
return d.cu
|
||||
}
|
||||
|
||||
func (d *DIBuilder) remapFilePath(path string) string {
|
||||
for _, pm := range d.prefixMaps {
|
||||
if strings.HasPrefix(path, pm.Source) {
|
||||
return pm.Replacement + path[len(pm.Source):]
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (d *DIBuilder) getFile(file *token.File) llvm.Value {
|
||||
if diFile := d.files[file]; !diFile.IsNil() {
|
||||
return diFile
|
||||
}
|
||||
diFile := d.builder.CreateFile(d.remapFilePath(file.Name()), "")
|
||||
d.files[file] = diFile
|
||||
return diFile
|
||||
}
|
||||
|
||||
// createCompileUnit creates and returns debug metadata for the compile
|
||||
// unit as a whole, using the first file in the file set as a representative
|
||||
// (the choice of file is arbitrary).
|
||||
func (d *DIBuilder) createCompileUnit() llvm.Value {
|
||||
var file *token.File
|
||||
d.fset.Iterate(func(f *token.File) bool {
|
||||
file = f
|
||||
return false
|
||||
})
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic("could not get current directory: " + err.Error())
|
||||
}
|
||||
return d.builder.CreateCompileUnit(llvm.DICompileUnit{
|
||||
Language: llvm.DW_LANG_Go,
|
||||
File: d.remapFilePath(file.Name()),
|
||||
Dir: dir,
|
||||
Producer: "llgo",
|
||||
})
|
||||
}
|
||||
|
||||
// PushFunction creates debug metadata for the specified function,
|
||||
// and pushes it onto the scope stack.
|
||||
func (d *DIBuilder) PushFunction(fnptr llvm.Value, sig *types.Signature, pos token.Pos) {
|
||||
var diFile llvm.Value
|
||||
var line int
|
||||
if file := d.fset.File(pos); file != nil {
|
||||
d.fnFile = file.Name()
|
||||
diFile = d.getFile(file)
|
||||
line = file.Line(pos)
|
||||
}
|
||||
d.fn = d.builder.CreateFunction(d.scope(), llvm.DIFunction{
|
||||
Name: fnptr.Name(), // TODO(axw) unmangled name?
|
||||
LinkageName: fnptr.Name(),
|
||||
File: diFile,
|
||||
Line: line,
|
||||
Type: d.DIType(sig),
|
||||
IsDefinition: true,
|
||||
Function: fnptr,
|
||||
})
|
||||
}
|
||||
|
||||
// PopFunction pops the previously pushed function off the scope stack.
|
||||
func (d *DIBuilder) PopFunction() {
|
||||
d.lb = llvm.Value{nil}
|
||||
d.fn = llvm.Value{nil}
|
||||
d.fnFile = ""
|
||||
}
|
||||
|
||||
// Declare creates an llvm.dbg.declare call for the specified function
|
||||
// parameter or local variable.
|
||||
func (d *DIBuilder) Declare(b llvm.Builder, v ssa.Value, llv llvm.Value, paramIndex int) {
|
||||
tag := tagAutoVariable
|
||||
if paramIndex >= 0 {
|
||||
tag = tagArgVariable
|
||||
}
|
||||
var diFile llvm.Value
|
||||
var line int
|
||||
if file := d.fset.File(v.Pos()); file != nil {
|
||||
line = file.Line(v.Pos())
|
||||
diFile = d.getFile(file)
|
||||
}
|
||||
localVar := d.builder.CreateLocalVariable(d.scope(), llvm.DILocalVariable{
|
||||
Tag: tag,
|
||||
Name: llv.Name(),
|
||||
File: diFile,
|
||||
Line: line,
|
||||
ArgNo: paramIndex + 1,
|
||||
Type: d.DIType(v.Type()),
|
||||
})
|
||||
expr := d.builder.CreateExpression(nil)
|
||||
d.builder.InsertDeclareAtEnd(llv, localVar, expr, b.GetInsertBlock())
|
||||
}
|
||||
|
||||
// Value creates an llvm.dbg.value call for the specified register value.
|
||||
func (d *DIBuilder) Value(b llvm.Builder, v ssa.Value, llv llvm.Value, paramIndex int) {
|
||||
// TODO(axw)
|
||||
}
|
||||
|
||||
// SetLocation sets the current debug location.
|
||||
func (d *DIBuilder) SetLocation(b llvm.Builder, pos token.Pos) {
|
||||
if !pos.IsValid() {
|
||||
return
|
||||
}
|
||||
position := d.fset.Position(pos)
|
||||
d.lb = llvm.Value{nil}
|
||||
if position.Filename != d.fnFile && position.Filename != "" {
|
||||
// This can happen rarely, e.g. in init functions.
|
||||
diFile := d.builder.CreateFile(d.remapFilePath(position.Filename), "")
|
||||
d.lb = d.builder.CreateLexicalBlockFile(d.scope(), diFile, 0)
|
||||
}
|
||||
b.SetCurrentDebugLocation(llvm.MDNode([]llvm.Value{
|
||||
llvm.ConstInt(llvm.Int32Type(), uint64(position.Line), false),
|
||||
llvm.ConstInt(llvm.Int32Type(), uint64(position.Column), false),
|
||||
d.scope(),
|
||||
llvm.Value{},
|
||||
}))
|
||||
}
|
||||
|
||||
// Finalize must be called after all compilation units are translated,
|
||||
// generating the final debug metadata for the module.
|
||||
func (d *DIBuilder) Finalize() {
|
||||
d.module.AddNamedMetadataOperand(
|
||||
"llvm.module.flags",
|
||||
llvm.MDNode([]llvm.Value{
|
||||
llvm.ConstInt(llvm.Int32Type(), 2, false), // Warn on mismatch
|
||||
llvm.MDString("Dwarf Version"),
|
||||
llvm.ConstInt(llvm.Int32Type(), 4, false),
|
||||
}),
|
||||
)
|
||||
d.module.AddNamedMetadataOperand(
|
||||
"llvm.module.flags",
|
||||
llvm.MDNode([]llvm.Value{
|
||||
llvm.ConstInt(llvm.Int32Type(), 1, false), // Error on mismatch
|
||||
llvm.MDString("Debug Info Version"),
|
||||
llvm.ConstInt(llvm.Int32Type(), 1, false),
|
||||
}),
|
||||
)
|
||||
d.builder.Finalize()
|
||||
}
|
||||
|
||||
// DIType maps a Go type to DIType debug metadata value.
|
||||
func (d *DIBuilder) DIType(t types.Type) llvm.Value {
|
||||
return d.typeDebugDescriptor(t, types.TypeString(nil, t))
|
||||
}
|
||||
|
||||
func (d *DIBuilder) typeDebugDescriptor(t types.Type, name string) llvm.Value {
|
||||
// Signature needs to be handled specially, to preprocess
|
||||
// methods, moving the receiver to the parameter list.
|
||||
if t, ok := t.(*types.Signature); ok {
|
||||
return d.descriptorSignature(t, name)
|
||||
}
|
||||
if t == nil {
|
||||
if d.voidType.IsNil() {
|
||||
d.voidType = d.builder.CreateBasicType(llvm.DIBasicType{Name: "void"})
|
||||
}
|
||||
return d.voidType
|
||||
}
|
||||
if dt, ok := d.types.At(t).(llvm.Value); ok {
|
||||
return dt
|
||||
}
|
||||
dt := d.descriptor(t, name)
|
||||
d.types.Set(t, dt)
|
||||
return dt
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptor(t types.Type, name string) llvm.Value {
|
||||
switch t := t.(type) {
|
||||
case *types.Basic:
|
||||
return d.descriptorBasic(t, name)
|
||||
case *types.Pointer:
|
||||
return d.descriptorPointer(t)
|
||||
case *types.Struct:
|
||||
return d.descriptorStruct(t, name)
|
||||
case *types.Named:
|
||||
return d.descriptorNamed(t)
|
||||
case *types.Array:
|
||||
return d.descriptorArray(t, name)
|
||||
case *types.Slice:
|
||||
return d.descriptorSlice(t, name)
|
||||
case *types.Map:
|
||||
return d.descriptorMap(t, name)
|
||||
case *types.Chan:
|
||||
return d.descriptorChan(t, name)
|
||||
case *types.Interface:
|
||||
return d.descriptorInterface(t, name)
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled type: %T", t))
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorBasic(t *types.Basic, name string) llvm.Value {
|
||||
switch t.Kind() {
|
||||
case types.String:
|
||||
return d.typeDebugDescriptor(types.NewStruct([]*types.Var{
|
||||
types.NewVar(0, nil, "ptr", types.NewPointer(types.Typ[types.Uint8])),
|
||||
types.NewVar(0, nil, "len", types.Typ[types.Int]),
|
||||
}, nil), name)
|
||||
case types.UnsafePointer:
|
||||
return d.builder.CreateBasicType(llvm.DIBasicType{
|
||||
Name: name,
|
||||
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
|
||||
AlignInBits: uint64(d.sizes.Alignof(t) * 8),
|
||||
Encoding: llvm.DW_ATE_unsigned,
|
||||
})
|
||||
default:
|
||||
bt := llvm.DIBasicType{
|
||||
Name: t.String(),
|
||||
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
|
||||
AlignInBits: uint64(d.sizes.Alignof(t) * 8),
|
||||
}
|
||||
switch bi := t.Info(); {
|
||||
case bi&types.IsBoolean != 0:
|
||||
bt.Encoding = llvm.DW_ATE_boolean
|
||||
case bi&types.IsUnsigned != 0:
|
||||
bt.Encoding = llvm.DW_ATE_unsigned
|
||||
case bi&types.IsInteger != 0:
|
||||
bt.Encoding = llvm.DW_ATE_signed
|
||||
case bi&types.IsFloat != 0:
|
||||
bt.Encoding = llvm.DW_ATE_float
|
||||
case bi&types.IsComplex != 0:
|
||||
bt.Encoding = llvm.DW_ATE_imaginary_float
|
||||
case bi&types.IsUnsigned != 0:
|
||||
bt.Encoding = llvm.DW_ATE_unsigned
|
||||
default:
|
||||
panic(fmt.Sprintf("unhandled: %#v", t))
|
||||
}
|
||||
return d.builder.CreateBasicType(bt)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorPointer(t *types.Pointer) llvm.Value {
|
||||
return d.builder.CreatePointerType(llvm.DIPointerType{
|
||||
Pointee: d.DIType(t.Elem()),
|
||||
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
|
||||
AlignInBits: uint64(d.sizes.Alignof(t) * 8),
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorStruct(t *types.Struct, name string) llvm.Value {
|
||||
fields := make([]*types.Var, t.NumFields())
|
||||
for i := range fields {
|
||||
fields[i] = t.Field(i)
|
||||
}
|
||||
offsets := d.sizes.Offsetsof(fields)
|
||||
members := make([]llvm.Value, len(fields))
|
||||
for i, f := range fields {
|
||||
// TODO(axw) file/line where member is defined.
|
||||
t := f.Type()
|
||||
members[i] = d.builder.CreateMemberType(d.cu, llvm.DIMemberType{
|
||||
Name: f.Name(),
|
||||
Type: d.DIType(t),
|
||||
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
|
||||
AlignInBits: uint64(d.sizes.Alignof(t) * 8),
|
||||
OffsetInBits: uint64(offsets[i] * 8),
|
||||
})
|
||||
}
|
||||
// TODO(axw) file/line where struct is defined.
|
||||
return d.builder.CreateStructType(d.cu, llvm.DIStructType{
|
||||
Name: name,
|
||||
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
|
||||
AlignInBits: uint64(d.sizes.Alignof(t) * 8),
|
||||
Elements: members,
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorNamed(t *types.Named) llvm.Value {
|
||||
// Create a placeholder for the named type, to terminate cycles.
|
||||
placeholder := llvm.MDNode(nil)
|
||||
d.types.Set(t, placeholder)
|
||||
var diFile llvm.Value
|
||||
var line int
|
||||
if file := d.fset.File(t.Obj().Pos()); file != nil {
|
||||
line = file.Line(t.Obj().Pos())
|
||||
diFile = d.getFile(file)
|
||||
}
|
||||
typedef := d.builder.CreateTypedef(llvm.DITypedef{
|
||||
Type: d.DIType(t.Underlying()),
|
||||
Name: t.Obj().Name(),
|
||||
File: diFile,
|
||||
Line: line,
|
||||
})
|
||||
placeholder.ReplaceAllUsesWith(typedef)
|
||||
return typedef
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorArray(t *types.Array, name string) llvm.Value {
|
||||
return d.builder.CreateArrayType(llvm.DIArrayType{
|
||||
SizeInBits: uint64(d.sizes.Sizeof(t) * 8),
|
||||
AlignInBits: uint64(d.sizes.Alignof(t) * 8),
|
||||
ElementType: d.DIType(t.Elem()),
|
||||
Subscripts: []llvm.DISubrange{{Count: t.Len()}},
|
||||
})
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorSlice(t *types.Slice, name string) llvm.Value {
|
||||
sliceStruct := types.NewStruct([]*types.Var{
|
||||
types.NewVar(0, nil, "ptr", types.NewPointer(t.Elem())),
|
||||
types.NewVar(0, nil, "len", types.Typ[types.Int]),
|
||||
types.NewVar(0, nil, "cap", types.Typ[types.Int]),
|
||||
}, nil)
|
||||
return d.typeDebugDescriptor(sliceStruct, name)
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorMap(t *types.Map, name string) llvm.Value {
|
||||
// FIXME: This should be DW_TAG_pointer_type to __go_map.
|
||||
return d.descriptorBasic(types.Typ[types.Uintptr], name)
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorChan(t *types.Chan, name string) llvm.Value {
|
||||
// FIXME: This should be DW_TAG_pointer_type to __go_channel.
|
||||
return d.descriptorBasic(types.Typ[types.Uintptr], name)
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorInterface(t *types.Interface, name string) llvm.Value {
|
||||
ifaceStruct := types.NewStruct([]*types.Var{
|
||||
types.NewVar(0, nil, "type", types.NewPointer(types.Typ[types.Uint8])),
|
||||
types.NewVar(0, nil, "data", types.NewPointer(types.Typ[types.Uint8])),
|
||||
}, nil)
|
||||
return d.typeDebugDescriptor(ifaceStruct, name)
|
||||
}
|
||||
|
||||
func (d *DIBuilder) descriptorSignature(t *types.Signature, name string) llvm.Value {
|
||||
// If there's a receiver change the receiver to an
|
||||
// additional (first) parameter, and take the value of
|
||||
// the resulting signature instead.
|
||||
if recv := t.Recv(); recv != nil {
|
||||
params := t.Params()
|
||||
paramvars := make([]*types.Var, int(params.Len()+1))
|
||||
paramvars[0] = recv
|
||||
for i := 0; i < int(params.Len()); i++ {
|
||||
paramvars[i+1] = params.At(i)
|
||||
}
|
||||
params = types.NewTuple(paramvars...)
|
||||
t := types.NewSignature(nil, nil, params, t.Results(), t.Variadic())
|
||||
return d.typeDebugDescriptor(t, name)
|
||||
}
|
||||
if dt, ok := d.types.At(t).(llvm.Value); ok {
|
||||
return dt
|
||||
}
|
||||
|
||||
var returnType llvm.Value
|
||||
results := t.Results()
|
||||
switch n := results.Len(); n {
|
||||
case 0:
|
||||
returnType = d.DIType(nil) // void
|
||||
case 1:
|
||||
returnType = d.DIType(results.At(0).Type())
|
||||
default:
|
||||
fields := make([]*types.Var, results.Len())
|
||||
for i := range fields {
|
||||
f := results.At(i)
|
||||
// Structs may not have multiple fields
|
||||
// with the same name, excepting "_".
|
||||
if f.Name() == "" {
|
||||
f = types.NewVar(f.Pos(), f.Pkg(), "_", f.Type())
|
||||
}
|
||||
fields[i] = f
|
||||
}
|
||||
returnType = d.typeDebugDescriptor(types.NewStruct(fields, nil), "")
|
||||
}
|
||||
|
||||
var paramTypes []llvm.Value
|
||||
params := t.Params()
|
||||
if params != nil && params.Len() > 0 {
|
||||
paramTypes = make([]llvm.Value, params.Len()+1)
|
||||
paramTypes[0] = returnType
|
||||
for i := range paramTypes[1:] {
|
||||
paramTypes[i+1] = d.DIType(params.At(i).Type())
|
||||
}
|
||||
} else {
|
||||
paramTypes = []llvm.Value{returnType}
|
||||
}
|
||||
|
||||
// TODO(axw) get position of type definition for File field
|
||||
return d.builder.CreateSubroutineType(llvm.DISubroutineType{
|
||||
Parameters: paramTypes,
|
||||
})
|
||||
}
|
94
llgo/include/dwarf2.h
Normal file
94
llgo/include/dwarf2.h
Normal file
@ -0,0 +1,94 @@
|
||||
//===----------------------------- dwarf2.h -------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
// DWARF constants. Derived from:
|
||||
// - libcxxabi/src/Unwind/dwarf2.h
|
||||
// - DWARF 4 specification
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef DWARF2_H
|
||||
#define DWARF2_H
|
||||
|
||||
enum dwarf_attribute {
|
||||
DW_AT_name = 0x03,
|
||||
DW_AT_stmt_list = 0x10,
|
||||
DW_AT_low_pc = 0x11,
|
||||
DW_AT_high_pc = 0x12,
|
||||
DW_AT_comp_dir = 0x1b,
|
||||
DW_AT_abstract_origin = 0x31,
|
||||
DW_AT_specification = 0x47,
|
||||
DW_AT_ranges = 0x55,
|
||||
DW_AT_call_file = 0x58,
|
||||
DW_AT_call_line = 0x59,
|
||||
DW_AT_linkage_name = 0x6e,
|
||||
DW_AT_MIPS_linkage_name = 0x2007
|
||||
};
|
||||
|
||||
enum dwarf_form {
|
||||
DW_FORM_addr = 0x01,
|
||||
DW_FORM_block2 = 0x03,
|
||||
DW_FORM_block4 = 0x04,
|
||||
DW_FORM_data2 = 0x05,
|
||||
DW_FORM_data4 = 0x06,
|
||||
DW_FORM_data8 = 0x07,
|
||||
DW_FORM_string = 0x08,
|
||||
DW_FORM_block = 0x09,
|
||||
DW_FORM_block1 = 0x0a,
|
||||
DW_FORM_data1 = 0x0b,
|
||||
DW_FORM_flag = 0x0c,
|
||||
DW_FORM_sdata = 0x0d,
|
||||
DW_FORM_strp = 0x0e,
|
||||
DW_FORM_udata = 0x0f,
|
||||
DW_FORM_ref_addr = 0x10,
|
||||
DW_FORM_ref1 = 0x11,
|
||||
DW_FORM_ref2 = 0x12,
|
||||
DW_FORM_ref4 = 0x13,
|
||||
DW_FORM_ref8 = 0x14,
|
||||
DW_FORM_ref_udata = 0x15,
|
||||
DW_FORM_indirect = 0x16,
|
||||
DW_FORM_sec_offset = 0x17,
|
||||
DW_FORM_exprloc = 0x18,
|
||||
DW_FORM_flag_present = 0x19,
|
||||
DW_FORM_ref_sig8 = 0x20,
|
||||
DW_FORM_GNU_addr_index = 0x1f01,
|
||||
DW_FORM_GNU_str_index = 0x1f02,
|
||||
DW_FORM_GNU_ref_alt = 0x1f20,
|
||||
DW_FORM_GNU_strp_alt = 0x1f21
|
||||
};
|
||||
|
||||
enum dwarf_tag {
|
||||
DW_TAG_entry_point = 0x03,
|
||||
DW_TAG_compile_unit = 0x11,
|
||||
DW_TAG_inlined_subroutine = 0x1d,
|
||||
DW_TAG_subprogram = 0x2e
|
||||
};
|
||||
|
||||
enum dwarf_lns {
|
||||
DW_LNS_extended_op = 0x00,
|
||||
DW_LNS_copy = 0x01,
|
||||
DW_LNS_advance_pc = 0x02,
|
||||
DW_LNS_advance_line = 0x03,
|
||||
DW_LNS_set_file = 0x04,
|
||||
DW_LNS_set_column = 0x05,
|
||||
DW_LNS_negate_stmt = 0x06,
|
||||
DW_LNS_set_basic_block = 0x07,
|
||||
DW_LNS_const_add_pc = 0x08,
|
||||
DW_LNS_fixed_advance_pc = 0x09,
|
||||
DW_LNS_set_prologue_end = 0x0a,
|
||||
DW_LNS_set_epilogue_begin = 0x0b,
|
||||
DW_LNS_set_isa = 0x0c
|
||||
};
|
||||
|
||||
enum dwarf_lne {
|
||||
DW_LNE_end_sequence = 0x01,
|
||||
DW_LNE_set_address = 0x02,
|
||||
DW_LNE_define_file = 0x03,
|
||||
DW_LNE_set_discriminator = 0x04
|
||||
};
|
||||
|
||||
#endif // DWARF2_H
|
15
llgo/include/filenames.h
Normal file
15
llgo/include/filenames.h
Normal file
@ -0,0 +1,15 @@
|
||||
//===----------------------------- filenames.h ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef FILENAMES_H
|
||||
#define FILENAMES_H
|
||||
|
||||
#define IS_ABSOLUTE_PATH(path) ((path)[0] == '/')
|
||||
|
||||
#endif
|
201
llgo/include/unwind-pe.h
Normal file
201
llgo/include/unwind-pe.h
Normal file
@ -0,0 +1,201 @@
|
||||
//===----------------------------- unwind-pe.h ----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is dual licensed under the MIT and the University of Illinois Open
|
||||
// Source Licenses. See LICENSE.TXT for details.
|
||||
//
|
||||
// Pointer-Encoding decoder. Derived from:
|
||||
// - libcxxabi/src/Unwind/dwarf2.h
|
||||
// - libcxxabi/src/Unwind/AddressSpace.h
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef UNWIND_PE_H
|
||||
#define UNWIND_PE_H
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
// FSF exception handling Pointer-Encoding constants
|
||||
// Used in CFI augmentation by GCC
|
||||
enum {
|
||||
DW_EH_PE_ptr = 0x00,
|
||||
DW_EH_PE_uleb128 = 0x01,
|
||||
DW_EH_PE_udata2 = 0x02,
|
||||
DW_EH_PE_udata4 = 0x03,
|
||||
DW_EH_PE_udata8 = 0x04,
|
||||
DW_EH_PE_signed = 0x08,
|
||||
DW_EH_PE_sleb128 = 0x09,
|
||||
DW_EH_PE_sdata2 = 0x0A,
|
||||
DW_EH_PE_sdata4 = 0x0B,
|
||||
DW_EH_PE_sdata8 = 0x0C,
|
||||
DW_EH_PE_absptr = 0x00,
|
||||
DW_EH_PE_pcrel = 0x10,
|
||||
DW_EH_PE_textrel = 0x20,
|
||||
DW_EH_PE_datarel = 0x30,
|
||||
DW_EH_PE_funcrel = 0x40,
|
||||
DW_EH_PE_aligned = 0x50,
|
||||
DW_EH_PE_indirect = 0x80,
|
||||
DW_EH_PE_omit = 0xFF
|
||||
};
|
||||
|
||||
/// Read a ULEB128 into a 64-bit word.
|
||||
static uint64_t unw_getULEB128(uintptr_t *addr) {
|
||||
const uint8_t *p = (uint8_t *)*addr;
|
||||
uint64_t result = 0;
|
||||
int bit = 0;
|
||||
do {
|
||||
uint64_t b;
|
||||
|
||||
b = *p & 0x7f;
|
||||
|
||||
if (bit >= 64 || b << bit >> bit != b) {
|
||||
assert(!"malformed uleb128 expression");
|
||||
} else {
|
||||
result |= b << bit;
|
||||
bit += 7;
|
||||
}
|
||||
} while (*p++ >= 0x80);
|
||||
*addr = (uintptr_t) p;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Read a SLEB128 into a 64-bit word.
|
||||
static int64_t unw_getSLEB128(uintptr_t *addr) {
|
||||
const uint8_t *p = (uint8_t *)addr;
|
||||
int64_t result = 0;
|
||||
int bit = 0;
|
||||
uint8_t byte;
|
||||
do {
|
||||
byte = *p++;
|
||||
result |= ((byte & 0x7f) << bit);
|
||||
bit += 7;
|
||||
} while (byte & 0x80);
|
||||
// sign extend negative numbers
|
||||
if ((byte & 0x40) != 0)
|
||||
result |= (-1LL) << bit;
|
||||
*addr = (uintptr_t) p;
|
||||
return result;
|
||||
}
|
||||
|
||||
static uint16_t unw_get16(uintptr_t addr) {
|
||||
uint16_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint32_t unw_get32(uintptr_t addr) {
|
||||
uint32_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static uint64_t unw_get64(uintptr_t addr) {
|
||||
uint64_t val;
|
||||
memcpy(&val, (void *)addr, sizeof(val));
|
||||
return val;
|
||||
}
|
||||
|
||||
static uintptr_t unw_getP(uintptr_t addr) {
|
||||
if (sizeof(uintptr_t) == 8)
|
||||
return unw_get64(addr);
|
||||
else
|
||||
return unw_get32(addr);
|
||||
}
|
||||
|
||||
static const unsigned char *read_uleb128(const unsigned char *p,
|
||||
_uleb128_t *ret) {
|
||||
uintptr_t addr = (uintptr_t)p;
|
||||
*ret = unw_getULEB128(&addr);
|
||||
return (unsigned char *)addr;
|
||||
}
|
||||
|
||||
static const unsigned char *read_encoded_value(struct _Unwind_Context *ctx,
|
||||
unsigned char encoding,
|
||||
const unsigned char *p,
|
||||
_Unwind_Ptr *ret) {
|
||||
uintptr_t addr = (uintptr_t)p;
|
||||
uintptr_t startAddr = addr;
|
||||
uintptr_t result;
|
||||
|
||||
(void)ctx;
|
||||
|
||||
// first get value
|
||||
switch (encoding & 0x0F) {
|
||||
case DW_EH_PE_ptr:
|
||||
result = unw_getP(addr);
|
||||
p += sizeof(uintptr_t);
|
||||
break;
|
||||
case DW_EH_PE_uleb128:
|
||||
result = (uintptr_t)unw_getULEB128(&addr);
|
||||
p = (const unsigned char *)addr;
|
||||
break;
|
||||
case DW_EH_PE_udata2:
|
||||
result = unw_get16(addr);
|
||||
p += 2;
|
||||
break;
|
||||
case DW_EH_PE_udata4:
|
||||
result = unw_get32(addr);
|
||||
p += 4;
|
||||
break;
|
||||
case DW_EH_PE_udata8:
|
||||
result = (uintptr_t)unw_get64(addr);
|
||||
p += 8;
|
||||
break;
|
||||
case DW_EH_PE_sleb128:
|
||||
result = (uintptr_t)unw_getSLEB128(&addr);
|
||||
p = (const unsigned char *)addr;
|
||||
break;
|
||||
case DW_EH_PE_sdata2:
|
||||
// Sign extend from signed 16-bit value.
|
||||
result = (uintptr_t)(int16_t)unw_get16(addr);
|
||||
p += 2;
|
||||
break;
|
||||
case DW_EH_PE_sdata4:
|
||||
// Sign extend from signed 32-bit value.
|
||||
result = (uintptr_t)(int32_t)unw_get32(addr);
|
||||
p += 4;
|
||||
break;
|
||||
case DW_EH_PE_sdata8:
|
||||
result = (uintptr_t)unw_get64(addr);
|
||||
p += 8;
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown pointer encoding");
|
||||
}
|
||||
|
||||
// then add relative offset
|
||||
switch (encoding & 0x70) {
|
||||
case DW_EH_PE_absptr:
|
||||
// do nothing
|
||||
break;
|
||||
case DW_EH_PE_pcrel:
|
||||
result += startAddr;
|
||||
break;
|
||||
case DW_EH_PE_textrel:
|
||||
assert(!"DW_EH_PE_textrel pointer encoding not supported");
|
||||
break;
|
||||
case DW_EH_PE_datarel:
|
||||
assert(!"DW_EH_PE_datarel pointer encoding not supported");
|
||||
break;
|
||||
case DW_EH_PE_funcrel:
|
||||
assert(!"DW_EH_PE_funcrel pointer encoding not supported");
|
||||
break;
|
||||
case DW_EH_PE_aligned:
|
||||
assert(!"DW_EH_PE_aligned pointer encoding not supported");
|
||||
break;
|
||||
default:
|
||||
assert(!"unknown pointer encoding");
|
||||
break;
|
||||
}
|
||||
|
||||
if (encoding & DW_EH_PE_indirect)
|
||||
result = unw_getP(result);
|
||||
|
||||
*ret = result;
|
||||
return p;
|
||||
}
|
||||
|
||||
#endif // UNWIND_PE_H
|
64
llgo/irgen/annotations.go
Normal file
64
llgo/irgen/annotations.go
Normal file
@ -0,0 +1,64 @@
|
||||
//===- annotations.go - annotation processor ------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file converts llgo annotations into attributes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"llvm.org/llgo/third_party/go.tools/go/loader"
|
||||
"llvm.org/llgo/third_party/go.tools/go/ssa"
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// processAnnotations takes an *ssa.Package and a
|
||||
// *importer.PackageInfo, and processes all of the
|
||||
// llgo source annotations attached to each top-level
|
||||
// function and global variable.
|
||||
func (c *compiler) processAnnotations(u *unit, pkginfo *loader.PackageInfo) {
|
||||
members := make(map[types.Object]llvm.Value, len(u.globals))
|
||||
for k, v := range u.globals {
|
||||
members[k.(ssa.Member).Object()] = v
|
||||
}
|
||||
applyAttributes := func(attrs []Attribute, idents ...*ast.Ident) {
|
||||
if len(attrs) == 0 {
|
||||
return
|
||||
}
|
||||
for _, ident := range idents {
|
||||
if v := members[pkginfo.ObjectOf(ident)]; !v.IsNil() {
|
||||
for _, attr := range attrs {
|
||||
attr.Apply(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, f := range pkginfo.Files {
|
||||
for _, decl := range f.Decls {
|
||||
switch decl := decl.(type) {
|
||||
case *ast.FuncDecl:
|
||||
attrs := parseAttributes(decl.Doc)
|
||||
applyAttributes(attrs, decl.Name)
|
||||
case *ast.GenDecl:
|
||||
if decl.Tok != token.VAR {
|
||||
continue
|
||||
}
|
||||
for _, spec := range decl.Specs {
|
||||
varspec := spec.(*ast.ValueSpec)
|
||||
attrs := parseAttributes(decl.Doc)
|
||||
applyAttributes(attrs, varspec.Names...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
177
llgo/irgen/attribute.go
Normal file
177
llgo/irgen/attribute.go
Normal file
@ -0,0 +1,177 @@
|
||||
//===- attribute.go - attribute processor ---------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file processes llgo and //extern attributes.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const AttributeCommentPrefix = "#llgo "
|
||||
|
||||
// Attribute represents an attribute associated with a
|
||||
// global variable or function.
|
||||
type Attribute interface {
|
||||
Apply(llvm.Value)
|
||||
}
|
||||
|
||||
// parseAttribute parses zero or more #llgo comment attributes associated with
|
||||
// a global variable or function. The comment group provided will be processed
|
||||
// one line at a time using parseAttribute.
|
||||
func parseAttributes(doc *ast.CommentGroup) []Attribute {
|
||||
var attributes []Attribute
|
||||
if doc == nil {
|
||||
return attributes
|
||||
}
|
||||
for _, comment := range doc.List {
|
||||
if strings.HasPrefix(comment.Text, "//extern ") {
|
||||
nameattr := nameAttribute(strings.TrimSpace(comment.Text[9:]))
|
||||
attributes = append(attributes, nameattr)
|
||||
continue
|
||||
}
|
||||
text := comment.Text[2:]
|
||||
if strings.HasPrefix(comment.Text, "/*") {
|
||||
text = text[:len(text)-2]
|
||||
}
|
||||
attr := parseAttribute(strings.TrimSpace(text))
|
||||
if attr != nil {
|
||||
attributes = append(attributes, attr)
|
||||
}
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
// parseAttribute parses a single #llgo comment attribute associated with
|
||||
// a global variable or function. The string provided will be parsed
|
||||
// if it begins with AttributeCommentPrefix, otherwise nil is returned.
|
||||
func parseAttribute(line string) Attribute {
|
||||
if !strings.HasPrefix(line, AttributeCommentPrefix) {
|
||||
return nil
|
||||
}
|
||||
line = strings.TrimSpace(line[len(AttributeCommentPrefix):])
|
||||
colon := strings.IndexRune(line, ':')
|
||||
var key, value string
|
||||
if colon == -1 {
|
||||
key = line
|
||||
} else {
|
||||
key, value = line[:colon], line[colon+1:]
|
||||
}
|
||||
switch key {
|
||||
case "linkage":
|
||||
return parseLinkageAttribute(value)
|
||||
case "name":
|
||||
return nameAttribute(strings.TrimSpace(value))
|
||||
case "attr":
|
||||
return parseLLVMAttribute(strings.TrimSpace(value))
|
||||
case "thread_local":
|
||||
return tlsAttribute{}
|
||||
default:
|
||||
// FIXME decide what to do here. return error? log warning?
|
||||
panic("unknown attribute key: " + key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type linkageAttribute llvm.Linkage
|
||||
|
||||
func (a linkageAttribute) Apply(v llvm.Value) {
|
||||
v.SetLinkage(llvm.Linkage(a))
|
||||
}
|
||||
|
||||
func parseLinkageAttribute(value string) linkageAttribute {
|
||||
var result linkageAttribute
|
||||
value = strings.Replace(value, ",", " ", -1)
|
||||
for _, field := range strings.Fields(value) {
|
||||
switch strings.ToLower(field) {
|
||||
case "private":
|
||||
result |= linkageAttribute(llvm.PrivateLinkage)
|
||||
case "internal":
|
||||
result |= linkageAttribute(llvm.InternalLinkage)
|
||||
case "available_externally":
|
||||
result |= linkageAttribute(llvm.AvailableExternallyLinkage)
|
||||
case "linkonce":
|
||||
result |= linkageAttribute(llvm.LinkOnceAnyLinkage)
|
||||
case "common":
|
||||
result |= linkageAttribute(llvm.CommonLinkage)
|
||||
case "weak":
|
||||
result |= linkageAttribute(llvm.WeakAnyLinkage)
|
||||
case "appending":
|
||||
result |= linkageAttribute(llvm.AppendingLinkage)
|
||||
case "extern_weak":
|
||||
result |= linkageAttribute(llvm.ExternalWeakLinkage)
|
||||
case "linkonce_odr":
|
||||
result |= linkageAttribute(llvm.LinkOnceODRLinkage)
|
||||
case "weak_odr":
|
||||
result |= linkageAttribute(llvm.WeakODRLinkage)
|
||||
case "external":
|
||||
result |= linkageAttribute(llvm.ExternalLinkage)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type nameAttribute string
|
||||
|
||||
func (a nameAttribute) Apply(v llvm.Value) {
|
||||
if !v.IsAFunction().IsNil() {
|
||||
name := string(a)
|
||||
curr := v.GlobalParent().NamedFunction(name)
|
||||
if !curr.IsNil() && curr != v {
|
||||
if curr.BasicBlocksCount() != 0 {
|
||||
panic(fmt.Errorf("Want to take the name %s from a function that has a body!", name))
|
||||
}
|
||||
curr.SetName(name + "_llgo_replaced")
|
||||
curr.ReplaceAllUsesWith(llvm.ConstBitCast(v, curr.Type()))
|
||||
}
|
||||
v.SetName(name)
|
||||
} else {
|
||||
v.SetName(string(a))
|
||||
}
|
||||
}
|
||||
|
||||
func parseLLVMAttribute(value string) llvmAttribute {
|
||||
var result llvmAttribute
|
||||
value = strings.Replace(value, ",", " ", -1)
|
||||
for _, field := range strings.Fields(value) {
|
||||
switch strings.ToLower(field) {
|
||||
case "noreturn":
|
||||
result |= llvmAttribute(llvm.NoReturnAttribute)
|
||||
case "nounwind":
|
||||
result |= llvmAttribute(llvm.NoUnwindAttribute)
|
||||
case "noinline":
|
||||
result |= llvmAttribute(llvm.NoInlineAttribute)
|
||||
case "alwaysinline":
|
||||
result |= llvmAttribute(llvm.AlwaysInlineAttribute)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
type llvmAttribute llvm.Attribute
|
||||
|
||||
func (a llvmAttribute) Apply(v llvm.Value) {
|
||||
if !v.IsAFunction().IsNil() {
|
||||
v.AddFunctionAttr(llvm.Attribute(a))
|
||||
} else {
|
||||
v.AddAttribute(llvm.Attribute(a))
|
||||
}
|
||||
}
|
||||
|
||||
type tlsAttribute struct{}
|
||||
|
||||
func (tlsAttribute) Apply(v llvm.Value) {
|
||||
v.SetThreadLocal(true)
|
||||
}
|
116
llgo/irgen/builtins.go
Normal file
116
llgo/irgen/builtins.go
Normal file
@ -0,0 +1,116 @@
|
||||
//===- builtins.go - IR generation for builtins ---------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for the built-in functions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
func (fr *frame) callCap(arg *govalue) *govalue {
|
||||
var v llvm.Value
|
||||
switch typ := arg.Type().Underlying().(type) {
|
||||
case *types.Array:
|
||||
v = llvm.ConstInt(fr.llvmtypes.inttype, uint64(typ.Len()), false)
|
||||
case *types.Pointer:
|
||||
atyp := typ.Elem().Underlying().(*types.Array)
|
||||
v = llvm.ConstInt(fr.llvmtypes.inttype, uint64(atyp.Len()), false)
|
||||
case *types.Slice:
|
||||
v = fr.builder.CreateExtractValue(arg.value, 2, "")
|
||||
case *types.Chan:
|
||||
v = fr.runtime.chanCap.call(fr, arg.value)[0]
|
||||
}
|
||||
return newValue(v, types.Typ[types.Int])
|
||||
}
|
||||
|
||||
func (fr *frame) callLen(arg *govalue) *govalue {
|
||||
var lenvalue llvm.Value
|
||||
switch typ := arg.Type().Underlying().(type) {
|
||||
case *types.Array:
|
||||
lenvalue = llvm.ConstInt(fr.llvmtypes.inttype, uint64(typ.Len()), false)
|
||||
case *types.Pointer:
|
||||
atyp := typ.Elem().Underlying().(*types.Array)
|
||||
lenvalue = llvm.ConstInt(fr.llvmtypes.inttype, uint64(atyp.Len()), false)
|
||||
case *types.Slice:
|
||||
lenvalue = fr.builder.CreateExtractValue(arg.value, 1, "")
|
||||
case *types.Map:
|
||||
lenvalue = fr.runtime.mapLen.call(fr, arg.value)[0]
|
||||
case *types.Basic:
|
||||
if isString(typ) {
|
||||
lenvalue = fr.builder.CreateExtractValue(arg.value, 1, "")
|
||||
}
|
||||
case *types.Chan:
|
||||
lenvalue = fr.runtime.chanLen.call(fr, arg.value)[0]
|
||||
}
|
||||
return newValue(lenvalue, types.Typ[types.Int])
|
||||
}
|
||||
|
||||
// callAppend takes two slices of the same type, and yields
|
||||
// the result of appending the second to the first.
|
||||
func (fr *frame) callAppend(a, b *govalue) *govalue {
|
||||
bptr := fr.builder.CreateExtractValue(b.value, 0, "")
|
||||
blen := fr.builder.CreateExtractValue(b.value, 1, "")
|
||||
elemsizeInt64 := fr.types.Sizeof(a.Type().Underlying().(*types.Slice).Elem())
|
||||
elemsize := llvm.ConstInt(fr.target.IntPtrType(), uint64(elemsizeInt64), false)
|
||||
result := fr.runtime.append.call(fr, a.value, bptr, blen, elemsize)[0]
|
||||
return newValue(result, a.Type())
|
||||
}
|
||||
|
||||
// callCopy takes two slices a and b of the same type, and
|
||||
// yields the result of calling "copy(a, b)".
|
||||
func (fr *frame) callCopy(dest, source *govalue) *govalue {
|
||||
aptr := fr.builder.CreateExtractValue(dest.value, 0, "")
|
||||
alen := fr.builder.CreateExtractValue(dest.value, 1, "")
|
||||
bptr := fr.builder.CreateExtractValue(source.value, 0, "")
|
||||
blen := fr.builder.CreateExtractValue(source.value, 1, "")
|
||||
aless := fr.builder.CreateICmp(llvm.IntULT, alen, blen, "")
|
||||
minlen := fr.builder.CreateSelect(aless, alen, blen, "")
|
||||
elemsizeInt64 := fr.types.Sizeof(dest.Type().Underlying().(*types.Slice).Elem())
|
||||
elemsize := llvm.ConstInt(fr.types.inttype, uint64(elemsizeInt64), false)
|
||||
bytes := fr.builder.CreateMul(minlen, elemsize, "")
|
||||
fr.runtime.copy.call(fr, aptr, bptr, bytes)
|
||||
return newValue(minlen, types.Typ[types.Int])
|
||||
}
|
||||
|
||||
func (fr *frame) callRecover(isDeferredRecover bool) *govalue {
|
||||
startbb := fr.builder.GetInsertBlock()
|
||||
recoverbb := llvm.AddBasicBlock(fr.function, "")
|
||||
contbb := llvm.AddBasicBlock(fr.function, "")
|
||||
canRecover := fr.builder.CreateTrunc(fr.canRecover, llvm.Int1Type(), "")
|
||||
fr.builder.CreateCondBr(canRecover, recoverbb, contbb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(recoverbb)
|
||||
var recovered llvm.Value
|
||||
if isDeferredRecover {
|
||||
recovered = fr.runtime.deferredRecover.call(fr)[0]
|
||||
} else {
|
||||
recovered = fr.runtime.recover.call(fr)[0]
|
||||
}
|
||||
recoverbb = fr.builder.GetInsertBlock()
|
||||
fr.builder.CreateBr(contbb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(contbb)
|
||||
eface := types.NewInterface(nil, nil)
|
||||
llv := fr.builder.CreatePHI(fr.types.ToLLVM(eface), "")
|
||||
llv.AddIncoming(
|
||||
[]llvm.Value{llvm.ConstNull(llv.Type()), recovered},
|
||||
[]llvm.BasicBlock{startbb, recoverbb},
|
||||
)
|
||||
return newValue(llv, eface)
|
||||
}
|
||||
|
||||
func (fr *frame) callPanic(arg *govalue) {
|
||||
fr.runtime.panic.call(fr, arg.value)
|
||||
fr.builder.CreateUnreachable()
|
||||
}
|
665
llgo/irgen/cabi.go
Normal file
665
llgo/irgen/cabi.go
Normal file
@ -0,0 +1,665 @@
|
||||
//===- cabi.go - C ABI abstraction layer ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements an abstraction layer for the platform's C ABI (currently
|
||||
// supports only Linux/x86_64).
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
type abiArgInfo int
|
||||
|
||||
const (
|
||||
AIK_Direct = abiArgInfo(iota)
|
||||
AIK_Indirect
|
||||
)
|
||||
|
||||
type backendType interface {
|
||||
ToLLVM(llvm.Context) llvm.Type
|
||||
}
|
||||
|
||||
type ptrBType struct {
|
||||
}
|
||||
|
||||
func (t ptrBType) ToLLVM(c llvm.Context) llvm.Type {
|
||||
return llvm.PointerType(c.Int8Type(), 0)
|
||||
}
|
||||
|
||||
type intBType struct {
|
||||
width int
|
||||
signed bool
|
||||
}
|
||||
|
||||
func (t intBType) ToLLVM(c llvm.Context) llvm.Type {
|
||||
return c.IntType(t.width * 8)
|
||||
}
|
||||
|
||||
type floatBType struct {
|
||||
isDouble bool
|
||||
}
|
||||
|
||||
func (t floatBType) ToLLVM(c llvm.Context) llvm.Type {
|
||||
if t.isDouble {
|
||||
return c.DoubleType()
|
||||
} else {
|
||||
return c.FloatType()
|
||||
}
|
||||
}
|
||||
|
||||
type structBType struct {
|
||||
fields []backendType
|
||||
}
|
||||
|
||||
func (t structBType) ToLLVM(c llvm.Context) llvm.Type {
|
||||
var lfields []llvm.Type
|
||||
for _, f := range t.fields {
|
||||
lfields = append(lfields, f.ToLLVM(c))
|
||||
}
|
||||
return c.StructType(lfields, false)
|
||||
}
|
||||
|
||||
type arrayBType struct {
|
||||
length uint64
|
||||
elem backendType
|
||||
}
|
||||
|
||||
func (t arrayBType) ToLLVM(c llvm.Context) llvm.Type {
|
||||
return llvm.ArrayType(t.elem.ToLLVM(c), int(t.length))
|
||||
}
|
||||
|
||||
// align returns the smallest y >= x such that y % a == 0.
|
||||
func align(x, a int64) int64 {
|
||||
y := x + a - 1
|
||||
return y - y%a
|
||||
}
|
||||
|
||||
func (tm *llvmTypeMap) sizeofStruct(fields ...types.Type) int64 {
|
||||
var o int64
|
||||
for _, f := range fields {
|
||||
a := tm.Alignof(f)
|
||||
o = align(o, a)
|
||||
o += tm.Sizeof(f)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// This decides whether the x86_64 classification algorithm produces MEMORY for
|
||||
// the given type. Given the subset of types that Go supports, this is exactly
|
||||
// equivalent to testing the type's size. See in particular the first step of
|
||||
// the algorithm and its footnote.
|
||||
func (tm *llvmTypeMap) classify(t ...types.Type) abiArgInfo {
|
||||
if tm.sizeofStruct(t...) > 16 {
|
||||
return AIK_Indirect
|
||||
}
|
||||
return AIK_Direct
|
||||
}
|
||||
|
||||
func (tm *llvmTypeMap) sliceBackendType() backendType {
|
||||
i8ptr := &ptrBType{}
|
||||
uintptr := &intBType{tm.target.PointerSize(), false}
|
||||
return &structBType{[]backendType{i8ptr, uintptr, uintptr}}
|
||||
}
|
||||
|
||||
func (tm *llvmTypeMap) getBackendType(t types.Type) backendType {
|
||||
switch t := t.(type) {
|
||||
case *types.Named:
|
||||
return tm.getBackendType(t.Underlying())
|
||||
|
||||
case *types.Basic:
|
||||
switch t.Kind() {
|
||||
case types.Bool, types.Uint8:
|
||||
return &intBType{1, false}
|
||||
case types.Int8:
|
||||
return &intBType{1, true}
|
||||
case types.Uint16:
|
||||
return &intBType{2, false}
|
||||
case types.Int16:
|
||||
return &intBType{2, true}
|
||||
case types.Uint32:
|
||||
return &intBType{4, false}
|
||||
case types.Int32:
|
||||
return &intBType{4, true}
|
||||
case types.Uint64:
|
||||
return &intBType{8, false}
|
||||
case types.Int64:
|
||||
return &intBType{8, true}
|
||||
case types.Uint, types.Uintptr:
|
||||
return &intBType{tm.target.PointerSize(), false}
|
||||
case types.Int:
|
||||
return &intBType{tm.target.PointerSize(), true}
|
||||
case types.Float32:
|
||||
return &floatBType{false}
|
||||
case types.Float64:
|
||||
return &floatBType{true}
|
||||
case types.UnsafePointer:
|
||||
return &ptrBType{}
|
||||
case types.Complex64:
|
||||
f32 := &floatBType{false}
|
||||
return &structBType{[]backendType{f32, f32}}
|
||||
case types.Complex128:
|
||||
f64 := &floatBType{true}
|
||||
return &structBType{[]backendType{f64, f64}}
|
||||
case types.String:
|
||||
return &structBType{[]backendType{&ptrBType{}, &intBType{tm.target.PointerSize(), false}}}
|
||||
}
|
||||
|
||||
case *types.Struct:
|
||||
var fields []backendType
|
||||
for i := 0; i != t.NumFields(); i++ {
|
||||
f := t.Field(i)
|
||||
fields = append(fields, tm.getBackendType(f.Type()))
|
||||
}
|
||||
return &structBType{fields}
|
||||
|
||||
case *types.Pointer, *types.Signature, *types.Map, *types.Chan:
|
||||
return &ptrBType{}
|
||||
|
||||
case *types.Interface:
|
||||
i8ptr := &ptrBType{}
|
||||
return &structBType{[]backendType{i8ptr, i8ptr}}
|
||||
|
||||
case *types.Slice:
|
||||
return tm.sliceBackendType()
|
||||
|
||||
case *types.Array:
|
||||
return &arrayBType{uint64(t.Len()), tm.getBackendType(t.Elem())}
|
||||
}
|
||||
|
||||
panic("unhandled type: " + t.String())
|
||||
}
|
||||
|
||||
type offsetedType struct {
|
||||
typ backendType
|
||||
offset uint64
|
||||
}
|
||||
|
||||
func (tm *llvmTypeMap) getBackendOffsets(bt backendType) (offsets []offsetedType) {
|
||||
switch bt := bt.(type) {
|
||||
case *structBType:
|
||||
t := bt.ToLLVM(tm.ctx)
|
||||
for i, f := range bt.fields {
|
||||
offset := tm.target.ElementOffset(t, i)
|
||||
fieldOffsets := tm.getBackendOffsets(f)
|
||||
for _, fo := range fieldOffsets {
|
||||
offsets = append(offsets, offsetedType{fo.typ, offset + fo.offset})
|
||||
}
|
||||
}
|
||||
|
||||
case *arrayBType:
|
||||
size := tm.target.TypeAllocSize(bt.elem.ToLLVM(tm.ctx))
|
||||
fieldOffsets := tm.getBackendOffsets(bt.elem)
|
||||
for i := uint64(0); i != bt.length; i++ {
|
||||
for _, fo := range fieldOffsets {
|
||||
offsets = append(offsets, offsetedType{fo.typ, i*size + fo.offset})
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
offsets = []offsetedType{offsetedType{bt, 0}}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (tm *llvmTypeMap) classifyEightbyte(offsets []offsetedType, numInt, numSSE *int) llvm.Type {
|
||||
if len(offsets) == 1 {
|
||||
if _, ok := offsets[0].typ.(*floatBType); ok {
|
||||
*numSSE++
|
||||
} else {
|
||||
*numInt++
|
||||
}
|
||||
return offsets[0].typ.ToLLVM(tm.ctx)
|
||||
}
|
||||
// This implements classification for the basic types and step 4 of the
|
||||
// classification algorithm. At this point, the only two possible
|
||||
// classifications are SSE (floats) and INTEGER (everything else).
|
||||
sse := true
|
||||
for _, ot := range offsets {
|
||||
if _, ok := ot.typ.(*floatBType); !ok {
|
||||
sse = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if sse {
|
||||
// This can only be (float, float), which uses an SSE vector.
|
||||
*numSSE++
|
||||
return llvm.VectorType(tm.ctx.FloatType(), 2)
|
||||
} else {
|
||||
*numInt++
|
||||
width := offsets[len(offsets)-1].offset + tm.target.TypeAllocSize(offsets[len(offsets)-1].typ.ToLLVM(tm.ctx)) - offsets[0].offset
|
||||
return tm.ctx.IntType(int(width) * 8)
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *llvmTypeMap) expandType(argTypes []llvm.Type, argAttrs []llvm.Attribute, bt backendType) ([]llvm.Type, []llvm.Attribute, int, int) {
|
||||
var numInt, numSSE int
|
||||
var argAttr llvm.Attribute
|
||||
|
||||
switch bt := bt.(type) {
|
||||
case *structBType, *arrayBType:
|
||||
bo := tm.getBackendOffsets(bt)
|
||||
sp := 0
|
||||
for sp != len(bo) && bo[sp].offset < 8 {
|
||||
sp++
|
||||
}
|
||||
eb1 := bo[0:sp]
|
||||
eb2 := bo[sp:]
|
||||
if len(eb2) > 0 {
|
||||
argTypes = append(argTypes, tm.classifyEightbyte(eb1, &numInt, &numSSE), tm.classifyEightbyte(eb2, &numInt, &numSSE))
|
||||
argAttrs = append(argAttrs, 0, 0)
|
||||
} else {
|
||||
argTypes = append(argTypes, tm.classifyEightbyte(eb1, &numInt, &numSSE))
|
||||
argAttrs = append(argAttrs, 0)
|
||||
}
|
||||
|
||||
return argTypes, argAttrs, numInt, numSSE
|
||||
|
||||
case *intBType:
|
||||
if bt.width < 4 {
|
||||
if bt.signed {
|
||||
argAttr = llvm.SExtAttribute
|
||||
} else {
|
||||
argAttr = llvm.ZExtAttribute
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
argTypes = append(argTypes, tm.classifyEightbyte([]offsetedType{{bt, 0}}, &numInt, &numSSE))
|
||||
argAttrs = append(argAttrs, argAttr)
|
||||
|
||||
return argTypes, argAttrs, numInt, numSSE
|
||||
}
|
||||
|
||||
type argInfo interface {
|
||||
// Emit instructions to builder to ABI encode val and store result to args.
|
||||
encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, args []llvm.Value, val llvm.Value)
|
||||
|
||||
// Emit instructions to builder to ABI decode and return the resulting Value.
|
||||
decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder) llvm.Value
|
||||
}
|
||||
|
||||
type retInfo interface {
|
||||
// Prepare args to receive a value. allocaBuilder refers to a builder in the entry block.
|
||||
prepare(ctx llvm.Context, allocaBuilder llvm.Builder, args []llvm.Value)
|
||||
|
||||
// Emit instructions to builder to ABI decode the return value(s), if any. call is the
|
||||
// call instruction. Must be called after prepare().
|
||||
decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, call llvm.Value) []llvm.Value
|
||||
|
||||
// Emit instructions to builder to ABI encode the return value(s), if any, and return.
|
||||
encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, vals []llvm.Value)
|
||||
}
|
||||
|
||||
type directArgInfo struct {
|
||||
argOffset int
|
||||
argTypes []llvm.Type
|
||||
valType llvm.Type
|
||||
}
|
||||
|
||||
func directEncode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, argTypes []llvm.Type, args []llvm.Value, val llvm.Value) {
|
||||
valType := val.Type()
|
||||
|
||||
switch len(argTypes) {
|
||||
case 0:
|
||||
// do nothing
|
||||
|
||||
case 1:
|
||||
if argTypes[0].C == valType.C {
|
||||
args[0] = val
|
||||
return
|
||||
}
|
||||
alloca := allocaBuilder.CreateAlloca(valType, "")
|
||||
bitcast := builder.CreateBitCast(alloca, llvm.PointerType(argTypes[0], 0), "")
|
||||
builder.CreateStore(val, alloca)
|
||||
args[0] = builder.CreateLoad(bitcast, "")
|
||||
|
||||
case 2:
|
||||
encodeType := llvm.StructType(argTypes, false)
|
||||
alloca := allocaBuilder.CreateAlloca(valType, "")
|
||||
bitcast := builder.CreateBitCast(alloca, llvm.PointerType(encodeType, 0), "")
|
||||
builder.CreateStore(val, alloca)
|
||||
args[0] = builder.CreateLoad(builder.CreateStructGEP(bitcast, 0, ""), "")
|
||||
args[1] = builder.CreateLoad(builder.CreateStructGEP(bitcast, 1, ""), "")
|
||||
|
||||
default:
|
||||
panic("unexpected argTypes size")
|
||||
}
|
||||
}
|
||||
|
||||
func (ai *directArgInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, args []llvm.Value, val llvm.Value) {
|
||||
directEncode(ctx, allocaBuilder, builder, ai.argTypes, args[ai.argOffset:ai.argOffset+len(ai.argTypes)], val)
|
||||
}
|
||||
|
||||
func directDecode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, valType llvm.Type, args []llvm.Value) llvm.Value {
|
||||
var alloca llvm.Value
|
||||
|
||||
switch len(args) {
|
||||
case 0:
|
||||
return llvm.ConstNull(ctx.StructType(nil, false))
|
||||
|
||||
case 1:
|
||||
if args[0].Type().C == valType.C {
|
||||
return args[0]
|
||||
}
|
||||
alloca = allocaBuilder.CreateAlloca(valType, "")
|
||||
bitcast := builder.CreateBitCast(alloca, llvm.PointerType(args[0].Type(), 0), "")
|
||||
builder.CreateStore(args[0], bitcast)
|
||||
|
||||
case 2:
|
||||
alloca = allocaBuilder.CreateAlloca(valType, "")
|
||||
var argTypes []llvm.Type
|
||||
for _, a := range args {
|
||||
argTypes = append(argTypes, a.Type())
|
||||
}
|
||||
encodeType := ctx.StructType(argTypes, false)
|
||||
bitcast := builder.CreateBitCast(alloca, llvm.PointerType(encodeType, 0), "")
|
||||
builder.CreateStore(args[0], builder.CreateStructGEP(bitcast, 0, ""))
|
||||
builder.CreateStore(args[1], builder.CreateStructGEP(bitcast, 1, ""))
|
||||
|
||||
default:
|
||||
panic("unexpected argTypes size")
|
||||
}
|
||||
|
||||
return builder.CreateLoad(alloca, "")
|
||||
}
|
||||
|
||||
func (ai *directArgInfo) decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder) llvm.Value {
|
||||
var args []llvm.Value
|
||||
fn := builder.GetInsertBlock().Parent()
|
||||
for i, _ := range ai.argTypes {
|
||||
args = append(args, fn.Param(ai.argOffset+i))
|
||||
}
|
||||
return directDecode(ctx, allocaBuilder, builder, ai.valType, args)
|
||||
}
|
||||
|
||||
type indirectArgInfo struct {
|
||||
argOffset int
|
||||
}
|
||||
|
||||
func (ai *indirectArgInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, args []llvm.Value, val llvm.Value) {
|
||||
alloca := allocaBuilder.CreateAlloca(val.Type(), "")
|
||||
builder.CreateStore(val, alloca)
|
||||
args[ai.argOffset] = alloca
|
||||
}
|
||||
|
||||
func (ai *indirectArgInfo) decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder) llvm.Value {
|
||||
fn := builder.GetInsertBlock().Parent()
|
||||
return builder.CreateLoad(fn.Param(ai.argOffset), "")
|
||||
}
|
||||
|
||||
type directRetInfo struct {
|
||||
numResults int
|
||||
retTypes []llvm.Type
|
||||
resultsType llvm.Type
|
||||
}
|
||||
|
||||
func (ri *directRetInfo) prepare(ctx llvm.Context, allocaBuilder llvm.Builder, args []llvm.Value) {
|
||||
}
|
||||
|
||||
func (ri *directRetInfo) decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, call llvm.Value) []llvm.Value {
|
||||
var args []llvm.Value
|
||||
switch len(ri.retTypes) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
args = []llvm.Value{call}
|
||||
default:
|
||||
args = make([]llvm.Value, len(ri.retTypes))
|
||||
for i := 0; i != len(ri.retTypes); i++ {
|
||||
args[i] = builder.CreateExtractValue(call, i, "")
|
||||
}
|
||||
}
|
||||
|
||||
d := directDecode(ctx, allocaBuilder, builder, ri.resultsType, args)
|
||||
|
||||
if ri.numResults == 1 {
|
||||
return []llvm.Value{d}
|
||||
} else {
|
||||
results := make([]llvm.Value, ri.numResults)
|
||||
for i := 0; i != ri.numResults; i++ {
|
||||
results[i] = builder.CreateExtractValue(d, i, "")
|
||||
}
|
||||
return results
|
||||
}
|
||||
}
|
||||
|
||||
func (ri *directRetInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, vals []llvm.Value) {
|
||||
if len(ri.retTypes) == 0 {
|
||||
builder.CreateRetVoid()
|
||||
return
|
||||
}
|
||||
|
||||
var val llvm.Value
|
||||
switch ri.numResults {
|
||||
case 1:
|
||||
val = vals[0]
|
||||
default:
|
||||
val = llvm.Undef(ri.resultsType)
|
||||
for i, v := range vals {
|
||||
val = builder.CreateInsertValue(val, v, i, "")
|
||||
}
|
||||
}
|
||||
|
||||
args := make([]llvm.Value, len(ri.retTypes))
|
||||
directEncode(ctx, allocaBuilder, builder, ri.retTypes, args, val)
|
||||
|
||||
var retval llvm.Value
|
||||
switch len(ri.retTypes) {
|
||||
case 1:
|
||||
retval = args[0]
|
||||
default:
|
||||
retval = llvm.Undef(ctx.StructType(ri.retTypes, false))
|
||||
for i, a := range args {
|
||||
retval = builder.CreateInsertValue(retval, a, i, "")
|
||||
}
|
||||
}
|
||||
builder.CreateRet(retval)
|
||||
}
|
||||
|
||||
type indirectRetInfo struct {
|
||||
numResults int
|
||||
sretSlot llvm.Value
|
||||
resultsType llvm.Type
|
||||
}
|
||||
|
||||
func (ri *indirectRetInfo) prepare(ctx llvm.Context, allocaBuilder llvm.Builder, args []llvm.Value) {
|
||||
ri.sretSlot = allocaBuilder.CreateAlloca(ri.resultsType, "")
|
||||
args[0] = ri.sretSlot
|
||||
}
|
||||
|
||||
func (ri *indirectRetInfo) decode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, call llvm.Value) []llvm.Value {
|
||||
if ri.numResults == 1 {
|
||||
return []llvm.Value{builder.CreateLoad(ri.sretSlot, "")}
|
||||
} else {
|
||||
vals := make([]llvm.Value, ri.numResults)
|
||||
for i, _ := range vals {
|
||||
vals[i] = builder.CreateLoad(builder.CreateStructGEP(ri.sretSlot, i, ""), "")
|
||||
}
|
||||
return vals
|
||||
}
|
||||
}
|
||||
|
||||
func (ri *indirectRetInfo) encode(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, vals []llvm.Value) {
|
||||
fn := builder.GetInsertBlock().Parent()
|
||||
sretSlot := fn.Param(0)
|
||||
|
||||
if ri.numResults == 1 {
|
||||
builder.CreateStore(vals[0], sretSlot)
|
||||
} else {
|
||||
for i, v := range vals {
|
||||
builder.CreateStore(v, builder.CreateStructGEP(sretSlot, i, ""))
|
||||
}
|
||||
}
|
||||
builder.CreateRetVoid()
|
||||
}
|
||||
|
||||
type functionTypeInfo struct {
|
||||
functionType llvm.Type
|
||||
argAttrs []llvm.Attribute
|
||||
retAttr llvm.Attribute
|
||||
argInfos []argInfo
|
||||
retInf retInfo
|
||||
}
|
||||
|
||||
func (fi *functionTypeInfo) declare(m llvm.Module, name string) llvm.Value {
|
||||
fn := llvm.AddFunction(m, name, fi.functionType)
|
||||
fn.AddFunctionAttr(fi.retAttr)
|
||||
for i, a := range fi.argAttrs {
|
||||
if a != 0 {
|
||||
fn.Param(i).AddAttribute(a)
|
||||
}
|
||||
}
|
||||
return fn
|
||||
}
|
||||
|
||||
func (fi *functionTypeInfo) call(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, callee llvm.Value, args []llvm.Value) []llvm.Value {
|
||||
callArgs := make([]llvm.Value, len(fi.argAttrs))
|
||||
for i, a := range args {
|
||||
fi.argInfos[i].encode(ctx, allocaBuilder, builder, callArgs, a)
|
||||
}
|
||||
fi.retInf.prepare(ctx, allocaBuilder, callArgs)
|
||||
typedCallee := builder.CreateBitCast(callee, llvm.PointerType(fi.functionType, 0), "")
|
||||
call := builder.CreateCall(typedCallee, callArgs, "")
|
||||
call.AddInstrAttribute(0, fi.retAttr)
|
||||
for i, a := range fi.argAttrs {
|
||||
call.AddInstrAttribute(i+1, a)
|
||||
}
|
||||
return fi.retInf.decode(ctx, allocaBuilder, builder, call)
|
||||
}
|
||||
|
||||
func (fi *functionTypeInfo) invoke(ctx llvm.Context, allocaBuilder llvm.Builder, builder llvm.Builder, callee llvm.Value, args []llvm.Value, cont, lpad llvm.BasicBlock) []llvm.Value {
|
||||
callArgs := make([]llvm.Value, len(fi.argAttrs))
|
||||
for i, a := range args {
|
||||
fi.argInfos[i].encode(ctx, allocaBuilder, builder, callArgs, a)
|
||||
}
|
||||
fi.retInf.prepare(ctx, allocaBuilder, callArgs)
|
||||
typedCallee := builder.CreateBitCast(callee, llvm.PointerType(fi.functionType, 0), "")
|
||||
call := builder.CreateInvoke(typedCallee, callArgs, cont, lpad, "")
|
||||
call.AddInstrAttribute(0, fi.retAttr)
|
||||
for i, a := range fi.argAttrs {
|
||||
call.AddInstrAttribute(i+1, a)
|
||||
}
|
||||
builder.SetInsertPointAtEnd(cont)
|
||||
return fi.retInf.decode(ctx, allocaBuilder, builder, call)
|
||||
}
|
||||
|
||||
func (tm *llvmTypeMap) getFunctionTypeInfo(args []types.Type, results []types.Type) (fi functionTypeInfo) {
|
||||
var returnType llvm.Type
|
||||
var argTypes []llvm.Type
|
||||
if len(results) == 0 {
|
||||
returnType = llvm.VoidType()
|
||||
fi.retInf = &directRetInfo{}
|
||||
} else {
|
||||
aik := tm.classify(results...)
|
||||
|
||||
var resultsType llvm.Type
|
||||
if len(results) == 1 {
|
||||
resultsType = tm.ToLLVM(results[0])
|
||||
} else {
|
||||
elements := make([]llvm.Type, len(results))
|
||||
for i := range elements {
|
||||
elements[i] = tm.ToLLVM(results[i])
|
||||
}
|
||||
resultsType = tm.ctx.StructType(elements, false)
|
||||
}
|
||||
|
||||
switch aik {
|
||||
case AIK_Direct:
|
||||
var retFields []backendType
|
||||
for _, t := range results {
|
||||
retFields = append(retFields, tm.getBackendType(t))
|
||||
}
|
||||
bt := &structBType{retFields}
|
||||
|
||||
retTypes, retAttrs, _, _ := tm.expandType(nil, nil, bt)
|
||||
switch len(retTypes) {
|
||||
case 0: // e.g., empty struct
|
||||
returnType = llvm.VoidType()
|
||||
case 1:
|
||||
returnType = retTypes[0]
|
||||
fi.retAttr = retAttrs[0]
|
||||
case 2:
|
||||
returnType = llvm.StructType(retTypes, false)
|
||||
default:
|
||||
panic("unexpected expandType result")
|
||||
}
|
||||
fi.retInf = &directRetInfo{numResults: len(results), retTypes: retTypes, resultsType: resultsType}
|
||||
|
||||
case AIK_Indirect:
|
||||
returnType = llvm.VoidType()
|
||||
argTypes = []llvm.Type{llvm.PointerType(resultsType, 0)}
|
||||
fi.argAttrs = []llvm.Attribute{llvm.StructRetAttribute}
|
||||
fi.retInf = &indirectRetInfo{numResults: len(results), resultsType: resultsType}
|
||||
}
|
||||
}
|
||||
|
||||
// Keep track of the number of INTEGER/SSE class registers remaining.
|
||||
remainingInt := 6
|
||||
remainingSSE := 8
|
||||
|
||||
for _, arg := range args {
|
||||
aik := tm.classify(arg)
|
||||
|
||||
isDirect := aik == AIK_Direct
|
||||
if isDirect {
|
||||
bt := tm.getBackendType(arg)
|
||||
directArgTypes, directArgAttrs, numInt, numSSE := tm.expandType(argTypes, fi.argAttrs, bt)
|
||||
|
||||
// Check if the argument can fit into the remaining registers, or if
|
||||
// it would just occupy one register (which pushes the whole argument
|
||||
// onto the stack anyway).
|
||||
if numInt <= remainingInt && numSSE <= remainingSSE || numInt+numSSE == 1 {
|
||||
remainingInt -= numInt
|
||||
remainingSSE -= numSSE
|
||||
argInfo := &directArgInfo{argOffset: len(argTypes), valType: bt.ToLLVM(tm.ctx)}
|
||||
fi.argInfos = append(fi.argInfos, argInfo)
|
||||
argTypes = directArgTypes
|
||||
fi.argAttrs = directArgAttrs
|
||||
argInfo.argTypes = argTypes[argInfo.argOffset:len(argTypes)]
|
||||
} else {
|
||||
// No remaining registers; pass on the stack.
|
||||
isDirect = false
|
||||
}
|
||||
}
|
||||
|
||||
if !isDirect {
|
||||
fi.argInfos = append(fi.argInfos, &indirectArgInfo{len(argTypes)})
|
||||
argTypes = append(argTypes, llvm.PointerType(tm.ToLLVM(arg), 0))
|
||||
fi.argAttrs = append(fi.argAttrs, llvm.ByValAttribute)
|
||||
}
|
||||
}
|
||||
|
||||
fi.functionType = llvm.FunctionType(returnType, argTypes, false)
|
||||
return
|
||||
}
|
||||
|
||||
func (tm *llvmTypeMap) getSignatureInfo(sig *types.Signature) functionTypeInfo {
|
||||
var args, results []types.Type
|
||||
if sig.Recv() != nil {
|
||||
recvtype := sig.Recv().Type()
|
||||
if _, ok := recvtype.Underlying().(*types.Pointer); !ok && recvtype != types.Typ[types.UnsafePointer] {
|
||||
recvtype = types.NewPointer(recvtype)
|
||||
}
|
||||
args = []types.Type{recvtype}
|
||||
}
|
||||
|
||||
for i := 0; i != sig.Params().Len(); i++ {
|
||||
args = append(args, sig.Params().At(i).Type())
|
||||
}
|
||||
for i := 0; i != sig.Results().Len(); i++ {
|
||||
results = append(results, sig.Results().At(i).Type())
|
||||
}
|
||||
return tm.getFunctionTypeInfo(args, results)
|
||||
}
|
44
llgo/irgen/call.go
Normal file
44
llgo/irgen/call.go
Normal file
@ -0,0 +1,44 @@
|
||||
//===- call.go - IR generation for calls ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for calls.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// createCall emits the code for a function call,
|
||||
// taking into account receivers, and panic/defer.
|
||||
func (fr *frame) createCall(fn *govalue, argValues []*govalue) []*govalue {
|
||||
fntyp := fn.Type().Underlying().(*types.Signature)
|
||||
typinfo := fr.types.getSignatureInfo(fntyp)
|
||||
|
||||
args := make([]llvm.Value, len(argValues))
|
||||
for i, arg := range argValues {
|
||||
args[i] = arg.value
|
||||
}
|
||||
var results []llvm.Value
|
||||
if fr.unwindBlock.IsNil() {
|
||||
results = typinfo.call(fr.types.ctx, fr.allocaBuilder, fr.builder, fn.value, args)
|
||||
} else {
|
||||
contbb := llvm.AddBasicBlock(fr.function, "")
|
||||
results = typinfo.invoke(fr.types.ctx, fr.allocaBuilder, fr.builder, fn.value, args, contbb, fr.unwindBlock)
|
||||
}
|
||||
|
||||
resultValues := make([]*govalue, len(results))
|
||||
for i, res := range results {
|
||||
resultValues[i] = newValue(res, fntyp.Results().At(i).Type())
|
||||
}
|
||||
return resultValues
|
||||
}
|
123
llgo/irgen/channels.go
Normal file
123
llgo/irgen/channels.go
Normal file
@ -0,0 +1,123 @@
|
||||
//===- channels.go - IR generation for channels ---------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for channels.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// makeChan implements make(chantype[, size])
|
||||
func (fr *frame) makeChan(chantyp types.Type, size *govalue) *govalue {
|
||||
// TODO(pcc): call __go_new_channel_big here if needed
|
||||
dyntyp := fr.types.ToRuntime(chantyp)
|
||||
size = fr.convert(size, types.Typ[types.Uintptr])
|
||||
ch := fr.runtime.newChannel.call(fr, dyntyp, size.value)[0]
|
||||
return newValue(ch, chantyp)
|
||||
}
|
||||
|
||||
// chanSend implements ch<- x
|
||||
func (fr *frame) chanSend(ch *govalue, elem *govalue) {
|
||||
elemtyp := ch.Type().Underlying().(*types.Chan).Elem()
|
||||
elem = fr.convert(elem, elemtyp)
|
||||
elemptr := fr.allocaBuilder.CreateAlloca(elem.value.Type(), "")
|
||||
fr.builder.CreateStore(elem.value, elemptr)
|
||||
elemptr = fr.builder.CreateBitCast(elemptr, llvm.PointerType(llvm.Int8Type(), 0), "")
|
||||
chantyp := fr.types.ToRuntime(ch.Type())
|
||||
fr.runtime.sendBig.call(fr, chantyp, ch.value, elemptr)
|
||||
}
|
||||
|
||||
// chanRecv implements x[, ok] = <-ch
|
||||
func (fr *frame) chanRecv(ch *govalue, commaOk bool) (x, ok *govalue) {
|
||||
elemtyp := ch.Type().Underlying().(*types.Chan).Elem()
|
||||
ptr := fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(elemtyp), "")
|
||||
ptri8 := fr.builder.CreateBitCast(ptr, llvm.PointerType(llvm.Int8Type(), 0), "")
|
||||
chantyp := fr.types.ToRuntime(ch.Type())
|
||||
|
||||
if commaOk {
|
||||
okval := fr.runtime.chanrecv2.call(fr, chantyp, ch.value, ptri8)[0]
|
||||
ok = newValue(okval, types.Typ[types.Bool])
|
||||
} else {
|
||||
fr.runtime.receive.call(fr, chantyp, ch.value, ptri8)
|
||||
}
|
||||
x = newValue(fr.builder.CreateLoad(ptr, ""), elemtyp)
|
||||
return
|
||||
}
|
||||
|
||||
// chanClose implements close(ch)
|
||||
func (fr *frame) chanClose(ch *govalue) {
|
||||
fr.runtime.builtinClose.call(fr, ch.value)
|
||||
}
|
||||
|
||||
// selectState is equivalent to ssa.SelectState
|
||||
type selectState struct {
|
||||
Dir types.ChanDir
|
||||
Chan *govalue
|
||||
Send *govalue
|
||||
}
|
||||
|
||||
func (fr *frame) chanSelect(states []selectState, blocking bool) (index, recvOk *govalue, recvElems []*govalue) {
|
||||
n := uint64(len(states))
|
||||
if !blocking {
|
||||
// non-blocking means there's a default case
|
||||
n++
|
||||
}
|
||||
size := llvm.ConstInt(llvm.Int32Type(), n, false)
|
||||
selectp := fr.runtime.newSelect.call(fr, size)[0]
|
||||
|
||||
// Allocate stack for the values to send and receive.
|
||||
//
|
||||
// TODO(axw) check if received elements have any users, and
|
||||
// elide stack allocation if not (pass nil to recv2 instead.)
|
||||
ptrs := make([]llvm.Value, len(states))
|
||||
for i, state := range states {
|
||||
chantyp := state.Chan.Type().Underlying().(*types.Chan)
|
||||
elemtyp := fr.types.ToLLVM(chantyp.Elem())
|
||||
ptrs[i] = fr.allocaBuilder.CreateAlloca(elemtyp, "")
|
||||
if state.Dir == types.SendOnly {
|
||||
fr.builder.CreateStore(state.Send.value, ptrs[i])
|
||||
} else {
|
||||
recvElems = append(recvElems, newValue(ptrs[i], chantyp.Elem()))
|
||||
}
|
||||
}
|
||||
|
||||
// Create select{send,recv2} calls.
|
||||
var receivedp llvm.Value
|
||||
if len(recvElems) > 0 {
|
||||
receivedp = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(types.Typ[types.Bool]), "")
|
||||
}
|
||||
if !blocking {
|
||||
// If the default case is chosen, the index must be -1.
|
||||
fr.runtime.selectdefault.call(fr, selectp, llvm.ConstAllOnes(llvm.Int32Type()))
|
||||
}
|
||||
for i, state := range states {
|
||||
ch := state.Chan.value
|
||||
index := llvm.ConstInt(llvm.Int32Type(), uint64(i), false)
|
||||
if state.Dir == types.SendOnly {
|
||||
fr.runtime.selectsend.call(fr, selectp, ch, ptrs[i], index)
|
||||
} else {
|
||||
fr.runtime.selectrecv2.call(fr, selectp, ch, ptrs[i], receivedp, index)
|
||||
}
|
||||
}
|
||||
|
||||
// Fire off the select.
|
||||
index = newValue(fr.runtime.selectgo.call(fr, selectp)[0], types.Typ[types.Int])
|
||||
if len(recvElems) > 0 {
|
||||
recvOk = newValue(fr.builder.CreateLoad(receivedp, ""), types.Typ[types.Bool])
|
||||
for _, recvElem := range recvElems {
|
||||
recvElem.value = fr.builder.CreateLoad(recvElem.value, "")
|
||||
}
|
||||
}
|
||||
return index, recvOk, recvElems
|
||||
}
|
37
llgo/irgen/closures.go
Normal file
37
llgo/irgen/closures.go
Normal file
@ -0,0 +1,37 @@
|
||||
//===- closures.go - IR generation for closures ---------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for closures.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
)
|
||||
|
||||
// makeClosure creates a closure from a function pointer and
|
||||
// a set of bindings. The bindings are addresses of captured
|
||||
// variables.
|
||||
func (fr *frame) makeClosure(fn *govalue, bindings []*govalue) *govalue {
|
||||
govalues := append([]*govalue{fn}, bindings...)
|
||||
fields := make([]*types.Var, len(govalues))
|
||||
for i, v := range govalues {
|
||||
field := types.NewField(0, nil, "_", v.Type(), false)
|
||||
fields[i] = field
|
||||
}
|
||||
block := fr.createTypeMalloc(types.NewStruct(fields, nil))
|
||||
for i, v := range govalues {
|
||||
addressPtr := fr.builder.CreateStructGEP(block, i, "")
|
||||
fr.builder.CreateStore(v.value, addressPtr)
|
||||
}
|
||||
closure := fr.builder.CreateBitCast(block, fn.value.Type(), "")
|
||||
return newValue(closure, fn.Type())
|
||||
}
|
361
llgo/irgen/compiler.go
Normal file
361
llgo/irgen/compiler.go
Normal file
@ -0,0 +1,361 @@
|
||||
//===- compiler.go - IR generator entry point -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements the main IR generator entry point, (*Compiler).Compile.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/token"
|
||||
"log"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
llgobuild "llvm.org/llgo/build"
|
||||
"llvm.org/llgo/debug"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
|
||||
"llvm.org/llgo/third_party/go.tools/go/gccgoimporter"
|
||||
"llvm.org/llgo/third_party/go.tools/go/importer"
|
||||
"llvm.org/llgo/third_party/go.tools/go/loader"
|
||||
"llvm.org/llgo/third_party/go.tools/go/ssa"
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
llvm.Module
|
||||
Path string
|
||||
ExportData []byte
|
||||
disposed bool
|
||||
}
|
||||
|
||||
func (m *Module) Dispose() {
|
||||
if m.disposed {
|
||||
return
|
||||
}
|
||||
m.Module.Dispose()
|
||||
m.disposed = true
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
type CompilerOptions struct {
|
||||
// TargetTriple is the LLVM triple for the target.
|
||||
TargetTriple string
|
||||
|
||||
// GenerateDebug decides whether debug data is
|
||||
// generated in the output module.
|
||||
GenerateDebug bool
|
||||
|
||||
// DebugPrefixMaps is a list of mappings from source prefixes to
|
||||
// replacement prefixes, to be applied in debug info.
|
||||
DebugPrefixMaps []debug.PrefixMap
|
||||
|
||||
// Logger is a logger used for tracing compilation.
|
||||
Logger *log.Logger
|
||||
|
||||
// DumpSSA is a debugging option that dumps each SSA function
|
||||
// to stderr before generating code for it.
|
||||
DumpSSA bool
|
||||
|
||||
// GccgoPath is the path to the gccgo binary whose libgo we read import
|
||||
// data from. If blank, the caller is expected to supply an import
|
||||
// path in ImportPaths.
|
||||
GccgoPath string
|
||||
|
||||
// ImportPaths is the list of additional import paths
|
||||
ImportPaths []string
|
||||
|
||||
// SanitizerAttribute is an attribute to apply to functions to enable
|
||||
// dynamic instrumentation using a sanitizer.
|
||||
SanitizerAttribute llvm.Attribute
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
opts CompilerOptions
|
||||
dataLayout string
|
||||
pnacl bool
|
||||
}
|
||||
|
||||
func NewCompiler(opts CompilerOptions) (*Compiler, error) {
|
||||
compiler := &Compiler{opts: opts}
|
||||
if strings.ToLower(compiler.opts.TargetTriple) == "pnacl" {
|
||||
compiler.opts.TargetTriple = PNaClTriple
|
||||
compiler.pnacl = true
|
||||
}
|
||||
dataLayout, err := llvmDataLayout(compiler.opts.TargetTriple)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
compiler.dataLayout = dataLayout
|
||||
return compiler, nil
|
||||
}
|
||||
|
||||
func (c *Compiler) Compile(filenames []string, importpath string) (m *Module, err error) {
|
||||
target := llvm.NewTargetData(c.dataLayout)
|
||||
compiler := &compiler{
|
||||
CompilerOptions: c.opts,
|
||||
dataLayout: c.dataLayout,
|
||||
target: target,
|
||||
pnacl: c.pnacl,
|
||||
llvmtypes: NewLLVMTypeMap(llvm.GlobalContext(), target),
|
||||
}
|
||||
return compiler.compile(filenames, importpath)
|
||||
}
|
||||
|
||||
type compiler struct {
|
||||
CompilerOptions
|
||||
|
||||
module *Module
|
||||
dataLayout string
|
||||
target llvm.TargetData
|
||||
fileset *token.FileSet
|
||||
|
||||
runtime *runtimeInterface
|
||||
llvmtypes *llvmTypeMap
|
||||
types *TypeMap
|
||||
|
||||
// runtimetypespkg is the type-checked runtime/types.go file,
|
||||
// which is used for evaluating the types of runtime functions.
|
||||
runtimetypespkg *types.Package
|
||||
|
||||
// pnacl is set to true if the target triple was originally
|
||||
// specified as "pnacl". This is necessary, as the TargetTriple
|
||||
// field will have been updated to the true triple used to
|
||||
// compile PNaCl modules.
|
||||
pnacl bool
|
||||
|
||||
debug *debug.DIBuilder
|
||||
}
|
||||
|
||||
func (c *compiler) logf(format string, v ...interface{}) {
|
||||
if c.Logger != nil {
|
||||
c.Logger.Printf(format, v...)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compiler) addCommonFunctionAttrs(fn llvm.Value) {
|
||||
fn.AddTargetDependentFunctionAttr("disable-tail-calls", "true")
|
||||
fn.AddTargetDependentFunctionAttr("split-stack", "")
|
||||
if attr := c.SanitizerAttribute; attr != 0 {
|
||||
fn.AddFunctionAttr(attr)
|
||||
}
|
||||
}
|
||||
|
||||
func (compiler *compiler) compile(filenames []string, importpath string) (m *Module, err error) {
|
||||
buildctx, err := llgobuild.ContextFromTriple(compiler.TargetTriple)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
initmap := make(map[*types.Package]gccgoimporter.InitData)
|
||||
var importer types.Importer
|
||||
if compiler.GccgoPath == "" {
|
||||
paths := append(append([]string{}, compiler.ImportPaths...), ".")
|
||||
importer = gccgoimporter.GetImporter(paths, initmap)
|
||||
} else {
|
||||
var inst gccgoimporter.GccgoInstallation
|
||||
err = inst.InitFromDriver(compiler.GccgoPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
importer = inst.GetImporter(compiler.ImportPaths, initmap)
|
||||
}
|
||||
|
||||
impcfg := &loader.Config{
|
||||
Fset: token.NewFileSet(),
|
||||
TypeChecker: types.Config{
|
||||
Import: importer,
|
||||
Sizes: compiler.llvmtypes,
|
||||
},
|
||||
Build: &buildctx.Context,
|
||||
}
|
||||
// Must use parseFiles, so we retain comments;
|
||||
// this is important for annotation processing.
|
||||
astFiles, err := parseFiles(impcfg.Fset, filenames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If no import path is specified, then set the import
|
||||
// path to be the same as the package's name.
|
||||
if importpath == "" {
|
||||
importpath = astFiles[0].Name.String()
|
||||
}
|
||||
impcfg.CreateFromFiles(importpath, astFiles...)
|
||||
iprog, err := impcfg.Load()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
program := ssa.Create(iprog, ssa.BareInits)
|
||||
mainPkginfo := iprog.InitialPackages()[0]
|
||||
mainPkg := program.CreatePackage(mainPkginfo)
|
||||
|
||||
// Create a Module, which contains the LLVM module.
|
||||
modulename := importpath
|
||||
compiler.module = &Module{Module: llvm.NewModule(modulename), Path: modulename}
|
||||
compiler.module.SetTarget(compiler.TargetTriple)
|
||||
compiler.module.SetDataLayout(compiler.dataLayout)
|
||||
|
||||
// Create a new translation unit.
|
||||
unit := newUnit(compiler, mainPkg)
|
||||
|
||||
// Create the runtime interface.
|
||||
compiler.runtime, err = newRuntimeInterface(compiler.module.Module, compiler.llvmtypes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mainPkg.Build()
|
||||
|
||||
// Create a struct responsible for mapping static types to LLVM types,
|
||||
// and to runtime/dynamic type values.
|
||||
compiler.types = NewTypeMap(
|
||||
mainPkg,
|
||||
compiler.llvmtypes,
|
||||
compiler.module.Module,
|
||||
compiler.runtime,
|
||||
MethodResolver(unit),
|
||||
)
|
||||
|
||||
if compiler.GenerateDebug {
|
||||
compiler.debug = debug.NewDIBuilder(
|
||||
types.Sizes(compiler.llvmtypes),
|
||||
compiler.module.Module,
|
||||
impcfg.Fset,
|
||||
compiler.DebugPrefixMaps,
|
||||
)
|
||||
defer compiler.debug.Destroy()
|
||||
defer compiler.debug.Finalize()
|
||||
}
|
||||
|
||||
unit.translatePackage(mainPkg)
|
||||
compiler.processAnnotations(unit, mainPkginfo)
|
||||
|
||||
if importpath == "main" {
|
||||
if err = compiler.createInitMainFunction(mainPkg, initmap); err != nil {
|
||||
return nil, fmt.Errorf("failed to create __go_init_main: %v", err)
|
||||
}
|
||||
} else {
|
||||
compiler.module.ExportData = compiler.buildExportData(mainPkg, initmap)
|
||||
}
|
||||
|
||||
return compiler.module, nil
|
||||
}
|
||||
|
||||
type byPriorityThenFunc []gccgoimporter.PackageInit
|
||||
|
||||
func (a byPriorityThenFunc) Len() int { return len(a) }
|
||||
func (a byPriorityThenFunc) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a byPriorityThenFunc) Less(i, j int) bool {
|
||||
switch {
|
||||
case a[i].Priority < a[j].Priority:
|
||||
return true
|
||||
case a[i].Priority > a[j].Priority:
|
||||
return false
|
||||
case a[i].InitFunc < a[j].InitFunc:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compiler) buildPackageInitData(mainPkg *ssa.Package, initmap map[*types.Package]gccgoimporter.InitData) gccgoimporter.InitData {
|
||||
var inits []gccgoimporter.PackageInit
|
||||
for _, imp := range mainPkg.Object.Imports() {
|
||||
inits = append(inits, initmap[imp].Inits...)
|
||||
}
|
||||
sort.Sort(byPriorityThenFunc(inits))
|
||||
|
||||
// Deduplicate init entries. We want to preserve the entry with the highest priority.
|
||||
// Normally a package's priorities will be consistent among its dependencies, but it is
|
||||
// possible for them to be different. For example, if a standard library test augments a
|
||||
// package which is a dependency of 'regexp' (which is imported by every test main package)
|
||||
// with additional dependencies, those dependencies may cause the package under test to
|
||||
// receive a higher priority than indicated by its init clause in 'regexp'.
|
||||
uniqinits := make([]gccgoimporter.PackageInit, len(inits))
|
||||
uniqinitpos := len(inits)
|
||||
uniqinitnames := make(map[string]struct{})
|
||||
for i, _ := range inits {
|
||||
init := inits[len(inits)-1-i]
|
||||
if _, ok := uniqinitnames[init.InitFunc]; !ok {
|
||||
uniqinitnames[init.InitFunc] = struct{}{}
|
||||
uniqinitpos--
|
||||
uniqinits[uniqinitpos] = init
|
||||
}
|
||||
}
|
||||
uniqinits = uniqinits[uniqinitpos:]
|
||||
|
||||
ourprio := 1
|
||||
if len(uniqinits) != 0 {
|
||||
ourprio = uniqinits[len(uniqinits)-1].Priority + 1
|
||||
}
|
||||
|
||||
if imp := mainPkg.Func("init"); imp != nil {
|
||||
impname := c.types.mc.mangleFunctionName(imp)
|
||||
uniqinits = append(uniqinits, gccgoimporter.PackageInit{mainPkg.Object.Name(), impname, ourprio})
|
||||
}
|
||||
|
||||
return gccgoimporter.InitData{ourprio, uniqinits}
|
||||
}
|
||||
|
||||
func (c *compiler) createInitMainFunction(mainPkg *ssa.Package, initmap map[*types.Package]gccgoimporter.InitData) error {
|
||||
initdata := c.buildPackageInitData(mainPkg, initmap)
|
||||
|
||||
ftyp := llvm.FunctionType(llvm.VoidType(), nil, false)
|
||||
initMain := llvm.AddFunction(c.module.Module, "__go_init_main", ftyp)
|
||||
c.addCommonFunctionAttrs(initMain)
|
||||
entry := llvm.AddBasicBlock(initMain, "entry")
|
||||
|
||||
builder := llvm.GlobalContext().NewBuilder()
|
||||
defer builder.Dispose()
|
||||
builder.SetInsertPointAtEnd(entry)
|
||||
|
||||
for _, init := range initdata.Inits {
|
||||
initfn := c.module.Module.NamedFunction(init.InitFunc)
|
||||
if initfn.IsNil() {
|
||||
initfn = llvm.AddFunction(c.module.Module, init.InitFunc, ftyp)
|
||||
}
|
||||
builder.CreateCall(initfn, nil, "")
|
||||
}
|
||||
|
||||
builder.CreateRetVoid()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *compiler) buildExportData(mainPkg *ssa.Package, initmap map[*types.Package]gccgoimporter.InitData) []byte {
|
||||
exportData := importer.ExportData(mainPkg.Object)
|
||||
b := bytes.NewBuffer(exportData)
|
||||
|
||||
initdata := c.buildPackageInitData(mainPkg, initmap)
|
||||
b.WriteString("v1;\npriority ")
|
||||
b.WriteString(strconv.Itoa(initdata.Priority))
|
||||
b.WriteString(";\n")
|
||||
|
||||
if len(initdata.Inits) != 0 {
|
||||
b.WriteString("init")
|
||||
for _, init := range initdata.Inits {
|
||||
b.WriteRune(' ')
|
||||
b.WriteString(init.Name)
|
||||
b.WriteRune(' ')
|
||||
b.WriteString(init.InitFunc)
|
||||
b.WriteRune(' ')
|
||||
b.WriteString(strconv.Itoa(init.Priority))
|
||||
}
|
||||
b.WriteString(";\n")
|
||||
}
|
||||
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
// vim: set ft=go :
|
72
llgo/irgen/errors.go
Normal file
72
llgo/irgen/errors.go
Normal file
@ -0,0 +1,72 @@
|
||||
//===- errors.go - IR generation for run-time panics ----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for triggering run-time panics.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
const (
|
||||
// From go-runtime-error.c
|
||||
gccgoRuntimeErrorSLICE_INDEX_OUT_OF_BOUNDS = 0
|
||||
gccgoRuntimeErrorARRAY_INDEX_OUT_OF_BOUNDS = 1
|
||||
gccgoRuntimeErrorSTRING_INDEX_OUT_OF_BOUNDS = 2
|
||||
gccgoRuntimeErrorSLICE_SLICE_OUT_OF_BOUNDS = 3
|
||||
gccgoRuntimeErrorARRAY_SLICE_OUT_OF_BOUNDS = 4
|
||||
gccgoRuntimeErrorSTRING_SLICE_OUT_OF_BOUNDS = 5
|
||||
gccgoRuntimeErrorNIL_DEREFERENCE = 6
|
||||
gccgoRuntimeErrorMAKE_SLICE_OUT_OF_BOUNDS = 7
|
||||
gccgoRuntimeErrorMAKE_MAP_OUT_OF_BOUNDS = 8
|
||||
gccgoRuntimeErrorMAKE_CHAN_OUT_OF_BOUNDS = 9
|
||||
gccgoRuntimeErrorDIVISION_BY_ZERO = 10
|
||||
gccgoRuntimeErrorCount = 11
|
||||
)
|
||||
|
||||
func (fr *frame) setBranchWeightMetadata(br llvm.Value, trueweight, falseweight uint64) {
|
||||
mdprof := llvm.MDKindID("prof")
|
||||
|
||||
mdnode := llvm.MDNode([]llvm.Value{
|
||||
llvm.MDString("branch_weights"),
|
||||
llvm.ConstInt(llvm.Int32Type(), trueweight, false),
|
||||
llvm.ConstInt(llvm.Int32Type(), falseweight, false),
|
||||
})
|
||||
|
||||
br.SetMetadata(mdprof, mdnode)
|
||||
}
|
||||
|
||||
func (fr *frame) condBrRuntimeError(cond llvm.Value, errcode uint64) {
|
||||
if cond.IsNull() {
|
||||
return
|
||||
}
|
||||
|
||||
errorbb := fr.runtimeErrorBlocks[errcode]
|
||||
newbb := errorbb.C == nil
|
||||
if newbb {
|
||||
errorbb = llvm.AddBasicBlock(fr.function, "")
|
||||
fr.runtimeErrorBlocks[errcode] = errorbb
|
||||
}
|
||||
|
||||
contbb := llvm.AddBasicBlock(fr.function, "")
|
||||
|
||||
br := fr.builder.CreateCondBr(cond, errorbb, contbb)
|
||||
fr.setBranchWeightMetadata(br, 1, 1000)
|
||||
|
||||
if newbb {
|
||||
fr.builder.SetInsertPointAtEnd(errorbb)
|
||||
fr.runtime.runtimeError.call(fr, llvm.ConstInt(llvm.Int32Type(), errcode, false))
|
||||
fr.builder.CreateUnreachable()
|
||||
}
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(contbb)
|
||||
}
|
125
llgo/irgen/indirect.go
Normal file
125
llgo/irgen/indirect.go
Normal file
@ -0,0 +1,125 @@
|
||||
//===- indirect.go - IR generation for thunks -----------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for thunks required by the "defer" and
|
||||
// "go" builtins.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/ssa"
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// createThunk creates a thunk from a
|
||||
// given function and arguments, suitable for use with
|
||||
// "defer" and "go".
|
||||
func (fr *frame) createThunk(call ssa.CallInstruction) (thunk llvm.Value, arg llvm.Value) {
|
||||
seenarg := make(map[ssa.Value]bool)
|
||||
var args []ssa.Value
|
||||
var argtypes []*types.Var
|
||||
|
||||
packArg := func(arg ssa.Value) {
|
||||
switch arg.(type) {
|
||||
case *ssa.Builtin, *ssa.Function, *ssa.Const, *ssa.Global:
|
||||
// Do nothing: we can generate these in the thunk
|
||||
default:
|
||||
if !seenarg[arg] {
|
||||
seenarg[arg] = true
|
||||
args = append(args, arg)
|
||||
field := types.NewField(0, nil, "_", arg.Type(), true)
|
||||
argtypes = append(argtypes, field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packArg(call.Common().Value)
|
||||
for _, arg := range call.Common().Args {
|
||||
packArg(arg)
|
||||
}
|
||||
|
||||
var isRecoverCall bool
|
||||
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
||||
var structllptr llvm.Type
|
||||
if len(args) == 0 {
|
||||
if builtin, ok := call.Common().Value.(*ssa.Builtin); ok {
|
||||
isRecoverCall = builtin.Name() == "recover"
|
||||
}
|
||||
if isRecoverCall {
|
||||
// When creating a thunk for recover(), we must pass fr.canRecover.
|
||||
arg = fr.builder.CreateZExt(fr.canRecover, fr.target.IntPtrType(), "")
|
||||
arg = fr.builder.CreateIntToPtr(arg, i8ptr, "")
|
||||
} else {
|
||||
arg = llvm.ConstPointerNull(i8ptr)
|
||||
}
|
||||
} else {
|
||||
structtype := types.NewStruct(argtypes, nil)
|
||||
arg = fr.createTypeMalloc(structtype)
|
||||
structllptr = arg.Type()
|
||||
for i, ssaarg := range args {
|
||||
argptr := fr.builder.CreateStructGEP(arg, i, "")
|
||||
fr.builder.CreateStore(fr.llvmvalue(ssaarg), argptr)
|
||||
}
|
||||
arg = fr.builder.CreateBitCast(arg, i8ptr, "")
|
||||
}
|
||||
|
||||
thunkfntype := llvm.FunctionType(llvm.VoidType(), []llvm.Type{i8ptr}, false)
|
||||
thunkfn := llvm.AddFunction(fr.module.Module, "", thunkfntype)
|
||||
thunkfn.SetLinkage(llvm.InternalLinkage)
|
||||
fr.addCommonFunctionAttrs(thunkfn)
|
||||
|
||||
thunkfr := newFrame(fr.unit, thunkfn)
|
||||
defer thunkfr.dispose()
|
||||
|
||||
prologuebb := llvm.AddBasicBlock(thunkfn, "prologue")
|
||||
thunkfr.builder.SetInsertPointAtEnd(prologuebb)
|
||||
|
||||
if isRecoverCall {
|
||||
thunkarg := thunkfn.Param(0)
|
||||
thunkarg = thunkfr.builder.CreatePtrToInt(thunkarg, fr.target.IntPtrType(), "")
|
||||
thunkfr.canRecover = thunkfr.builder.CreateTrunc(thunkarg, llvm.Int1Type(), "")
|
||||
} else if len(args) > 0 {
|
||||
thunkarg := thunkfn.Param(0)
|
||||
thunkarg = thunkfr.builder.CreateBitCast(thunkarg, structllptr, "")
|
||||
for i, ssaarg := range args {
|
||||
thunkargptr := thunkfr.builder.CreateStructGEP(thunkarg, i, "")
|
||||
thunkarg := thunkfr.builder.CreateLoad(thunkargptr, "")
|
||||
thunkfr.env[ssaarg] = newValue(thunkarg, ssaarg.Type())
|
||||
}
|
||||
}
|
||||
|
||||
_, isDefer := call.(*ssa.Defer)
|
||||
|
||||
entrybb := llvm.AddBasicBlock(thunkfn, "entry")
|
||||
br := thunkfr.builder.CreateBr(entrybb)
|
||||
thunkfr.allocaBuilder.SetInsertPointBefore(br)
|
||||
|
||||
thunkfr.builder.SetInsertPointAtEnd(entrybb)
|
||||
var exitbb llvm.BasicBlock
|
||||
if isDefer {
|
||||
exitbb = llvm.AddBasicBlock(thunkfn, "exit")
|
||||
thunkfr.runtime.setDeferRetaddr.call(thunkfr, llvm.BlockAddress(thunkfn, exitbb))
|
||||
}
|
||||
if isDefer && isRecoverCall {
|
||||
thunkfr.callRecover(true)
|
||||
} else {
|
||||
thunkfr.callInstruction(call)
|
||||
}
|
||||
if isDefer {
|
||||
thunkfr.builder.CreateBr(exitbb)
|
||||
thunkfr.builder.SetInsertPointAtEnd(exitbb)
|
||||
}
|
||||
thunkfr.builder.CreateRetVoid()
|
||||
|
||||
thunk = fr.builder.CreateBitCast(thunkfn, i8ptr, "")
|
||||
return
|
||||
}
|
196
llgo/irgen/interfaces.go
Normal file
196
llgo/irgen/interfaces.go
Normal file
@ -0,0 +1,196 @@
|
||||
//===- interfaces.go - IR generation for interfaces -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for dealing with interface values.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// interfaceMethod returns a function and receiver pointer for the specified
|
||||
// interface and method pair.
|
||||
func (fr *frame) interfaceMethod(lliface llvm.Value, ifacety types.Type, method *types.Func) (fn, recv *govalue) {
|
||||
llitab := fr.builder.CreateExtractValue(lliface, 0, "")
|
||||
recv = newValue(fr.builder.CreateExtractValue(lliface, 1, ""), types.Typ[types.UnsafePointer])
|
||||
methodset := fr.types.MethodSet(ifacety)
|
||||
// TODO(axw) cache ordered method index
|
||||
index := -1
|
||||
for i, m := range orderedMethodSet(methodset) {
|
||||
if m.Obj() == method {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if index == -1 {
|
||||
panic("could not find method index")
|
||||
}
|
||||
llitab = fr.builder.CreateBitCast(llitab, llvm.PointerType(llvm.PointerType(llvm.Int8Type(), 0), 0), "")
|
||||
// Skip runtime type pointer.
|
||||
llifnptr := fr.builder.CreateGEP(llitab, []llvm.Value{
|
||||
llvm.ConstInt(llvm.Int32Type(), uint64(index+1), false),
|
||||
}, "")
|
||||
|
||||
llifn := fr.builder.CreateLoad(llifnptr, "")
|
||||
// Replace receiver type with unsafe.Pointer.
|
||||
recvparam := types.NewParam(0, nil, "", types.Typ[types.UnsafePointer])
|
||||
sig := method.Type().(*types.Signature)
|
||||
sig = types.NewSignature(nil, recvparam, sig.Params(), sig.Results(), sig.Variadic())
|
||||
fn = newValue(llifn, sig)
|
||||
return
|
||||
}
|
||||
|
||||
// compareInterfaces emits code to compare two interfaces for
|
||||
// equality.
|
||||
func (fr *frame) compareInterfaces(a, b *govalue) *govalue {
|
||||
aNull := a.value.IsNull()
|
||||
bNull := b.value.IsNull()
|
||||
if aNull && bNull {
|
||||
return newValue(boolLLVMValue(true), types.Typ[types.Bool])
|
||||
}
|
||||
|
||||
compare := fr.runtime.emptyInterfaceCompare
|
||||
aI := a.Type().Underlying().(*types.Interface).NumMethods() > 0
|
||||
bI := b.Type().Underlying().(*types.Interface).NumMethods() > 0
|
||||
switch {
|
||||
case aI && bI:
|
||||
compare = fr.runtime.interfaceCompare
|
||||
case aI:
|
||||
a = fr.convertI2E(a)
|
||||
case bI:
|
||||
b = fr.convertI2E(b)
|
||||
}
|
||||
|
||||
result := compare.call(fr, a.value, b.value)[0]
|
||||
result = fr.builder.CreateIsNull(result, "")
|
||||
result = fr.builder.CreateZExt(result, llvm.Int8Type(), "")
|
||||
return newValue(result, types.Typ[types.Bool])
|
||||
}
|
||||
|
||||
func (fr *frame) makeInterface(llv llvm.Value, vty types.Type, iface types.Type) *govalue {
|
||||
if _, ok := vty.Underlying().(*types.Pointer); !ok {
|
||||
ptr := fr.createTypeMalloc(vty)
|
||||
fr.builder.CreateStore(llv, ptr)
|
||||
llv = ptr
|
||||
}
|
||||
return fr.makeInterfaceFromPointer(llv, vty, iface)
|
||||
}
|
||||
|
||||
func (fr *frame) makeInterfaceFromPointer(vptr llvm.Value, vty types.Type, iface types.Type) *govalue {
|
||||
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
||||
llv := fr.builder.CreateBitCast(vptr, i8ptr, "")
|
||||
value := llvm.Undef(fr.types.ToLLVM(iface))
|
||||
itab := fr.types.getItabPointer(vty, iface.Underlying().(*types.Interface))
|
||||
value = fr.builder.CreateInsertValue(value, itab, 0, "")
|
||||
value = fr.builder.CreateInsertValue(value, llv, 1, "")
|
||||
return newValue(value, iface)
|
||||
}
|
||||
|
||||
// Reads the type descriptor from the given interface type.
|
||||
func (fr *frame) getInterfaceTypeDescriptor(v *govalue) llvm.Value {
|
||||
isempty := v.Type().Underlying().(*types.Interface).NumMethods() == 0
|
||||
itab := fr.builder.CreateExtractValue(v.value, 0, "")
|
||||
if isempty {
|
||||
return itab
|
||||
} else {
|
||||
itabnonnull := fr.builder.CreateIsNotNull(itab, "")
|
||||
return fr.loadOrNull(itabnonnull, itab, types.Typ[types.UnsafePointer]).value
|
||||
}
|
||||
}
|
||||
|
||||
// Reads the value from the given interface type, assuming that the
|
||||
// interface holds a value of the correct type.
|
||||
func (fr *frame) getInterfaceValue(v *govalue, ty types.Type) *govalue {
|
||||
val := fr.builder.CreateExtractValue(v.value, 1, "")
|
||||
if _, ok := ty.Underlying().(*types.Pointer); !ok {
|
||||
typedval := fr.builder.CreateBitCast(val, llvm.PointerType(fr.types.ToLLVM(ty), 0), "")
|
||||
val = fr.builder.CreateLoad(typedval, "")
|
||||
}
|
||||
return newValue(val, ty)
|
||||
}
|
||||
|
||||
// If cond is true, reads the value from the given interface type, otherwise
|
||||
// returns a nil value.
|
||||
func (fr *frame) getInterfaceValueOrNull(cond llvm.Value, v *govalue, ty types.Type) *govalue {
|
||||
val := fr.builder.CreateExtractValue(v.value, 1, "")
|
||||
if _, ok := ty.Underlying().(*types.Pointer); ok {
|
||||
val = fr.builder.CreateSelect(cond, val, llvm.ConstNull(val.Type()), "")
|
||||
} else {
|
||||
val = fr.loadOrNull(cond, val, ty).value
|
||||
}
|
||||
return newValue(val, ty)
|
||||
}
|
||||
|
||||
func (fr *frame) interfaceTypeCheck(val *govalue, ty types.Type) (v *govalue, okval *govalue) {
|
||||
tytd := fr.types.ToRuntime(ty)
|
||||
if _, ok := ty.Underlying().(*types.Interface); ok {
|
||||
var result []llvm.Value
|
||||
if val.Type().Underlying().(*types.Interface).NumMethods() > 0 {
|
||||
result = fr.runtime.ifaceI2I2.call(fr, tytd, val.value)
|
||||
} else {
|
||||
result = fr.runtime.ifaceE2I2.call(fr, tytd, val.value)
|
||||
}
|
||||
v = newValue(result[0], ty)
|
||||
okval = newValue(result[1], types.Typ[types.Bool])
|
||||
} else {
|
||||
valtd := fr.getInterfaceTypeDescriptor(val)
|
||||
tyequal := fr.runtime.typeDescriptorsEqual.call(fr, valtd, tytd)[0]
|
||||
okval = newValue(tyequal, types.Typ[types.Bool])
|
||||
tyequal = fr.builder.CreateTrunc(tyequal, llvm.Int1Type(), "")
|
||||
|
||||
v = fr.getInterfaceValueOrNull(tyequal, val, ty)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (fr *frame) interfaceTypeAssert(val *govalue, ty types.Type) *govalue {
|
||||
if _, ok := ty.Underlying().(*types.Interface); ok {
|
||||
return fr.changeInterface(val, ty, true)
|
||||
} else {
|
||||
valtytd := fr.types.ToRuntime(val.Type())
|
||||
valtd := fr.getInterfaceTypeDescriptor(val)
|
||||
tytd := fr.types.ToRuntime(ty)
|
||||
fr.runtime.checkInterfaceType.call(fr, valtd, tytd, valtytd)
|
||||
|
||||
return fr.getInterfaceValue(val, ty)
|
||||
}
|
||||
}
|
||||
|
||||
// convertI2E converts a non-empty interface value to an empty interface.
|
||||
func (fr *frame) convertI2E(v *govalue) *govalue {
|
||||
td := fr.getInterfaceTypeDescriptor(v)
|
||||
val := fr.builder.CreateExtractValue(v.value, 1, "")
|
||||
|
||||
typ := types.NewInterface(nil, nil)
|
||||
intf := llvm.Undef(fr.types.ToLLVM(typ))
|
||||
intf = fr.builder.CreateInsertValue(intf, td, 0, "")
|
||||
intf = fr.builder.CreateInsertValue(intf, val, 1, "")
|
||||
return newValue(intf, typ)
|
||||
}
|
||||
|
||||
func (fr *frame) changeInterface(v *govalue, ty types.Type, assert bool) *govalue {
|
||||
td := fr.getInterfaceTypeDescriptor(v)
|
||||
tytd := fr.types.ToRuntime(ty)
|
||||
var itab llvm.Value
|
||||
if assert {
|
||||
itab = fr.runtime.assertInterface.call(fr, tytd, td)[0]
|
||||
} else {
|
||||
itab = fr.runtime.convertInterface.call(fr, tytd, td)[0]
|
||||
}
|
||||
val := fr.builder.CreateExtractValue(v.value, 1, "")
|
||||
|
||||
intf := llvm.Undef(fr.types.ToLLVM(ty))
|
||||
intf = fr.builder.CreateInsertValue(intf, itab, 0, "")
|
||||
intf = fr.builder.CreateInsertValue(intf, val, 1, "")
|
||||
return newValue(intf, ty)
|
||||
}
|
153
llgo/irgen/maps.go
Normal file
153
llgo/irgen/maps.go
Normal file
@ -0,0 +1,153 @@
|
||||
//===- maps.go - IR generation for maps -----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for maps.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// makeMap implements make(maptype[, initial space])
|
||||
func (fr *frame) makeMap(typ types.Type, cap_ *govalue) *govalue {
|
||||
// TODO(pcc): call __go_new_map_big here if needed
|
||||
dyntyp := fr.types.getMapDescriptorPointer(typ)
|
||||
dyntyp = fr.builder.CreateBitCast(dyntyp, llvm.PointerType(llvm.Int8Type(), 0), "")
|
||||
var cap llvm.Value
|
||||
if cap_ != nil {
|
||||
cap = fr.convert(cap_, types.Typ[types.Uintptr]).value
|
||||
} else {
|
||||
cap = llvm.ConstNull(fr.types.inttype)
|
||||
}
|
||||
m := fr.runtime.newMap.call(fr, dyntyp, cap)
|
||||
return newValue(m[0], typ)
|
||||
}
|
||||
|
||||
// mapLookup implements v[, ok] = m[k]
|
||||
func (fr *frame) mapLookup(m, k *govalue) (v *govalue, ok *govalue) {
|
||||
llk := k.value
|
||||
pk := fr.allocaBuilder.CreateAlloca(llk.Type(), "")
|
||||
fr.builder.CreateStore(llk, pk)
|
||||
valptr := fr.runtime.mapIndex.call(fr, m.value, pk, boolLLVMValue(false))[0]
|
||||
valptr.AddInstrAttribute(2, llvm.NoCaptureAttribute)
|
||||
valptr.AddInstrAttribute(2, llvm.ReadOnlyAttribute)
|
||||
okbit := fr.builder.CreateIsNotNull(valptr, "")
|
||||
|
||||
elemtyp := m.Type().Underlying().(*types.Map).Elem()
|
||||
ok = newValue(fr.builder.CreateZExt(okbit, llvm.Int8Type(), ""), types.Typ[types.Bool])
|
||||
v = fr.loadOrNull(okbit, valptr, elemtyp)
|
||||
return
|
||||
}
|
||||
|
||||
// mapUpdate implements m[k] = v
|
||||
func (fr *frame) mapUpdate(m, k, v *govalue) {
|
||||
llk := k.value
|
||||
pk := fr.allocaBuilder.CreateAlloca(llk.Type(), "")
|
||||
fr.builder.CreateStore(llk, pk)
|
||||
valptr := fr.runtime.mapIndex.call(fr, m.value, pk, boolLLVMValue(true))[0]
|
||||
valptr.AddInstrAttribute(2, llvm.NoCaptureAttribute)
|
||||
valptr.AddInstrAttribute(2, llvm.ReadOnlyAttribute)
|
||||
|
||||
elemtyp := m.Type().Underlying().(*types.Map).Elem()
|
||||
llelemtyp := fr.types.ToLLVM(elemtyp)
|
||||
typedvalptr := fr.builder.CreateBitCast(valptr, llvm.PointerType(llelemtyp, 0), "")
|
||||
fr.builder.CreateStore(v.value, typedvalptr)
|
||||
}
|
||||
|
||||
// mapDelete implements delete(m, k)
|
||||
func (fr *frame) mapDelete(m, k *govalue) {
|
||||
llk := k.value
|
||||
pk := fr.allocaBuilder.CreateAlloca(llk.Type(), "")
|
||||
fr.builder.CreateStore(llk, pk)
|
||||
fr.runtime.mapdelete.call(fr, m.value, pk)
|
||||
}
|
||||
|
||||
// mapIterInit creates a map iterator
|
||||
func (fr *frame) mapIterInit(m *govalue) []*govalue {
|
||||
// We represent an iterator as a tuple (map, *bool). The second element
|
||||
// controls whether the code we generate for "next" (below) calls the
|
||||
// runtime function for the first or the next element. We let the
|
||||
// optimizer reorganize this into something more sensible.
|
||||
isinit := fr.allocaBuilder.CreateAlloca(llvm.Int1Type(), "")
|
||||
fr.builder.CreateStore(llvm.ConstNull(llvm.Int1Type()), isinit)
|
||||
|
||||
return []*govalue{m, newValue(isinit, types.NewPointer(types.Typ[types.Bool]))}
|
||||
}
|
||||
|
||||
// mapIterNext advances the iterator, and returns the tuple (ok, k, v).
|
||||
func (fr *frame) mapIterNext(iter []*govalue) []*govalue {
|
||||
maptyp := iter[0].Type().Underlying().(*types.Map)
|
||||
ktyp := maptyp.Key()
|
||||
klltyp := fr.types.ToLLVM(ktyp)
|
||||
vtyp := maptyp.Elem()
|
||||
vlltyp := fr.types.ToLLVM(vtyp)
|
||||
|
||||
m, isinitptr := iter[0], iter[1]
|
||||
|
||||
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
||||
mapiterbufty := llvm.ArrayType(i8ptr, 4)
|
||||
mapiterbuf := fr.allocaBuilder.CreateAlloca(mapiterbufty, "")
|
||||
mapiterbufelem0ptr := fr.builder.CreateStructGEP(mapiterbuf, 0, "")
|
||||
|
||||
keybuf := fr.allocaBuilder.CreateAlloca(klltyp, "")
|
||||
keyptr := fr.builder.CreateBitCast(keybuf, i8ptr, "")
|
||||
valbuf := fr.allocaBuilder.CreateAlloca(vlltyp, "")
|
||||
valptr := fr.builder.CreateBitCast(valbuf, i8ptr, "")
|
||||
|
||||
isinit := fr.builder.CreateLoad(isinitptr.value, "")
|
||||
|
||||
initbb := llvm.AddBasicBlock(fr.function, "")
|
||||
nextbb := llvm.AddBasicBlock(fr.function, "")
|
||||
contbb := llvm.AddBasicBlock(fr.function, "")
|
||||
|
||||
fr.builder.CreateCondBr(isinit, nextbb, initbb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(initbb)
|
||||
fr.builder.CreateStore(llvm.ConstAllOnes(llvm.Int1Type()), isinitptr.value)
|
||||
fr.runtime.mapiterinit.call(fr, m.value, mapiterbufelem0ptr)
|
||||
fr.builder.CreateBr(contbb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(nextbb)
|
||||
fr.runtime.mapiternext.call(fr, mapiterbufelem0ptr)
|
||||
fr.builder.CreateBr(contbb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(contbb)
|
||||
mapiterbufelem0 := fr.builder.CreateLoad(mapiterbufelem0ptr, "")
|
||||
okbit := fr.builder.CreateIsNotNull(mapiterbufelem0, "")
|
||||
ok := fr.builder.CreateZExt(okbit, llvm.Int8Type(), "")
|
||||
|
||||
loadbb := llvm.AddBasicBlock(fr.function, "")
|
||||
cont2bb := llvm.AddBasicBlock(fr.function, "")
|
||||
fr.builder.CreateCondBr(okbit, loadbb, cont2bb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(loadbb)
|
||||
fr.runtime.mapiter2.call(fr, mapiterbufelem0ptr, keyptr, valptr)
|
||||
loadbb = fr.builder.GetInsertBlock()
|
||||
loadedkey := fr.builder.CreateLoad(keybuf, "")
|
||||
loadedval := fr.builder.CreateLoad(valbuf, "")
|
||||
fr.builder.CreateBr(cont2bb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(cont2bb)
|
||||
k := fr.builder.CreatePHI(klltyp, "")
|
||||
k.AddIncoming(
|
||||
[]llvm.Value{llvm.ConstNull(klltyp), loadedkey},
|
||||
[]llvm.BasicBlock{contbb, loadbb},
|
||||
)
|
||||
v := fr.builder.CreatePHI(vlltyp, "")
|
||||
v.AddIncoming(
|
||||
[]llvm.Value{llvm.ConstNull(vlltyp), loadedval},
|
||||
[]llvm.BasicBlock{contbb, loadbb},
|
||||
)
|
||||
|
||||
return []*govalue{newValue(ok, types.Typ[types.Bool]), newValue(k, ktyp), newValue(v, vtyp)}
|
||||
}
|
42
llgo/irgen/parser.go
Normal file
42
llgo/irgen/parser.go
Normal file
@ -0,0 +1,42 @@
|
||||
//===- parser.go - parser wrapper -----------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains functions for calling the parser in an appropriate way for
|
||||
// llgo.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/scanner"
|
||||
"go/token"
|
||||
)
|
||||
|
||||
func parseFile(fset *token.FileSet, filename string) (*ast.File, error) {
|
||||
mode := parser.DeclarationErrors | parser.ParseComments
|
||||
return parser.ParseFile(fset, filename, nil, mode)
|
||||
}
|
||||
|
||||
func parseFiles(fset *token.FileSet, filenames []string) ([]*ast.File, error) {
|
||||
files := make([]*ast.File, len(filenames))
|
||||
for i, filename := range filenames {
|
||||
file, err := parseFile(fset, filename)
|
||||
if _, ok := err.(scanner.ErrorList); ok {
|
||||
return nil, err
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("%q: %v", filename, err)
|
||||
}
|
||||
files[i] = file
|
||||
}
|
||||
return files, nil
|
||||
}
|
58
llgo/irgen/predicates.go
Normal file
58
llgo/irgen/predicates.go
Normal file
@ -0,0 +1,58 @@
|
||||
//===- predicates.go - type predicates ------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements commonly used type predicates.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
)
|
||||
|
||||
func isBoolean(typ types.Type) bool {
|
||||
t, ok := typ.Underlying().(*types.Basic)
|
||||
return ok && t.Info()&types.IsBoolean != 0
|
||||
}
|
||||
|
||||
func isInteger(typ types.Type) bool {
|
||||
t, ok := typ.Underlying().(*types.Basic)
|
||||
return ok && t.Info()&types.IsInteger != 0
|
||||
}
|
||||
|
||||
func isUnsigned(typ types.Type) bool {
|
||||
t, ok := typ.Underlying().(*types.Basic)
|
||||
return ok && t.Info()&types.IsUnsigned != 0
|
||||
}
|
||||
|
||||
func isFloat(typ types.Type) bool {
|
||||
t, ok := typ.Underlying().(*types.Basic)
|
||||
return ok && t.Info()&types.IsFloat != 0
|
||||
}
|
||||
|
||||
func isComplex(typ types.Type) bool {
|
||||
t, ok := typ.Underlying().(*types.Basic)
|
||||
return ok && t.Info()&types.IsComplex != 0
|
||||
}
|
||||
|
||||
func isString(typ types.Type) bool {
|
||||
t, ok := typ.Underlying().(*types.Basic)
|
||||
return ok && t.Info()&types.IsString != 0
|
||||
}
|
||||
|
||||
func isUntyped(typ types.Type) bool {
|
||||
t, ok := typ.Underlying().(*types.Basic)
|
||||
return ok && t.Info()&types.IsUntyped != 0
|
||||
}
|
||||
|
||||
func isSlice(typ types.Type, bkind types.BasicKind) bool {
|
||||
t, ok := typ.Underlying().(*types.Slice)
|
||||
return ok && types.Identical(t.Elem().Underlying(), types.Typ[bkind])
|
||||
}
|
93
llgo/irgen/println.go
Normal file
93
llgo/irgen/println.go
Normal file
@ -0,0 +1,93 @@
|
||||
//===- println.go - IR generation for print and println -------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for the print and println built-in
|
||||
// functions.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
)
|
||||
|
||||
func (fr *frame) printValues(println_ bool, values ...*govalue) {
|
||||
for i, value := range values {
|
||||
llvm_value := value.value
|
||||
|
||||
typ := value.Type().Underlying()
|
||||
if name, isname := typ.(*types.Named); isname {
|
||||
typ = name.Underlying()
|
||||
}
|
||||
|
||||
if println_ && i > 0 {
|
||||
fr.runtime.printSpace.call(fr)
|
||||
}
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
switch typ.Kind() {
|
||||
case types.Uint8, types.Uint16, types.Uint32, types.Uintptr, types.Uint, types.Uint64:
|
||||
i64 := fr.llvmtypes.ctx.Int64Type()
|
||||
zext := fr.builder.CreateZExt(llvm_value, i64, "")
|
||||
fr.runtime.printUint64.call(fr, zext)
|
||||
|
||||
case types.Int, types.Int8, types.Int16, types.Int32, types.Int64:
|
||||
i64 := fr.llvmtypes.ctx.Int64Type()
|
||||
sext := fr.builder.CreateSExt(llvm_value, i64, "")
|
||||
fr.runtime.printInt64.call(fr, sext)
|
||||
|
||||
case types.Float32:
|
||||
llvm_value = fr.builder.CreateFPExt(llvm_value, fr.llvmtypes.ctx.DoubleType(), "")
|
||||
fallthrough
|
||||
case types.Float64:
|
||||
fr.runtime.printDouble.call(fr, llvm_value)
|
||||
|
||||
case types.Complex64:
|
||||
llvm_value = fr.convert(value, types.Typ[types.Complex128]).value
|
||||
fallthrough
|
||||
case types.Complex128:
|
||||
fr.runtime.printComplex.call(fr, llvm_value)
|
||||
|
||||
case types.String, types.UntypedString:
|
||||
fr.runtime.printString.call(fr, llvm_value)
|
||||
|
||||
case types.Bool:
|
||||
fr.runtime.printBool.call(fr, llvm_value)
|
||||
|
||||
case types.UnsafePointer:
|
||||
fr.runtime.printPointer.call(fr, llvm_value)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprint("Unhandled Basic Kind: ", typ.Kind))
|
||||
}
|
||||
|
||||
case *types.Interface:
|
||||
if typ.Empty() {
|
||||
fr.runtime.printEmptyInterface.call(fr, llvm_value)
|
||||
} else {
|
||||
fr.runtime.printInterface.call(fr, llvm_value)
|
||||
}
|
||||
|
||||
case *types.Slice:
|
||||
fr.runtime.printSlice.call(fr, llvm_value)
|
||||
|
||||
case *types.Pointer, *types.Map, *types.Chan, *types.Signature:
|
||||
fr.runtime.printPointer.call(fr, llvm_value)
|
||||
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled type kind: %s (%T)", typ, typ))
|
||||
}
|
||||
}
|
||||
if println_ {
|
||||
fr.runtime.printNl.call(fr)
|
||||
}
|
||||
}
|
614
llgo/irgen/runtime.go
Normal file
614
llgo/irgen/runtime.go
Normal file
@ -0,0 +1,614 @@
|
||||
//===- runtime.go - IR generation for runtime calls -----------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for calls to the runtime library.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
type runtimeFnInfo struct {
|
||||
fi *functionTypeInfo
|
||||
fn llvm.Value
|
||||
}
|
||||
|
||||
func (rfi *runtimeFnInfo) init(tm *llvmTypeMap, m llvm.Module, name string, args []types.Type, results []types.Type) {
|
||||
rfi.fi = new(functionTypeInfo)
|
||||
*rfi.fi = tm.getFunctionTypeInfo(args, results)
|
||||
rfi.fn = rfi.fi.declare(m, name)
|
||||
}
|
||||
|
||||
func (rfi *runtimeFnInfo) call(f *frame, args ...llvm.Value) []llvm.Value {
|
||||
if f.unwindBlock.IsNil() {
|
||||
return rfi.callOnly(f, args...)
|
||||
} else {
|
||||
return rfi.invoke(f, f.unwindBlock, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (rfi *runtimeFnInfo) callOnly(f *frame, args ...llvm.Value) []llvm.Value {
|
||||
return rfi.fi.call(f.llvmtypes.ctx, f.allocaBuilder, f.builder, rfi.fn, args)
|
||||
}
|
||||
|
||||
func (rfi *runtimeFnInfo) invoke(f *frame, lpad llvm.BasicBlock, args ...llvm.Value) []llvm.Value {
|
||||
contbb := llvm.AddBasicBlock(f.function, "")
|
||||
return rfi.fi.invoke(f.llvmtypes.ctx, f.allocaBuilder, f.builder, rfi.fn, args, contbb, lpad)
|
||||
}
|
||||
|
||||
// runtimeInterface is a struct containing references to
|
||||
// runtime types and intrinsic function declarations.
|
||||
type runtimeInterface struct {
|
||||
// LLVM intrinsics
|
||||
memcpy,
|
||||
memset,
|
||||
returnaddress llvm.Value
|
||||
|
||||
// Exception handling support
|
||||
gccgoPersonality llvm.Value
|
||||
gccgoExceptionType llvm.Type
|
||||
|
||||
// Runtime intrinsics
|
||||
append,
|
||||
assertInterface,
|
||||
canRecover,
|
||||
chanCap,
|
||||
chanLen,
|
||||
chanrecv2,
|
||||
checkDefer,
|
||||
checkInterfaceType,
|
||||
builtinClose,
|
||||
convertInterface,
|
||||
copy,
|
||||
Defer,
|
||||
deferredRecover,
|
||||
emptyInterfaceCompare,
|
||||
getClosure,
|
||||
Go,
|
||||
ifaceE2I2,
|
||||
ifaceI2I2,
|
||||
intArrayToString,
|
||||
interfaceCompare,
|
||||
intToString,
|
||||
makeSlice,
|
||||
mapdelete,
|
||||
mapiter2,
|
||||
mapiterinit,
|
||||
mapiternext,
|
||||
mapIndex,
|
||||
mapLen,
|
||||
New,
|
||||
newChannel,
|
||||
newMap,
|
||||
NewNopointers,
|
||||
newSelect,
|
||||
panic,
|
||||
printBool,
|
||||
printComplex,
|
||||
printDouble,
|
||||
printEmptyInterface,
|
||||
printInterface,
|
||||
printInt64,
|
||||
printNl,
|
||||
printPointer,
|
||||
printSlice,
|
||||
printSpace,
|
||||
printString,
|
||||
printUint64,
|
||||
receive,
|
||||
recover,
|
||||
registerGcRoots,
|
||||
runtimeError,
|
||||
selectdefault,
|
||||
selectrecv2,
|
||||
selectsend,
|
||||
selectgo,
|
||||
sendBig,
|
||||
setClosure,
|
||||
setDeferRetaddr,
|
||||
strcmp,
|
||||
stringiter2,
|
||||
stringPlus,
|
||||
stringSlice,
|
||||
stringToIntArray,
|
||||
typeDescriptorsEqual,
|
||||
undefer runtimeFnInfo
|
||||
}
|
||||
|
||||
func newRuntimeInterface(module llvm.Module, tm *llvmTypeMap) (*runtimeInterface, error) {
|
||||
var ri runtimeInterface
|
||||
|
||||
Bool := types.Typ[types.Bool]
|
||||
Complex128 := types.Typ[types.Complex128]
|
||||
Float64 := types.Typ[types.Float64]
|
||||
Int32 := types.Typ[types.Int32]
|
||||
Int64 := types.Typ[types.Int64]
|
||||
Int := types.Typ[types.Int]
|
||||
Rune := types.Typ[types.Rune]
|
||||
String := types.Typ[types.String]
|
||||
Uintptr := types.Typ[types.Uintptr]
|
||||
UnsafePointer := types.Typ[types.UnsafePointer]
|
||||
|
||||
EmptyInterface := types.NewInterface(nil, nil)
|
||||
IntSlice := types.NewSlice(types.Typ[types.Int])
|
||||
|
||||
for _, rt := range [...]struct {
|
||||
name string
|
||||
rfi *runtimeFnInfo
|
||||
args, res []types.Type
|
||||
attrs []llvm.Attribute
|
||||
}{
|
||||
{
|
||||
name: "__go_append",
|
||||
rfi: &ri.append,
|
||||
args: []types.Type{IntSlice, UnsafePointer, Uintptr, Uintptr},
|
||||
res: []types.Type{IntSlice},
|
||||
},
|
||||
{
|
||||
name: "__go_assert_interface",
|
||||
rfi: &ri.assertInterface,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer},
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_can_recover",
|
||||
rfi: &ri.canRecover,
|
||||
args: []types.Type{UnsafePointer},
|
||||
res: []types.Type{Bool},
|
||||
},
|
||||
{
|
||||
name: "__go_chan_cap",
|
||||
rfi: &ri.chanCap,
|
||||
args: []types.Type{UnsafePointer},
|
||||
res: []types.Type{Int},
|
||||
},
|
||||
{
|
||||
name: "__go_chan_len",
|
||||
rfi: &ri.chanLen,
|
||||
args: []types.Type{UnsafePointer},
|
||||
res: []types.Type{Int},
|
||||
},
|
||||
{
|
||||
name: "runtime.chanrecv2",
|
||||
rfi: &ri.chanrecv2,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
|
||||
res: []types.Type{Bool},
|
||||
},
|
||||
{
|
||||
name: "__go_check_defer",
|
||||
rfi: &ri.checkDefer,
|
||||
args: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_check_interface_type",
|
||||
rfi: &ri.checkInterfaceType,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_builtin_close",
|
||||
rfi: &ri.builtinClose,
|
||||
args: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_convert_interface",
|
||||
rfi: &ri.convertInterface,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer},
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_copy",
|
||||
rfi: &ri.copy,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, Uintptr},
|
||||
},
|
||||
{
|
||||
name: "__go_defer",
|
||||
rfi: &ri.Defer,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_deferred_recover",
|
||||
rfi: &ri.deferredRecover,
|
||||
res: []types.Type{EmptyInterface},
|
||||
},
|
||||
{
|
||||
name: "__go_empty_interface_compare",
|
||||
rfi: &ri.emptyInterfaceCompare,
|
||||
args: []types.Type{EmptyInterface, EmptyInterface},
|
||||
res: []types.Type{Int},
|
||||
},
|
||||
{
|
||||
name: "__go_get_closure",
|
||||
rfi: &ri.getClosure,
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_go",
|
||||
rfi: &ri.Go,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "runtime.ifaceE2I2",
|
||||
rfi: &ri.ifaceE2I2,
|
||||
args: []types.Type{UnsafePointer, EmptyInterface},
|
||||
res: []types.Type{EmptyInterface, Bool},
|
||||
},
|
||||
{
|
||||
name: "runtime.ifaceI2I2",
|
||||
rfi: &ri.ifaceI2I2,
|
||||
args: []types.Type{UnsafePointer, EmptyInterface},
|
||||
res: []types.Type{EmptyInterface, Bool},
|
||||
},
|
||||
{
|
||||
name: "__go_int_array_to_string",
|
||||
rfi: &ri.intArrayToString,
|
||||
args: []types.Type{UnsafePointer, Int},
|
||||
res: []types.Type{String},
|
||||
},
|
||||
{
|
||||
name: "__go_int_to_string",
|
||||
rfi: &ri.intToString,
|
||||
args: []types.Type{Int},
|
||||
res: []types.Type{String},
|
||||
},
|
||||
{
|
||||
name: "__go_interface_compare",
|
||||
rfi: &ri.interfaceCompare,
|
||||
args: []types.Type{EmptyInterface, EmptyInterface},
|
||||
res: []types.Type{Int},
|
||||
},
|
||||
{
|
||||
name: "__go_make_slice2",
|
||||
rfi: &ri.makeSlice,
|
||||
args: []types.Type{UnsafePointer, Uintptr, Uintptr},
|
||||
res: []types.Type{IntSlice},
|
||||
},
|
||||
{
|
||||
name: "runtime.mapdelete",
|
||||
rfi: &ri.mapdelete,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "runtime.mapiter2",
|
||||
rfi: &ri.mapiter2,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "runtime.mapiterinit",
|
||||
rfi: &ri.mapiterinit,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "runtime.mapiternext",
|
||||
rfi: &ri.mapiternext,
|
||||
args: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_map_index",
|
||||
rfi: &ri.mapIndex,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, Bool},
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_map_len",
|
||||
rfi: &ri.mapLen,
|
||||
args: []types.Type{UnsafePointer},
|
||||
res: []types.Type{Int},
|
||||
},
|
||||
{
|
||||
name: "__go_new",
|
||||
rfi: &ri.New,
|
||||
args: []types.Type{UnsafePointer, Uintptr},
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_new_channel",
|
||||
rfi: &ri.newChannel,
|
||||
args: []types.Type{UnsafePointer, Uintptr},
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_new_map",
|
||||
rfi: &ri.newMap,
|
||||
args: []types.Type{UnsafePointer, Uintptr},
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_new_nopointers",
|
||||
rfi: &ri.NewNopointers,
|
||||
args: []types.Type{UnsafePointer, Uintptr},
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "runtime.newselect",
|
||||
rfi: &ri.newSelect,
|
||||
args: []types.Type{Int32},
|
||||
res: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_panic",
|
||||
rfi: &ri.panic,
|
||||
args: []types.Type{EmptyInterface},
|
||||
attrs: []llvm.Attribute{llvm.NoReturnAttribute},
|
||||
},
|
||||
{
|
||||
name: "__go_print_bool",
|
||||
rfi: &ri.printBool,
|
||||
args: []types.Type{Bool},
|
||||
},
|
||||
{
|
||||
name: "__go_print_complex",
|
||||
rfi: &ri.printComplex,
|
||||
args: []types.Type{Complex128},
|
||||
},
|
||||
{
|
||||
name: "__go_print_double",
|
||||
rfi: &ri.printDouble,
|
||||
args: []types.Type{Float64},
|
||||
},
|
||||
{
|
||||
name: "__go_print_empty_interface",
|
||||
rfi: &ri.printEmptyInterface,
|
||||
args: []types.Type{EmptyInterface},
|
||||
},
|
||||
{
|
||||
name: "__go_print_interface",
|
||||
rfi: &ri.printInterface,
|
||||
args: []types.Type{EmptyInterface},
|
||||
},
|
||||
{
|
||||
name: "__go_print_int64",
|
||||
rfi: &ri.printInt64,
|
||||
args: []types.Type{Int64},
|
||||
},
|
||||
{
|
||||
name: "__go_print_nl",
|
||||
rfi: &ri.printNl,
|
||||
},
|
||||
{
|
||||
name: "__go_print_pointer",
|
||||
rfi: &ri.printPointer,
|
||||
args: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_print_slice",
|
||||
rfi: &ri.printSlice,
|
||||
args: []types.Type{IntSlice},
|
||||
},
|
||||
{
|
||||
name: "__go_print_space",
|
||||
rfi: &ri.printSpace,
|
||||
},
|
||||
{
|
||||
name: "__go_print_string",
|
||||
rfi: &ri.printString,
|
||||
args: []types.Type{String},
|
||||
},
|
||||
{
|
||||
name: "__go_print_uint64",
|
||||
rfi: &ri.printUint64,
|
||||
args: []types.Type{Int64},
|
||||
},
|
||||
{
|
||||
name: "__go_receive",
|
||||
rfi: &ri.receive,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_recover",
|
||||
rfi: &ri.recover,
|
||||
res: []types.Type{EmptyInterface},
|
||||
},
|
||||
{
|
||||
name: "__go_register_gc_roots",
|
||||
rfi: &ri.registerGcRoots,
|
||||
args: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_runtime_error",
|
||||
rfi: &ri.runtimeError,
|
||||
args: []types.Type{Int32},
|
||||
attrs: []llvm.Attribute{llvm.NoReturnAttribute},
|
||||
},
|
||||
{
|
||||
name: "runtime.selectdefault",
|
||||
rfi: &ri.selectdefault,
|
||||
args: []types.Type{UnsafePointer, Int32},
|
||||
},
|
||||
{
|
||||
name: "runtime.selectgo",
|
||||
rfi: &ri.selectgo,
|
||||
args: []types.Type{UnsafePointer},
|
||||
res: []types.Type{Int},
|
||||
},
|
||||
{
|
||||
name: "runtime.selectrecv2",
|
||||
rfi: &ri.selectrecv2,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer, UnsafePointer, Int32},
|
||||
},
|
||||
{
|
||||
name: "runtime.selectsend",
|
||||
rfi: &ri.selectsend,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer, Int32},
|
||||
},
|
||||
{
|
||||
name: "__go_send_big",
|
||||
rfi: &ri.sendBig,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer, UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_set_closure",
|
||||
rfi: &ri.setClosure,
|
||||
args: []types.Type{UnsafePointer},
|
||||
},
|
||||
{
|
||||
name: "__go_set_defer_retaddr",
|
||||
rfi: &ri.setDeferRetaddr,
|
||||
args: []types.Type{UnsafePointer},
|
||||
res: []types.Type{Bool},
|
||||
},
|
||||
{
|
||||
name: "__go_strcmp",
|
||||
rfi: &ri.strcmp,
|
||||
args: []types.Type{String, String},
|
||||
res: []types.Type{Int},
|
||||
},
|
||||
{
|
||||
name: "__go_string_plus",
|
||||
rfi: &ri.stringPlus,
|
||||
args: []types.Type{String, String},
|
||||
res: []types.Type{String},
|
||||
},
|
||||
{
|
||||
name: "__go_string_slice",
|
||||
rfi: &ri.stringSlice,
|
||||
args: []types.Type{String, Int, Int},
|
||||
res: []types.Type{String},
|
||||
},
|
||||
{
|
||||
name: "__go_string_to_int_array",
|
||||
rfi: &ri.stringToIntArray,
|
||||
args: []types.Type{String},
|
||||
res: []types.Type{IntSlice},
|
||||
},
|
||||
{
|
||||
name: "runtime.stringiter2",
|
||||
rfi: &ri.stringiter2,
|
||||
args: []types.Type{String, Int},
|
||||
res: []types.Type{Int, Rune},
|
||||
},
|
||||
{
|
||||
name: "__go_type_descriptors_equal",
|
||||
rfi: &ri.typeDescriptorsEqual,
|
||||
args: []types.Type{UnsafePointer, UnsafePointer},
|
||||
res: []types.Type{Bool},
|
||||
},
|
||||
{
|
||||
name: "__go_undefer",
|
||||
rfi: &ri.undefer,
|
||||
args: []types.Type{UnsafePointer},
|
||||
},
|
||||
} {
|
||||
rt.rfi.init(tm, module, rt.name, rt.args, rt.res)
|
||||
for _, attr := range rt.attrs {
|
||||
rt.rfi.fn.AddFunctionAttr(attr)
|
||||
}
|
||||
}
|
||||
|
||||
memsetName := "llvm.memset.p0i8.i" + strconv.Itoa(tm.target.IntPtrType().IntTypeWidth())
|
||||
memsetType := llvm.FunctionType(
|
||||
llvm.VoidType(),
|
||||
[]llvm.Type{
|
||||
llvm.PointerType(llvm.Int8Type(), 0),
|
||||
llvm.Int8Type(),
|
||||
tm.target.IntPtrType(),
|
||||
llvm.Int32Type(),
|
||||
llvm.Int1Type(),
|
||||
},
|
||||
false,
|
||||
)
|
||||
ri.memset = llvm.AddFunction(module, memsetName, memsetType)
|
||||
|
||||
memcpyName := "llvm.memcpy.p0i8.p0i8.i" + strconv.Itoa(tm.target.IntPtrType().IntTypeWidth())
|
||||
memcpyType := llvm.FunctionType(
|
||||
llvm.VoidType(),
|
||||
[]llvm.Type{
|
||||
llvm.PointerType(llvm.Int8Type(), 0),
|
||||
llvm.PointerType(llvm.Int8Type(), 0),
|
||||
tm.target.IntPtrType(),
|
||||
llvm.Int32Type(),
|
||||
llvm.Int1Type(),
|
||||
},
|
||||
false,
|
||||
)
|
||||
ri.memcpy = llvm.AddFunction(module, memcpyName, memcpyType)
|
||||
|
||||
returnaddressType := llvm.FunctionType(
|
||||
llvm.PointerType(llvm.Int8Type(), 0),
|
||||
[]llvm.Type{llvm.Int32Type()},
|
||||
false,
|
||||
)
|
||||
ri.returnaddress = llvm.AddFunction(module, "llvm.returnaddress", returnaddressType)
|
||||
|
||||
gccgoPersonalityType := llvm.FunctionType(
|
||||
llvm.Int32Type(),
|
||||
[]llvm.Type{
|
||||
llvm.Int32Type(),
|
||||
llvm.Int64Type(),
|
||||
llvm.PointerType(llvm.Int8Type(), 0),
|
||||
llvm.PointerType(llvm.Int8Type(), 0),
|
||||
},
|
||||
false,
|
||||
)
|
||||
ri.gccgoPersonality = llvm.AddFunction(module, "__gccgo_personality_v0", gccgoPersonalityType)
|
||||
|
||||
ri.gccgoExceptionType = llvm.StructType(
|
||||
[]llvm.Type{
|
||||
llvm.PointerType(llvm.Int8Type(), 0),
|
||||
llvm.Int32Type(),
|
||||
},
|
||||
false,
|
||||
)
|
||||
|
||||
return &ri, nil
|
||||
}
|
||||
|
||||
func (fr *frame) createZExtOrTrunc(v llvm.Value, t llvm.Type, name string) llvm.Value {
|
||||
switch n := v.Type().IntTypeWidth() - t.IntTypeWidth(); {
|
||||
case n < 0:
|
||||
v = fr.builder.CreateZExt(v, fr.target.IntPtrType(), name)
|
||||
case n > 0:
|
||||
v = fr.builder.CreateTrunc(v, fr.target.IntPtrType(), name)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func (fr *frame) createMalloc(size llvm.Value) llvm.Value {
|
||||
return fr.runtime.NewNopointers.callOnly(fr,
|
||||
llvm.ConstNull(llvm.PointerType(llvm.Int8Type(), 0)),
|
||||
fr.createZExtOrTrunc(size, fr.target.IntPtrType(), ""))[0]
|
||||
}
|
||||
|
||||
func (fr *frame) createTypeMalloc(t types.Type) llvm.Value {
|
||||
size := llvm.ConstInt(fr.target.IntPtrType(), uint64(fr.llvmtypes.Sizeof(t)), false)
|
||||
malloc := fr.runtime.New.callOnly(fr, fr.types.ToRuntime(t), size)[0]
|
||||
return fr.builder.CreateBitCast(malloc, llvm.PointerType(fr.types.ToLLVM(t), 0), "")
|
||||
}
|
||||
|
||||
func (fr *frame) memsetZero(ptr llvm.Value, size llvm.Value) {
|
||||
memset := fr.runtime.memset
|
||||
ptr = fr.builder.CreateBitCast(ptr, llvm.PointerType(llvm.Int8Type(), 0), "")
|
||||
fill := llvm.ConstNull(llvm.Int8Type())
|
||||
size = fr.createZExtOrTrunc(size, fr.target.IntPtrType(), "")
|
||||
align := llvm.ConstInt(llvm.Int32Type(), 1, false)
|
||||
isvolatile := llvm.ConstNull(llvm.Int1Type())
|
||||
fr.builder.CreateCall(memset, []llvm.Value{ptr, fill, size, align, isvolatile}, "")
|
||||
}
|
||||
|
||||
func (fr *frame) memcpy(dest llvm.Value, src llvm.Value, size llvm.Value) {
|
||||
memcpy := fr.runtime.memcpy
|
||||
dest = fr.builder.CreateBitCast(dest, llvm.PointerType(llvm.Int8Type(), 0), "")
|
||||
src = fr.builder.CreateBitCast(src, llvm.PointerType(llvm.Int8Type(), 0), "")
|
||||
size = fr.createZExtOrTrunc(size, fr.target.IntPtrType(), "")
|
||||
align := llvm.ConstInt(llvm.Int32Type(), 1, false)
|
||||
isvolatile := llvm.ConstNull(llvm.Int1Type())
|
||||
fr.builder.CreateCall(memcpy, []llvm.Value{dest, src, size, align, isvolatile}, "")
|
||||
}
|
||||
|
||||
func (fr *frame) returnAddress(level uint64) llvm.Value {
|
||||
returnaddress := fr.runtime.returnaddress
|
||||
levelValue := llvm.ConstInt(llvm.Int32Type(), level, false)
|
||||
return fr.builder.CreateCall(returnaddress, []llvm.Value{levelValue}, "")
|
||||
}
|
106
llgo/irgen/slice.go
Normal file
106
llgo/irgen/slice.go
Normal file
@ -0,0 +1,106 @@
|
||||
//===- slice.go - IR generation for slices --------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for slices.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// makeSlice allocates a new slice with the optional length and capacity,
|
||||
// initialising its contents to their zero values.
|
||||
func (fr *frame) makeSlice(sliceType types.Type, length, capacity *govalue) *govalue {
|
||||
length = fr.convert(length, types.Typ[types.Uintptr])
|
||||
capacity = fr.convert(capacity, types.Typ[types.Uintptr])
|
||||
runtimeType := fr.types.ToRuntime(sliceType)
|
||||
llslice := fr.runtime.makeSlice.call(fr, runtimeType, length.value, capacity.value)
|
||||
return newValue(llslice[0], sliceType)
|
||||
}
|
||||
|
||||
func (fr *frame) slice(x llvm.Value, xtyp types.Type, low, high, max llvm.Value) llvm.Value {
|
||||
if !low.IsNil() {
|
||||
low = fr.createZExtOrTrunc(low, fr.types.inttype, "")
|
||||
} else {
|
||||
low = llvm.ConstNull(fr.types.inttype)
|
||||
}
|
||||
if !high.IsNil() {
|
||||
high = fr.createZExtOrTrunc(high, fr.types.inttype, "")
|
||||
}
|
||||
if !max.IsNil() {
|
||||
max = fr.createZExtOrTrunc(max, fr.types.inttype, "")
|
||||
}
|
||||
|
||||
var arrayptr, arraylen, arraycap llvm.Value
|
||||
var elemtyp types.Type
|
||||
var errcode uint64
|
||||
switch typ := xtyp.Underlying().(type) {
|
||||
case *types.Pointer: // *array
|
||||
errcode = gccgoRuntimeErrorARRAY_SLICE_OUT_OF_BOUNDS
|
||||
arraytyp := typ.Elem().Underlying().(*types.Array)
|
||||
elemtyp = arraytyp.Elem()
|
||||
arrayptr = x
|
||||
arrayptr = fr.builder.CreateBitCast(arrayptr, llvm.PointerType(llvm.Int8Type(), 0), "")
|
||||
arraylen = llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false)
|
||||
arraycap = arraylen
|
||||
case *types.Slice:
|
||||
errcode = gccgoRuntimeErrorSLICE_SLICE_OUT_OF_BOUNDS
|
||||
elemtyp = typ.Elem()
|
||||
arrayptr = fr.builder.CreateExtractValue(x, 0, "")
|
||||
arraylen = fr.builder.CreateExtractValue(x, 1, "")
|
||||
arraycap = fr.builder.CreateExtractValue(x, 2, "")
|
||||
case *types.Basic:
|
||||
if high.IsNil() {
|
||||
high = llvm.ConstAllOnes(fr.types.inttype) // -1
|
||||
}
|
||||
result := fr.runtime.stringSlice.call(fr, x, low, high)
|
||||
return result[0]
|
||||
default:
|
||||
panic("unimplemented")
|
||||
}
|
||||
if high.IsNil() {
|
||||
high = arraylen
|
||||
}
|
||||
if max.IsNil() {
|
||||
max = arraycap
|
||||
}
|
||||
|
||||
// Bounds checking: 0 <= low <= high <= max <= cap
|
||||
zero := llvm.ConstNull(fr.types.inttype)
|
||||
l0 := fr.builder.CreateICmp(llvm.IntSLT, low, zero, "")
|
||||
hl := fr.builder.CreateICmp(llvm.IntSLT, high, low, "")
|
||||
mh := fr.builder.CreateICmp(llvm.IntSLT, max, high, "")
|
||||
cm := fr.builder.CreateICmp(llvm.IntSLT, arraycap, max, "")
|
||||
|
||||
cond := fr.builder.CreateOr(l0, hl, "")
|
||||
cond = fr.builder.CreateOr(cond, mh, "")
|
||||
cond = fr.builder.CreateOr(cond, cm, "")
|
||||
|
||||
fr.condBrRuntimeError(cond, errcode)
|
||||
|
||||
slicelen := fr.builder.CreateSub(high, low, "")
|
||||
slicecap := fr.builder.CreateSub(max, low, "")
|
||||
|
||||
elemsize := llvm.ConstInt(fr.llvmtypes.inttype, uint64(fr.llvmtypes.Sizeof(elemtyp)), false)
|
||||
offset := fr.builder.CreateMul(low, elemsize, "")
|
||||
|
||||
sliceptr := fr.builder.CreateInBoundsGEP(arrayptr, []llvm.Value{offset}, "")
|
||||
|
||||
llslicetyp := fr.llvmtypes.sliceBackendType().ToLLVM(fr.llvmtypes.ctx)
|
||||
sliceValue := llvm.Undef(llslicetyp)
|
||||
sliceValue = fr.builder.CreateInsertValue(sliceValue, sliceptr, 0, "")
|
||||
sliceValue = fr.builder.CreateInsertValue(sliceValue, slicelen, 1, "")
|
||||
sliceValue = fr.builder.CreateInsertValue(sliceValue, slicecap, 2, "")
|
||||
|
||||
return sliceValue
|
||||
}
|
1303
llgo/irgen/ssa.go
Normal file
1303
llgo/irgen/ssa.go
Normal file
File diff suppressed because it is too large
Load Diff
97
llgo/irgen/strings.go
Normal file
97
llgo/irgen/strings.go
Normal file
@ -0,0 +1,97 @@
|
||||
//===- strings.go - IR generation for string ops --------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements IR generation for string operations.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
func (fr *frame) concatenateStrings(lhs, rhs *govalue) *govalue {
|
||||
result := fr.runtime.stringPlus.call(fr, lhs.value, rhs.value)
|
||||
return newValue(result[0], types.Typ[types.String])
|
||||
}
|
||||
|
||||
func (fr *frame) compareStrings(lhs, rhs *govalue, op token.Token) *govalue {
|
||||
result := fr.runtime.strcmp.call(fr, lhs.value, rhs.value)[0]
|
||||
zero := llvm.ConstNull(fr.types.inttype)
|
||||
var pred llvm.IntPredicate
|
||||
switch op {
|
||||
case token.EQL:
|
||||
pred = llvm.IntEQ
|
||||
case token.LSS:
|
||||
pred = llvm.IntSLT
|
||||
case token.GTR:
|
||||
pred = llvm.IntSGT
|
||||
case token.LEQ:
|
||||
pred = llvm.IntSLE
|
||||
case token.GEQ:
|
||||
pred = llvm.IntSGE
|
||||
case token.NEQ:
|
||||
panic("NEQ is handled in govalue.BinaryOp")
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
result = fr.builder.CreateICmp(pred, result, zero, "")
|
||||
result = fr.builder.CreateZExt(result, llvm.Int8Type(), "")
|
||||
return newValue(result, types.Typ[types.Bool])
|
||||
}
|
||||
|
||||
// stringIndex implements v = m[i]
|
||||
func (fr *frame) stringIndex(s, i *govalue) *govalue {
|
||||
ptr := fr.builder.CreateExtractValue(s.value, 0, "")
|
||||
ptr = fr.builder.CreateGEP(ptr, []llvm.Value{i.value}, "")
|
||||
return newValue(fr.builder.CreateLoad(ptr, ""), types.Typ[types.Byte])
|
||||
}
|
||||
|
||||
func (fr *frame) stringIterInit(str *govalue) []*govalue {
|
||||
indexptr := fr.allocaBuilder.CreateAlloca(fr.types.inttype, "")
|
||||
fr.builder.CreateStore(llvm.ConstNull(fr.types.inttype), indexptr)
|
||||
return []*govalue{str, newValue(indexptr, types.Typ[types.Int])}
|
||||
}
|
||||
|
||||
// stringIterNext advances the iterator, and returns the tuple (ok, k, v).
|
||||
func (fr *frame) stringIterNext(iter []*govalue) []*govalue {
|
||||
str, indexptr := iter[0], iter[1]
|
||||
k := fr.builder.CreateLoad(indexptr.value, "")
|
||||
|
||||
result := fr.runtime.stringiter2.call(fr, str.value, k)
|
||||
fr.builder.CreateStore(result[0], indexptr.value)
|
||||
ok := fr.builder.CreateIsNotNull(result[0], "")
|
||||
ok = fr.builder.CreateZExt(ok, llvm.Int8Type(), "")
|
||||
v := result[1]
|
||||
|
||||
return []*govalue{newValue(ok, types.Typ[types.Bool]), newValue(k, types.Typ[types.Int]), newValue(v, types.Typ[types.Rune])}
|
||||
}
|
||||
|
||||
func (fr *frame) runeToString(v *govalue) *govalue {
|
||||
v = fr.convert(v, types.Typ[types.Int])
|
||||
result := fr.runtime.intToString.call(fr, v.value)
|
||||
return newValue(result[0], types.Typ[types.String])
|
||||
}
|
||||
|
||||
func (fr *frame) stringToRuneSlice(v *govalue) *govalue {
|
||||
result := fr.runtime.stringToIntArray.call(fr, v.value)
|
||||
runeslice := types.NewSlice(types.Typ[types.Rune])
|
||||
return newValue(result[0], runeslice)
|
||||
}
|
||||
|
||||
func (fr *frame) runeSliceToString(v *govalue) *govalue {
|
||||
llv := v.value
|
||||
ptr := fr.builder.CreateExtractValue(llv, 0, "")
|
||||
len := fr.builder.CreateExtractValue(llv, 1, "")
|
||||
result := fr.runtime.intArrayToString.call(fr, ptr, len)
|
||||
return newValue(result[0], types.Typ[types.String])
|
||||
}
|
102
llgo/irgen/targets.go
Normal file
102
llgo/irgen/targets.go
Normal file
@ -0,0 +1,102 @@
|
||||
//===- targets.go - target data -------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file contains functions for retrieving target-specific data.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// PNaClTriple is the LLVM target triple that should be used to compile
|
||||
// modules to be compatible with PNaCl (Portable Native Client).
|
||||
const PNaClTriple = "armv7-none-linux-gnueabi"
|
||||
|
||||
// Below are the target data representation constants generated by clang.
|
||||
// For unknown targets, we enumerate all targets known to LLVM and use
|
||||
// the first one with a matching architecture.
|
||||
const (
|
||||
x86TargetData = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
|
||||
)
|
||||
|
||||
// llvmDataLayout returns the data layout string
|
||||
// representation for the specified LLVM triple.
|
||||
func llvmDataLayout(triple string) (string, error) {
|
||||
// Triples are several fields separated by '-' characters.
|
||||
// The first field is the architecture. The architecture's
|
||||
// canonical form may include a '-' character, which would
|
||||
// have been translated to '_' for inclusion in a triple.
|
||||
arch := parseArch(triple[:strings.IndexRune(triple, '-')])
|
||||
switch arch {
|
||||
case "x86-64":
|
||||
return x86TargetData, nil
|
||||
}
|
||||
for target := llvm.FirstTarget(); target.C != nil; target = target.NextTarget() {
|
||||
if arch == target.Name() {
|
||||
machine := target.CreateTargetMachine(
|
||||
triple, "", "",
|
||||
llvm.CodeGenLevelDefault,
|
||||
llvm.RelocDefault,
|
||||
llvm.CodeModelDefault,
|
||||
)
|
||||
target := machine.TargetData().String()
|
||||
machine.Dispose()
|
||||
return target, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("Invalid target triple: %s", triple)
|
||||
}
|
||||
|
||||
// Based on parseArch from LLVM's lib/Support/Triple.cpp.
|
||||
// This is used to match the target machine type.
|
||||
func parseArch(arch string) string {
|
||||
switch arch {
|
||||
case "i386", "i486", "i586", "i686", "i786", "i886", "i986":
|
||||
return "x86"
|
||||
case "amd64", "x86_64":
|
||||
return "x86-64"
|
||||
case "powerpc":
|
||||
return "ppc"
|
||||
case "powerpc64", "ppu":
|
||||
return "ppc64"
|
||||
case "mblaze":
|
||||
return "mblaze"
|
||||
case "arm", "xscale":
|
||||
return "arm"
|
||||
case "thumb":
|
||||
return "thumb"
|
||||
case "spu", "cellspu":
|
||||
return "cellspu"
|
||||
case "msp430":
|
||||
return "msp430"
|
||||
case "mips", "mipseb", "mipsallegrex":
|
||||
return "mips"
|
||||
case "mipsel", "mipsallegrexel":
|
||||
return "mipsel"
|
||||
case "mips64", "mips64eb":
|
||||
return "mips64"
|
||||
case "mipsel64":
|
||||
return "mipsel64"
|
||||
case "r600", "hexagon", "sparc", "sparcv9", "tce",
|
||||
"xcore", "nvptx", "nvptx64", "le32", "amdil":
|
||||
return arch
|
||||
}
|
||||
if strings.HasPrefix(arch, "armv") {
|
||||
return "arm"
|
||||
} else if strings.HasPrefix(arch, "thumbv") {
|
||||
return "thumb"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
2033
llgo/irgen/typemap.go
Normal file
2033
llgo/irgen/typemap.go
Normal file
File diff suppressed because it is too large
Load Diff
22
llgo/irgen/types.go
Normal file
22
llgo/irgen/types.go
Normal file
@ -0,0 +1,22 @@
|
||||
//===- types.go - convenience functions for types -------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements convenience functions for dealing with types.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
)
|
||||
|
||||
func deref(t types.Type) types.Type {
|
||||
return t.Underlying().(*types.Pointer).Elem()
|
||||
}
|
40
llgo/irgen/utils.go
Normal file
40
llgo/irgen/utils.go
Normal file
@ -0,0 +1,40 @@
|
||||
//===- utils.go - misc utils ----------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file implements misellaneous utilities for IR generation.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
func (fr *frame) loadOrNull(cond, ptr llvm.Value, ty types.Type) *govalue {
|
||||
startbb := fr.builder.GetInsertBlock()
|
||||
loadbb := llvm.AddBasicBlock(fr.function, "")
|
||||
contbb := llvm.AddBasicBlock(fr.function, "")
|
||||
fr.builder.CreateCondBr(cond, loadbb, contbb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(loadbb)
|
||||
llty := fr.types.ToLLVM(ty)
|
||||
typedptr := fr.builder.CreateBitCast(ptr, llvm.PointerType(llty, 0), "")
|
||||
loadedval := fr.builder.CreateLoad(typedptr, "")
|
||||
fr.builder.CreateBr(contbb)
|
||||
|
||||
fr.builder.SetInsertPointAtEnd(contbb)
|
||||
llv := fr.builder.CreatePHI(llty, "")
|
||||
llv.AddIncoming(
|
||||
[]llvm.Value{llvm.ConstNull(llty), loadedval},
|
||||
[]llvm.BasicBlock{startbb, loadbb},
|
||||
)
|
||||
return newValue(llv, ty)
|
||||
}
|
658
llgo/irgen/value.go
Normal file
658
llgo/irgen/value.go
Normal file
@ -0,0 +1,658 @@
|
||||
//===- value.go - govalue and operations ----------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file defines the govalue type, which combines an LLVM value with its Go
|
||||
// type, and implements various basic operations on govalues.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"llvm.org/llgo/third_party/go.tools/go/exact"
|
||||
"llvm.org/llgo/third_party/go.tools/go/types"
|
||||
"llvm.org/llvm/bindings/go/llvm"
|
||||
)
|
||||
|
||||
// govalue contains an LLVM value and a Go type,
|
||||
// representing the result of a Go expression.
|
||||
type govalue struct {
|
||||
value llvm.Value
|
||||
typ types.Type
|
||||
}
|
||||
|
||||
func (v *govalue) String() string {
|
||||
return fmt.Sprintf("[llgo.govalue typ:%s value:%v]", v.typ, v.value)
|
||||
}
|
||||
|
||||
// Create a new dynamic value from a (LLVM Value, Type) pair.
|
||||
func newValue(v llvm.Value, t types.Type) *govalue {
|
||||
return &govalue{v, t}
|
||||
}
|
||||
|
||||
// TODO(axw) remove this, use .typ directly
|
||||
func (v *govalue) Type() types.Type {
|
||||
return v.typ
|
||||
}
|
||||
|
||||
// newValueFromConst converts a constant value to an LLVM value.
|
||||
func (fr *frame) newValueFromConst(v exact.Value, typ types.Type) *govalue {
|
||||
switch {
|
||||
case v == nil:
|
||||
llvmtyp := fr.types.ToLLVM(typ)
|
||||
return newValue(llvm.ConstNull(llvmtyp), typ)
|
||||
|
||||
case isString(typ):
|
||||
if isUntyped(typ) {
|
||||
typ = types.Typ[types.String]
|
||||
}
|
||||
llvmtyp := fr.types.ToLLVM(typ)
|
||||
strval := exact.StringVal(v)
|
||||
strlen := len(strval)
|
||||
i8ptr := llvm.PointerType(llvm.Int8Type(), 0)
|
||||
var ptr llvm.Value
|
||||
if strlen > 0 {
|
||||
init := llvm.ConstString(strval, false)
|
||||
ptr = llvm.AddGlobal(fr.module.Module, init.Type(), "")
|
||||
ptr.SetInitializer(init)
|
||||
ptr.SetLinkage(llvm.InternalLinkage)
|
||||
ptr = llvm.ConstBitCast(ptr, i8ptr)
|
||||
} else {
|
||||
ptr = llvm.ConstNull(i8ptr)
|
||||
}
|
||||
len_ := llvm.ConstInt(fr.types.inttype, uint64(strlen), false)
|
||||
llvmvalue := llvm.Undef(llvmtyp)
|
||||
llvmvalue = llvm.ConstInsertValue(llvmvalue, ptr, []uint32{0})
|
||||
llvmvalue = llvm.ConstInsertValue(llvmvalue, len_, []uint32{1})
|
||||
return newValue(llvmvalue, typ)
|
||||
|
||||
case isInteger(typ):
|
||||
if isUntyped(typ) {
|
||||
typ = types.Typ[types.Int]
|
||||
}
|
||||
llvmtyp := fr.types.ToLLVM(typ)
|
||||
var llvmvalue llvm.Value
|
||||
if isUnsigned(typ) {
|
||||
v, _ := exact.Uint64Val(v)
|
||||
llvmvalue = llvm.ConstInt(llvmtyp, v, false)
|
||||
} else {
|
||||
v, _ := exact.Int64Val(v)
|
||||
llvmvalue = llvm.ConstInt(llvmtyp, uint64(v), true)
|
||||
}
|
||||
return newValue(llvmvalue, typ)
|
||||
|
||||
case isBoolean(typ):
|
||||
if isUntyped(typ) {
|
||||
typ = types.Typ[types.Bool]
|
||||
}
|
||||
return newValue(boolLLVMValue(exact.BoolVal(v)), typ)
|
||||
|
||||
case isFloat(typ):
|
||||
if isUntyped(typ) {
|
||||
typ = types.Typ[types.Float64]
|
||||
}
|
||||
llvmtyp := fr.types.ToLLVM(typ)
|
||||
floatval, _ := exact.Float64Val(v)
|
||||
llvmvalue := llvm.ConstFloat(llvmtyp, floatval)
|
||||
return newValue(llvmvalue, typ)
|
||||
|
||||
case typ == types.Typ[types.UnsafePointer]:
|
||||
llvmtyp := fr.types.ToLLVM(typ)
|
||||
v, _ := exact.Uint64Val(v)
|
||||
llvmvalue := llvm.ConstInt(fr.types.inttype, v, false)
|
||||
llvmvalue = llvm.ConstIntToPtr(llvmvalue, llvmtyp)
|
||||
return newValue(llvmvalue, typ)
|
||||
|
||||
case isComplex(typ):
|
||||
if isUntyped(typ) {
|
||||
typ = types.Typ[types.Complex128]
|
||||
}
|
||||
llvmtyp := fr.types.ToLLVM(typ)
|
||||
floattyp := llvmtyp.StructElementTypes()[0]
|
||||
llvmvalue := llvm.ConstNull(llvmtyp)
|
||||
realv := exact.Real(v)
|
||||
imagv := exact.Imag(v)
|
||||
realfloatval, _ := exact.Float64Val(realv)
|
||||
imagfloatval, _ := exact.Float64Val(imagv)
|
||||
llvmre := llvm.ConstFloat(floattyp, realfloatval)
|
||||
llvmim := llvm.ConstFloat(floattyp, imagfloatval)
|
||||
llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmre, []uint32{0})
|
||||
llvmvalue = llvm.ConstInsertValue(llvmvalue, llvmim, []uint32{1})
|
||||
return newValue(llvmvalue, typ)
|
||||
}
|
||||
|
||||
// Special case for string -> [](byte|rune)
|
||||
if u, ok := typ.Underlying().(*types.Slice); ok && isInteger(u.Elem()) {
|
||||
if v.Kind() == exact.String {
|
||||
strval := fr.newValueFromConst(v, types.Typ[types.String])
|
||||
return fr.convert(strval, typ)
|
||||
}
|
||||
}
|
||||
|
||||
panic(fmt.Sprintf("unhandled: t=%s(%T), v=%v(%T)", typ, typ, v, v))
|
||||
}
|
||||
|
||||
func (fr *frame) binaryOp(lhs *govalue, op token.Token, rhs *govalue) *govalue {
|
||||
if op == token.NEQ {
|
||||
result := fr.binaryOp(lhs, token.EQL, rhs)
|
||||
return fr.unaryOp(result, token.NOT)
|
||||
}
|
||||
|
||||
var result llvm.Value
|
||||
b := fr.builder
|
||||
|
||||
switch typ := lhs.typ.Underlying().(type) {
|
||||
case *types.Struct:
|
||||
// TODO(axw) use runtime equality algorithm (will be suitably inlined).
|
||||
// For now, we use compare all fields unconditionally and bitwise AND
|
||||
// to avoid branching (i.e. so we don't create additional blocks).
|
||||
value := newValue(boolLLVMValue(true), types.Typ[types.Bool])
|
||||
for i := 0; i < typ.NumFields(); i++ {
|
||||
t := typ.Field(i).Type()
|
||||
lhs := newValue(b.CreateExtractValue(lhs.value, i, ""), t)
|
||||
rhs := newValue(b.CreateExtractValue(rhs.value, i, ""), t)
|
||||
value = fr.binaryOp(value, token.AND, fr.binaryOp(lhs, token.EQL, rhs))
|
||||
}
|
||||
return value
|
||||
|
||||
case *types.Array:
|
||||
// TODO(pcc): as above.
|
||||
value := newValue(boolLLVMValue(true), types.Typ[types.Bool])
|
||||
t := typ.Elem()
|
||||
for i := int64(0); i < typ.Len(); i++ {
|
||||
lhs := newValue(b.CreateExtractValue(lhs.value, int(i), ""), t)
|
||||
rhs := newValue(b.CreateExtractValue(rhs.value, int(i), ""), t)
|
||||
value = fr.binaryOp(value, token.AND, fr.binaryOp(lhs, token.EQL, rhs))
|
||||
}
|
||||
return value
|
||||
|
||||
case *types.Slice:
|
||||
// []T == nil or nil == []T
|
||||
lhsptr := b.CreateExtractValue(lhs.value, 0, "")
|
||||
rhsptr := b.CreateExtractValue(rhs.value, 0, "")
|
||||
isnil := b.CreateICmp(llvm.IntEQ, lhsptr, rhsptr, "")
|
||||
isnil = b.CreateZExt(isnil, llvm.Int8Type(), "")
|
||||
return newValue(isnil, types.Typ[types.Bool])
|
||||
|
||||
case *types.Signature:
|
||||
// func == nil or nil == func
|
||||
isnil := b.CreateICmp(llvm.IntEQ, lhs.value, rhs.value, "")
|
||||
isnil = b.CreateZExt(isnil, llvm.Int8Type(), "")
|
||||
return newValue(isnil, types.Typ[types.Bool])
|
||||
|
||||
case *types.Interface:
|
||||
return fr.compareInterfaces(lhs, rhs)
|
||||
}
|
||||
|
||||
// Strings.
|
||||
if isString(lhs.typ) {
|
||||
if isString(rhs.typ) {
|
||||
switch op {
|
||||
case token.ADD:
|
||||
return fr.concatenateStrings(lhs, rhs)
|
||||
case token.EQL, token.LSS, token.GTR, token.LEQ, token.GEQ:
|
||||
return fr.compareStrings(lhs, rhs, op)
|
||||
default:
|
||||
panic(fmt.Sprint("Unimplemented operator: ", op))
|
||||
}
|
||||
}
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// Complex numbers.
|
||||
if isComplex(lhs.typ) {
|
||||
// XXX Should we represent complex numbers as vectors?
|
||||
lhsval := lhs.value
|
||||
rhsval := rhs.value
|
||||
a_ := b.CreateExtractValue(lhsval, 0, "")
|
||||
b_ := b.CreateExtractValue(lhsval, 1, "")
|
||||
c_ := b.CreateExtractValue(rhsval, 0, "")
|
||||
d_ := b.CreateExtractValue(rhsval, 1, "")
|
||||
switch op {
|
||||
case token.QUO:
|
||||
// (a+bi)/(c+di) = (ac+bd)/(c**2+d**2) + (bc-ad)/(c**2+d**2)i
|
||||
ac := b.CreateFMul(a_, c_, "")
|
||||
bd := b.CreateFMul(b_, d_, "")
|
||||
bc := b.CreateFMul(b_, c_, "")
|
||||
ad := b.CreateFMul(a_, d_, "")
|
||||
cpow2 := b.CreateFMul(c_, c_, "")
|
||||
dpow2 := b.CreateFMul(d_, d_, "")
|
||||
denom := b.CreateFAdd(cpow2, dpow2, "")
|
||||
realnumer := b.CreateFAdd(ac, bd, "")
|
||||
imagnumer := b.CreateFSub(bc, ad, "")
|
||||
real_ := b.CreateFDiv(realnumer, denom, "")
|
||||
imag_ := b.CreateFDiv(imagnumer, denom, "")
|
||||
lhsval = b.CreateInsertValue(lhsval, real_, 0, "")
|
||||
result = b.CreateInsertValue(lhsval, imag_, 1, "")
|
||||
case token.MUL:
|
||||
// (a+bi)(c+di) = (ac-bd)+(bc+ad)i
|
||||
ac := b.CreateFMul(a_, c_, "")
|
||||
bd := b.CreateFMul(b_, d_, "")
|
||||
bc := b.CreateFMul(b_, c_, "")
|
||||
ad := b.CreateFMul(a_, d_, "")
|
||||
real_ := b.CreateFSub(ac, bd, "")
|
||||
imag_ := b.CreateFAdd(bc, ad, "")
|
||||
lhsval = b.CreateInsertValue(lhsval, real_, 0, "")
|
||||
result = b.CreateInsertValue(lhsval, imag_, 1, "")
|
||||
case token.ADD:
|
||||
real_ := b.CreateFAdd(a_, c_, "")
|
||||
imag_ := b.CreateFAdd(b_, d_, "")
|
||||
lhsval = b.CreateInsertValue(lhsval, real_, 0, "")
|
||||
result = b.CreateInsertValue(lhsval, imag_, 1, "")
|
||||
case token.SUB:
|
||||
real_ := b.CreateFSub(a_, c_, "")
|
||||
imag_ := b.CreateFSub(b_, d_, "")
|
||||
lhsval = b.CreateInsertValue(lhsval, real_, 0, "")
|
||||
result = b.CreateInsertValue(lhsval, imag_, 1, "")
|
||||
case token.EQL:
|
||||
realeq := b.CreateFCmp(llvm.FloatOEQ, a_, c_, "")
|
||||
imageq := b.CreateFCmp(llvm.FloatOEQ, b_, d_, "")
|
||||
result = b.CreateAnd(realeq, imageq, "")
|
||||
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
||||
return newValue(result, types.Typ[types.Bool])
|
||||
default:
|
||||
panic(fmt.Errorf("unhandled operator: %v", op))
|
||||
}
|
||||
return newValue(result, lhs.typ)
|
||||
}
|
||||
|
||||
// Floats and integers.
|
||||
// TODO determine the NaN rules.
|
||||
|
||||
switch op {
|
||||
case token.MUL:
|
||||
if isFloat(lhs.typ) {
|
||||
result = b.CreateFMul(lhs.value, rhs.value, "")
|
||||
} else {
|
||||
result = b.CreateMul(lhs.value, rhs.value, "")
|
||||
}
|
||||
return newValue(result, lhs.typ)
|
||||
case token.QUO:
|
||||
switch {
|
||||
case isFloat(lhs.typ):
|
||||
result = b.CreateFDiv(lhs.value, rhs.value, "")
|
||||
case !isUnsigned(lhs.typ):
|
||||
result = b.CreateSDiv(lhs.value, rhs.value, "")
|
||||
default:
|
||||
result = b.CreateUDiv(lhs.value, rhs.value, "")
|
||||
}
|
||||
return newValue(result, lhs.typ)
|
||||
case token.REM:
|
||||
switch {
|
||||
case isFloat(lhs.typ):
|
||||
result = b.CreateFRem(lhs.value, rhs.value, "")
|
||||
case !isUnsigned(lhs.typ):
|
||||
result = b.CreateSRem(lhs.value, rhs.value, "")
|
||||
default:
|
||||
result = b.CreateURem(lhs.value, rhs.value, "")
|
||||
}
|
||||
return newValue(result, lhs.typ)
|
||||
case token.ADD:
|
||||
if isFloat(lhs.typ) {
|
||||
result = b.CreateFAdd(lhs.value, rhs.value, "")
|
||||
} else {
|
||||
result = b.CreateAdd(lhs.value, rhs.value, "")
|
||||
}
|
||||
return newValue(result, lhs.typ)
|
||||
case token.SUB:
|
||||
if isFloat(lhs.typ) {
|
||||
result = b.CreateFSub(lhs.value, rhs.value, "")
|
||||
} else {
|
||||
result = b.CreateSub(lhs.value, rhs.value, "")
|
||||
}
|
||||
return newValue(result, lhs.typ)
|
||||
case token.SHL, token.SHR:
|
||||
return fr.shift(lhs, rhs, op)
|
||||
case token.EQL:
|
||||
if isFloat(lhs.typ) {
|
||||
result = b.CreateFCmp(llvm.FloatOEQ, lhs.value, rhs.value, "")
|
||||
} else {
|
||||
result = b.CreateICmp(llvm.IntEQ, lhs.value, rhs.value, "")
|
||||
}
|
||||
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
||||
return newValue(result, types.Typ[types.Bool])
|
||||
case token.LSS:
|
||||
switch {
|
||||
case isFloat(lhs.typ):
|
||||
result = b.CreateFCmp(llvm.FloatOLT, lhs.value, rhs.value, "")
|
||||
case !isUnsigned(lhs.typ):
|
||||
result = b.CreateICmp(llvm.IntSLT, lhs.value, rhs.value, "")
|
||||
default:
|
||||
result = b.CreateICmp(llvm.IntULT, lhs.value, rhs.value, "")
|
||||
}
|
||||
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
||||
return newValue(result, types.Typ[types.Bool])
|
||||
case token.LEQ:
|
||||
switch {
|
||||
case isFloat(lhs.typ):
|
||||
result = b.CreateFCmp(llvm.FloatOLE, lhs.value, rhs.value, "")
|
||||
case !isUnsigned(lhs.typ):
|
||||
result = b.CreateICmp(llvm.IntSLE, lhs.value, rhs.value, "")
|
||||
default:
|
||||
result = b.CreateICmp(llvm.IntULE, lhs.value, rhs.value, "")
|
||||
}
|
||||
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
||||
return newValue(result, types.Typ[types.Bool])
|
||||
case token.GTR:
|
||||
switch {
|
||||
case isFloat(lhs.typ):
|
||||
result = b.CreateFCmp(llvm.FloatOGT, lhs.value, rhs.value, "")
|
||||
case !isUnsigned(lhs.typ):
|
||||
result = b.CreateICmp(llvm.IntSGT, lhs.value, rhs.value, "")
|
||||
default:
|
||||
result = b.CreateICmp(llvm.IntUGT, lhs.value, rhs.value, "")
|
||||
}
|
||||
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
||||
return newValue(result, types.Typ[types.Bool])
|
||||
case token.GEQ:
|
||||
switch {
|
||||
case isFloat(lhs.typ):
|
||||
result = b.CreateFCmp(llvm.FloatOGE, lhs.value, rhs.value, "")
|
||||
case !isUnsigned(lhs.typ):
|
||||
result = b.CreateICmp(llvm.IntSGE, lhs.value, rhs.value, "")
|
||||
default:
|
||||
result = b.CreateICmp(llvm.IntUGE, lhs.value, rhs.value, "")
|
||||
}
|
||||
result = b.CreateZExt(result, llvm.Int8Type(), "")
|
||||
return newValue(result, types.Typ[types.Bool])
|
||||
case token.AND: // a & b
|
||||
result = b.CreateAnd(lhs.value, rhs.value, "")
|
||||
return newValue(result, lhs.typ)
|
||||
case token.AND_NOT: // a &^ b
|
||||
rhsval := rhs.value
|
||||
rhsval = b.CreateXor(rhsval, llvm.ConstAllOnes(rhsval.Type()), "")
|
||||
result = b.CreateAnd(lhs.value, rhsval, "")
|
||||
return newValue(result, lhs.typ)
|
||||
case token.OR: // a | b
|
||||
result = b.CreateOr(lhs.value, rhs.value, "")
|
||||
return newValue(result, lhs.typ)
|
||||
case token.XOR: // a ^ b
|
||||
result = b.CreateXor(lhs.value, rhs.value, "")
|
||||
return newValue(result, lhs.typ)
|
||||
default:
|
||||
panic(fmt.Sprint("Unimplemented operator: ", op))
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
func (fr *frame) shift(lhs *govalue, rhs *govalue, op token.Token) *govalue {
|
||||
rhs = fr.convert(rhs, lhs.Type())
|
||||
lhsval := lhs.value
|
||||
bits := rhs.value
|
||||
unsigned := isUnsigned(lhs.Type())
|
||||
// Shifting >= width of lhs yields undefined behaviour, so we must select.
|
||||
max := llvm.ConstInt(bits.Type(), uint64(lhsval.Type().IntTypeWidth()-1), false)
|
||||
var result llvm.Value
|
||||
lessEqualWidth := fr.builder.CreateICmp(llvm.IntULE, bits, max, "")
|
||||
if !unsigned && op == token.SHR {
|
||||
bits := fr.builder.CreateSelect(lessEqualWidth, bits, max, "")
|
||||
result = fr.builder.CreateAShr(lhsval, bits, "")
|
||||
} else {
|
||||
if op == token.SHL {
|
||||
result = fr.builder.CreateShl(lhsval, bits, "")
|
||||
} else {
|
||||
result = fr.builder.CreateLShr(lhsval, bits, "")
|
||||
}
|
||||
zero := llvm.ConstNull(lhsval.Type())
|
||||
result = fr.builder.CreateSelect(lessEqualWidth, result, zero, "")
|
||||
}
|
||||
return newValue(result, lhs.typ)
|
||||
}
|
||||
|
||||
func (fr *frame) unaryOp(v *govalue, op token.Token) *govalue {
|
||||
switch op {
|
||||
case token.SUB:
|
||||
var value llvm.Value
|
||||
if isComplex(v.typ) {
|
||||
realv := fr.builder.CreateExtractValue(v.value, 0, "")
|
||||
imagv := fr.builder.CreateExtractValue(v.value, 1, "")
|
||||
negzero := llvm.ConstFloatFromString(realv.Type(), "-0")
|
||||
realv = fr.builder.CreateFSub(negzero, realv, "")
|
||||
imagv = fr.builder.CreateFSub(negzero, imagv, "")
|
||||
value = llvm.Undef(v.value.Type())
|
||||
value = fr.builder.CreateInsertValue(value, realv, 0, "")
|
||||
value = fr.builder.CreateInsertValue(value, imagv, 1, "")
|
||||
} else if isFloat(v.typ) {
|
||||
negzero := llvm.ConstFloatFromString(fr.types.ToLLVM(v.Type()), "-0")
|
||||
value = fr.builder.CreateFSub(negzero, v.value, "")
|
||||
} else {
|
||||
value = fr.builder.CreateNeg(v.value, "")
|
||||
}
|
||||
return newValue(value, v.typ)
|
||||
case token.ADD:
|
||||
return v // No-op
|
||||
case token.NOT:
|
||||
value := fr.builder.CreateXor(v.value, boolLLVMValue(true), "")
|
||||
return newValue(value, v.typ)
|
||||
case token.XOR:
|
||||
lhs := v.value
|
||||
rhs := llvm.ConstAllOnes(lhs.Type())
|
||||
value := fr.builder.CreateXor(lhs, rhs, "")
|
||||
return newValue(value, v.typ)
|
||||
default:
|
||||
panic(fmt.Sprintf("Unhandled operator: %s", op))
|
||||
}
|
||||
}
|
||||
|
||||
func (fr *frame) convert(v *govalue, dsttyp types.Type) *govalue {
|
||||
b := fr.builder
|
||||
|
||||
// If it's a stack allocated value, we'll want to compare the
|
||||
// value type, not the pointer type.
|
||||
srctyp := v.typ
|
||||
|
||||
// Get the underlying type, if any.
|
||||
origdsttyp := dsttyp
|
||||
dsttyp = dsttyp.Underlying()
|
||||
srctyp = srctyp.Underlying()
|
||||
|
||||
// Identical (underlying) types? Just swap in the destination type.
|
||||
if types.Identical(srctyp, dsttyp) {
|
||||
return newValue(v.value, origdsttyp)
|
||||
}
|
||||
|
||||
// Both pointer types with identical underlying types? Same as above.
|
||||
if srctyp, ok := srctyp.(*types.Pointer); ok {
|
||||
if dsttyp, ok := dsttyp.(*types.Pointer); ok {
|
||||
srctyp := srctyp.Elem().Underlying()
|
||||
dsttyp := dsttyp.Elem().Underlying()
|
||||
if types.Identical(srctyp, dsttyp) {
|
||||
return newValue(v.value, origdsttyp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// string ->
|
||||
if isString(srctyp) {
|
||||
// (untyped) string -> string
|
||||
// XXX should untyped strings be able to escape go/types?
|
||||
if isString(dsttyp) {
|
||||
return newValue(v.value, origdsttyp)
|
||||
}
|
||||
|
||||
// string -> []byte
|
||||
if isSlice(dsttyp, types.Byte) {
|
||||
value := v.value
|
||||
strdata := fr.builder.CreateExtractValue(value, 0, "")
|
||||
strlen := fr.builder.CreateExtractValue(value, 1, "")
|
||||
|
||||
// Data must be copied, to prevent changes in
|
||||
// the byte slice from mutating the string.
|
||||
newdata := fr.createMalloc(strlen)
|
||||
fr.memcpy(newdata, strdata, strlen)
|
||||
|
||||
struct_ := llvm.Undef(fr.types.ToLLVM(dsttyp))
|
||||
struct_ = fr.builder.CreateInsertValue(struct_, newdata, 0, "")
|
||||
struct_ = fr.builder.CreateInsertValue(struct_, strlen, 1, "")
|
||||
struct_ = fr.builder.CreateInsertValue(struct_, strlen, 2, "")
|
||||
return newValue(struct_, origdsttyp)
|
||||
}
|
||||
|
||||
// string -> []rune
|
||||
if isSlice(dsttyp, types.Rune) {
|
||||
return fr.stringToRuneSlice(v)
|
||||
}
|
||||
}
|
||||
|
||||
// []byte -> string
|
||||
if isSlice(srctyp, types.Byte) && isString(dsttyp) {
|
||||
value := v.value
|
||||
data := fr.builder.CreateExtractValue(value, 0, "")
|
||||
len := fr.builder.CreateExtractValue(value, 1, "")
|
||||
|
||||
// Data must be copied, to prevent changes in
|
||||
// the byte slice from mutating the string.
|
||||
newdata := fr.createMalloc(len)
|
||||
fr.memcpy(newdata, data, len)
|
||||
|
||||
struct_ := llvm.Undef(fr.types.ToLLVM(types.Typ[types.String]))
|
||||
struct_ = fr.builder.CreateInsertValue(struct_, newdata, 0, "")
|
||||
struct_ = fr.builder.CreateInsertValue(struct_, len, 1, "")
|
||||
return newValue(struct_, types.Typ[types.String])
|
||||
}
|
||||
|
||||
// []rune -> string
|
||||
if isSlice(srctyp, types.Rune) && isString(dsttyp) {
|
||||
return fr.runeSliceToString(v)
|
||||
}
|
||||
|
||||
// rune -> string
|
||||
if isString(dsttyp) && isInteger(srctyp) {
|
||||
return fr.runeToString(v)
|
||||
}
|
||||
|
||||
// Unsafe pointer conversions.
|
||||
llvm_type := fr.types.ToLLVM(dsttyp)
|
||||
if dsttyp == types.Typ[types.UnsafePointer] { // X -> unsafe.Pointer
|
||||
if _, isptr := srctyp.(*types.Pointer); isptr {
|
||||
return newValue(v.value, origdsttyp)
|
||||
} else if srctyp == types.Typ[types.Uintptr] {
|
||||
value := b.CreateIntToPtr(v.value, llvm_type, "")
|
||||
return newValue(value, origdsttyp)
|
||||
}
|
||||
} else if srctyp == types.Typ[types.UnsafePointer] { // unsafe.Pointer -> X
|
||||
if _, isptr := dsttyp.(*types.Pointer); isptr {
|
||||
return newValue(v.value, origdsttyp)
|
||||
} else if dsttyp == types.Typ[types.Uintptr] {
|
||||
value := b.CreatePtrToInt(v.value, llvm_type, "")
|
||||
return newValue(value, origdsttyp)
|
||||
}
|
||||
}
|
||||
|
||||
lv := v.value
|
||||
srcType := lv.Type()
|
||||
switch srcType.TypeKind() {
|
||||
case llvm.IntegerTypeKind:
|
||||
switch llvm_type.TypeKind() {
|
||||
case llvm.IntegerTypeKind:
|
||||
srcBits := srcType.IntTypeWidth()
|
||||
dstBits := llvm_type.IntTypeWidth()
|
||||
delta := srcBits - dstBits
|
||||
switch {
|
||||
case delta < 0:
|
||||
if !isUnsigned(srctyp) {
|
||||
lv = b.CreateSExt(lv, llvm_type, "")
|
||||
} else {
|
||||
lv = b.CreateZExt(lv, llvm_type, "")
|
||||
}
|
||||
case delta > 0:
|
||||
lv = b.CreateTrunc(lv, llvm_type, "")
|
||||
}
|
||||
return newValue(lv, origdsttyp)
|
||||
case llvm.FloatTypeKind, llvm.DoubleTypeKind:
|
||||
if !isUnsigned(v.Type()) {
|
||||
lv = b.CreateSIToFP(lv, llvm_type, "")
|
||||
} else {
|
||||
lv = b.CreateUIToFP(lv, llvm_type, "")
|
||||
}
|
||||
return newValue(lv, origdsttyp)
|
||||
}
|
||||
case llvm.DoubleTypeKind:
|
||||
switch llvm_type.TypeKind() {
|
||||
case llvm.FloatTypeKind:
|
||||
lv = b.CreateFPTrunc(lv, llvm_type, "")
|
||||
return newValue(lv, origdsttyp)
|
||||
case llvm.IntegerTypeKind:
|
||||
if !isUnsigned(dsttyp) {
|
||||
lv = b.CreateFPToSI(lv, llvm_type, "")
|
||||
} else {
|
||||
lv = b.CreateFPToUI(lv, llvm_type, "")
|
||||
}
|
||||
return newValue(lv, origdsttyp)
|
||||
}
|
||||
case llvm.FloatTypeKind:
|
||||
switch llvm_type.TypeKind() {
|
||||
case llvm.DoubleTypeKind:
|
||||
lv = b.CreateFPExt(lv, llvm_type, "")
|
||||
return newValue(lv, origdsttyp)
|
||||
case llvm.IntegerTypeKind:
|
||||
if !isUnsigned(dsttyp) {
|
||||
lv = b.CreateFPToSI(lv, llvm_type, "")
|
||||
} else {
|
||||
lv = b.CreateFPToUI(lv, llvm_type, "")
|
||||
}
|
||||
return newValue(lv, origdsttyp)
|
||||
}
|
||||
}
|
||||
|
||||
// Complex -> complex. Complexes are only convertible to other
|
||||
// complexes, contant conversions aside. So we can just check the
|
||||
// source type here; given that the types are not identical
|
||||
// (checked above), we can assume the destination type is the alternate
|
||||
// complex type.
|
||||
if isComplex(srctyp) {
|
||||
var fpcast func(llvm.Builder, llvm.Value, llvm.Type, string) llvm.Value
|
||||
var fptype llvm.Type
|
||||
if srctyp == types.Typ[types.Complex64] {
|
||||
fpcast = (llvm.Builder).CreateFPExt
|
||||
fptype = llvm.DoubleType()
|
||||
} else {
|
||||
fpcast = (llvm.Builder).CreateFPTrunc
|
||||
fptype = llvm.FloatType()
|
||||
}
|
||||
if fpcast != nil {
|
||||
realv := b.CreateExtractValue(lv, 0, "")
|
||||
imagv := b.CreateExtractValue(lv, 1, "")
|
||||
realv = fpcast(b, realv, fptype, "")
|
||||
imagv = fpcast(b, imagv, fptype, "")
|
||||
lv = llvm.Undef(fr.types.ToLLVM(dsttyp))
|
||||
lv = b.CreateInsertValue(lv, realv, 0, "")
|
||||
lv = b.CreateInsertValue(lv, imagv, 1, "")
|
||||
return newValue(lv, origdsttyp)
|
||||
}
|
||||
}
|
||||
panic(fmt.Sprintf("unimplemented conversion: %s (%s) -> %s", v.typ, lv.Type(), origdsttyp))
|
||||
}
|
||||
|
||||
// extractRealValue extracts the real component of a complex number.
|
||||
func (fr *frame) extractRealValue(v *govalue) *govalue {
|
||||
component := fr.builder.CreateExtractValue(v.value, 0, "")
|
||||
if component.Type().TypeKind() == llvm.FloatTypeKind {
|
||||
return newValue(component, types.Typ[types.Float32])
|
||||
}
|
||||
return newValue(component, types.Typ[types.Float64])
|
||||
}
|
||||
|
||||
// extractRealValue extracts the imaginary component of a complex number.
|
||||
func (fr *frame) extractImagValue(v *govalue) *govalue {
|
||||
component := fr.builder.CreateExtractValue(v.value, 1, "")
|
||||
if component.Type().TypeKind() == llvm.FloatTypeKind {
|
||||
return newValue(component, types.Typ[types.Float32])
|
||||
}
|
||||
return newValue(component, types.Typ[types.Float64])
|
||||
}
|
||||
|
||||
func boolLLVMValue(v bool) (lv llvm.Value) {
|
||||
if v {
|
||||
return llvm.ConstInt(llvm.Int8Type(), 1, false)
|
||||
}
|
||||
return llvm.ConstNull(llvm.Int8Type())
|
||||
}
|
23
llgo/irgen/version.go
Normal file
23
llgo/irgen/version.go
Normal file
@ -0,0 +1,23 @@
|
||||
//===- version.go - version info ------------------------------------------===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This file specifies the Go version supported by the IR generator.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
package irgen
|
||||
|
||||
const (
|
||||
goVersion = "go1.3"
|
||||
)
|
||||
|
||||
// GoVersion returns the version of Go that we are targeting.
|
||||
func GoVersion() string {
|
||||
return goVersion
|
||||
}
|
1788
llgo/libgo-noext.diff
Normal file
1788
llgo/libgo-noext.diff
Normal file
File diff suppressed because it is too large
Load Diff
19
llgo/llgo-go.sh
Normal file
19
llgo/llgo-go.sh
Normal file
@ -0,0 +1,19 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
scriptpath=$(which "$0")
|
||||
scriptpath=$(readlink -f "$scriptpath")
|
||||
bindir=$(dirname "$scriptpath")
|
||||
prefix=$(dirname "$bindir")
|
||||
|
||||
cmd="$1"
|
||||
|
||||
case "$cmd" in
|
||||
build | get | install | run | test)
|
||||
shift
|
||||
PATH="$prefix/lib/llgo/go-path:$PATH" exec go "$cmd" -compiler gccgo "$@"
|
||||
;;
|
||||
|
||||
*)
|
||||
exec go "$@"
|
||||
;;
|
||||
esac
|
14
llgo/mvifdiff.sh
Executable file
14
llgo/mvifdiff.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
# This file is distributed under the University of Illinois Open Source
|
||||
# License. See LICENSE.TXT for details.
|
||||
|
||||
# The mvifdiff.sh script works like the mv(1) command, except
|
||||
# that it does not touch the destination file if its contents
|
||||
# are the same as the source file.
|
||||
|
||||
if cmp -s "$1" "$2" ; then
|
||||
rm "$1"
|
||||
else
|
||||
mv "$1" "$2"
|
||||
fi
|
94
llgo/ssaopt/esc.go
Normal file
94
llgo/ssaopt/esc.go
Normal file
@ -0,0 +1,94 @@
|
||||
// Copyright 2014 The llgo Authors.
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package ssaopt
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
|
||||
"llvm.org/llgo/third_party/go.tools/go/ssa"
|
||||
)
|
||||
|
||||
func escapes(val ssa.Value, bb *ssa.BasicBlock, pending []ssa.Value) bool {
|
||||
for _, p := range pending {
|
||||
if val == p {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, ref := range *val.Referrers() {
|
||||
switch ref := ref.(type) {
|
||||
case *ssa.Phi:
|
||||
// We must consider the variable to have escaped if it is
|
||||
// possible for the program to see more than one "version"
|
||||
// of the variable at once, as this requires the program
|
||||
// to use heap allocation for the multiple versions.
|
||||
//
|
||||
// I (pcc) think that this is only possible (without stores)
|
||||
// in the case where a phi node that (directly or indirectly)
|
||||
// refers to the allocation dominates the allocation.
|
||||
if ref.Block().Dominates(bb) {
|
||||
return true
|
||||
}
|
||||
if escapes(ref, bb, append(pending, val)) {
|
||||
return true
|
||||
}
|
||||
|
||||
case *ssa.BinOp, *ssa.ChangeType, *ssa.Convert, *ssa.ChangeInterface, *ssa.MakeInterface, *ssa.Slice, *ssa.FieldAddr, *ssa.IndexAddr, *ssa.TypeAssert, *ssa.Extract:
|
||||
if escapes(ref.(ssa.Value), bb, append(pending, val)) {
|
||||
return true
|
||||
}
|
||||
|
||||
case *ssa.Range, *ssa.DebugRef:
|
||||
continue
|
||||
|
||||
case *ssa.UnOp:
|
||||
if ref.Op == token.MUL || ref.Op == token.ARROW {
|
||||
continue
|
||||
}
|
||||
if escapes(ref, bb, append(pending, val)) {
|
||||
return true
|
||||
}
|
||||
|
||||
case *ssa.Store:
|
||||
if val == ref.Val {
|
||||
return true
|
||||
}
|
||||
|
||||
case *ssa.Call:
|
||||
if builtin, ok := ref.Call.Value.(*ssa.Builtin); ok {
|
||||
switch builtin.Name() {
|
||||
case "cap", "len", "copy", "ssa:wrapnilchk":
|
||||
continue
|
||||
case "append":
|
||||
if ref.Call.Args[0] == val && escapes(ref, bb, append(pending, val)) {
|
||||
return true
|
||||
}
|
||||
default:
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func LowerAllocsToStack(f *ssa.Function) {
|
||||
pending := make([]ssa.Value, 0, 10)
|
||||
|
||||
for _, b := range f.Blocks {
|
||||
for _, instr := range b.Instrs {
|
||||
if alloc, ok := instr.(*ssa.Alloc); ok && alloc.Heap && !escapes(alloc, alloc.Block(), pending) {
|
||||
alloc.Heap = false
|
||||
f.Locals = append(f.Locals, alloc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
15
llgo/test/CMakeLists.txt
Normal file
15
llgo/test/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
configure_lit_site_cfg(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg
|
||||
)
|
||||
|
||||
add_lit_testsuite(check-llgo "Running the llgo regression tests"
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
DEPENDS
|
||||
FileCheck
|
||||
count
|
||||
llgo
|
||||
libgo
|
||||
not
|
||||
)
|
||||
set_target_properties(check-llgo PROPERTIES FOLDER "Tests")
|
7
llgo/test/debuginfo/emptyname.go
Normal file
7
llgo/test/debuginfo/emptyname.go
Normal file
@ -0,0 +1,7 @@
|
||||
// RUN: llgo -c -o /dev/null -g %s
|
||||
|
||||
package main
|
||||
|
||||
//line :1
|
||||
func main() {
|
||||
}
|
17
llgo/test/driver/parse-arguments.go
Normal file
17
llgo/test/driver/parse-arguments.go
Normal file
@ -0,0 +1,17 @@
|
||||
// RUN: not llgo -B 2>&1 | FileCheck --check-prefix=B %s
|
||||
// RUN: not llgo -D 2>&1 | FileCheck --check-prefix=D %s
|
||||
// RUN: not llgo -I 2>&1 | FileCheck --check-prefix=I %s
|
||||
// RUN: not llgo -isystem 2>&1 | FileCheck --check-prefix=isystem %s
|
||||
// RUN: not llgo -L 2>&1 | FileCheck --check-prefix=L %s
|
||||
// RUN: not llgo -fload-plugin 2>&1 | FileCheck --check-prefix=fload-plugin %s
|
||||
// RUN: not llgo -mllvm 2>&1 | FileCheck --check-prefix=mllvm %s
|
||||
// RUN: not llgo -o 2>&1 | FileCheck --check-prefix=o %s
|
||||
|
||||
// B: missing argument after '-B'
|
||||
// D: missing argument after '-D'
|
||||
// I: missing argument after '-I'
|
||||
// isystem: missing argument after '-isystem'
|
||||
// L: missing argument after '-L'
|
||||
// fload-plugin: missing argument after '-fload-plugin'
|
||||
// mllvm: missing argument after '-mllvm'
|
||||
// o: missing argument after '-o'
|
5
llgo/test/execution/Inputs/init2.go
Normal file
5
llgo/test/execution/Inputs/init2.go
Normal file
@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func init() {
|
||||
println("do some other stuff before main")
|
||||
}
|
18
llgo/test/execution/arrays/compare.go
Normal file
18
llgo/test/execution/arrays/compare.go
Normal file
@ -0,0 +1,18 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: false
|
||||
// CHECK-NEXT: true
|
||||
// CHECK-NEXT: false
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
b := [...]int{10, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
||||
c := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
|
||||
|
||||
println(a == b)
|
||||
println(a == c)
|
||||
println(b == c)
|
||||
}
|
288
llgo/test/execution/arrays/index.go
Normal file
288
llgo/test/execution/arrays/index.go
Normal file
@ -0,0 +1,288 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: 3
|
||||
// CHECK-NEXT: 4
|
||||
// CHECK-NEXT: 5
|
||||
// CHECK-NEXT: 6
|
||||
// CHECK-NEXT: 7
|
||||
// CHECK-NEXT: 8
|
||||
// CHECK-NEXT: 9
|
||||
// CHECK-NEXT: 10
|
||||
// CHECK-NEXT: 11
|
||||
// CHECK-NEXT: 12
|
||||
// CHECK-NEXT: 13
|
||||
// CHECK-NEXT: 14
|
||||
// CHECK-NEXT: 15
|
||||
// CHECK-NEXT: 16
|
||||
// CHECK-NEXT: 17
|
||||
// CHECK-NEXT: 18
|
||||
// CHECK-NEXT: 19
|
||||
// CHECK-NEXT: 20
|
||||
// CHECK-NEXT: 21
|
||||
// CHECK-NEXT: 22
|
||||
// CHECK-NEXT: 23
|
||||
// CHECK-NEXT: 24
|
||||
// CHECK-NEXT: 25
|
||||
// CHECK-NEXT: 26
|
||||
// CHECK-NEXT: 27
|
||||
// CHECK-NEXT: 28
|
||||
// CHECK-NEXT: 29
|
||||
// CHECK-NEXT: 30
|
||||
// CHECK-NEXT: 31
|
||||
// CHECK-NEXT: 32
|
||||
// CHECK-NEXT: 33
|
||||
// CHECK-NEXT: 34
|
||||
// CHECK-NEXT: 35
|
||||
// CHECK-NEXT: 36
|
||||
// CHECK-NEXT: 37
|
||||
// CHECK-NEXT: 38
|
||||
// CHECK-NEXT: 39
|
||||
// CHECK-NEXT: 40
|
||||
// CHECK-NEXT: 41
|
||||
// CHECK-NEXT: 42
|
||||
// CHECK-NEXT: 43
|
||||
// CHECK-NEXT: 44
|
||||
// CHECK-NEXT: 45
|
||||
// CHECK-NEXT: 46
|
||||
// CHECK-NEXT: 47
|
||||
// CHECK-NEXT: 48
|
||||
// CHECK-NEXT: 49
|
||||
// CHECK-NEXT: 50
|
||||
// CHECK-NEXT: 51
|
||||
// CHECK-NEXT: 52
|
||||
// CHECK-NEXT: 53
|
||||
// CHECK-NEXT: 54
|
||||
// CHECK-NEXT: 55
|
||||
// CHECK-NEXT: 56
|
||||
// CHECK-NEXT: 57
|
||||
// CHECK-NEXT: 58
|
||||
// CHECK-NEXT: 59
|
||||
// CHECK-NEXT: 60
|
||||
// CHECK-NEXT: 61
|
||||
// CHECK-NEXT: 62
|
||||
// CHECK-NEXT: 63
|
||||
// CHECK-NEXT: 64
|
||||
// CHECK-NEXT: 65
|
||||
// CHECK-NEXT: 66
|
||||
// CHECK-NEXT: 67
|
||||
// CHECK-NEXT: 68
|
||||
// CHECK-NEXT: 69
|
||||
// CHECK-NEXT: 70
|
||||
// CHECK-NEXT: 71
|
||||
// CHECK-NEXT: 72
|
||||
// CHECK-NEXT: 73
|
||||
// CHECK-NEXT: 74
|
||||
// CHECK-NEXT: 75
|
||||
// CHECK-NEXT: 76
|
||||
// CHECK-NEXT: 77
|
||||
// CHECK-NEXT: 78
|
||||
// CHECK-NEXT: 79
|
||||
// CHECK-NEXT: 80
|
||||
// CHECK-NEXT: 81
|
||||
// CHECK-NEXT: 82
|
||||
// CHECK-NEXT: 83
|
||||
// CHECK-NEXT: 84
|
||||
// CHECK-NEXT: 85
|
||||
// CHECK-NEXT: 86
|
||||
// CHECK-NEXT: 87
|
||||
// CHECK-NEXT: 88
|
||||
// CHECK-NEXT: 89
|
||||
// CHECK-NEXT: 90
|
||||
// CHECK-NEXT: 91
|
||||
// CHECK-NEXT: 92
|
||||
// CHECK-NEXT: 93
|
||||
// CHECK-NEXT: 94
|
||||
// CHECK-NEXT: 95
|
||||
// CHECK-NEXT: 96
|
||||
// CHECK-NEXT: 97
|
||||
// CHECK-NEXT: 98
|
||||
// CHECK-NEXT: 99
|
||||
// CHECK-NEXT: 100
|
||||
// CHECK-NEXT: 101
|
||||
// CHECK-NEXT: 102
|
||||
// CHECK-NEXT: 103
|
||||
// CHECK-NEXT: 104
|
||||
// CHECK-NEXT: 105
|
||||
// CHECK-NEXT: 106
|
||||
// CHECK-NEXT: 107
|
||||
// CHECK-NEXT: 108
|
||||
// CHECK-NEXT: 109
|
||||
// CHECK-NEXT: 110
|
||||
// CHECK-NEXT: 111
|
||||
// CHECK-NEXT: 112
|
||||
// CHECK-NEXT: 113
|
||||
// CHECK-NEXT: 114
|
||||
// CHECK-NEXT: 115
|
||||
// CHECK-NEXT: 116
|
||||
// CHECK-NEXT: 117
|
||||
// CHECK-NEXT: 118
|
||||
// CHECK-NEXT: 119
|
||||
// CHECK-NEXT: 120
|
||||
// CHECK-NEXT: 121
|
||||
// CHECK-NEXT: 122
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 124
|
||||
// CHECK-NEXT: 125
|
||||
// CHECK-NEXT: 126
|
||||
// CHECK-NEXT: 127
|
||||
// CHECK-NEXT: 128
|
||||
// CHECK-NEXT: 129
|
||||
// CHECK-NEXT: 130
|
||||
// CHECK-NEXT: 131
|
||||
// CHECK-NEXT: 132
|
||||
// CHECK-NEXT: 133
|
||||
// CHECK-NEXT: 134
|
||||
// CHECK-NEXT: 135
|
||||
// CHECK-NEXT: 136
|
||||
// CHECK-NEXT: 137
|
||||
// CHECK-NEXT: 138
|
||||
// CHECK-NEXT: 139
|
||||
// CHECK-NEXT: 140
|
||||
// CHECK-NEXT: 141
|
||||
// CHECK-NEXT: 142
|
||||
// CHECK-NEXT: 143
|
||||
// CHECK-NEXT: 144
|
||||
// CHECK-NEXT: 145
|
||||
// CHECK-NEXT: 146
|
||||
// CHECK-NEXT: 147
|
||||
// CHECK-NEXT: 148
|
||||
// CHECK-NEXT: 149
|
||||
// CHECK-NEXT: 150
|
||||
// CHECK-NEXT: 151
|
||||
// CHECK-NEXT: 152
|
||||
// CHECK-NEXT: 153
|
||||
// CHECK-NEXT: 154
|
||||
// CHECK-NEXT: 155
|
||||
// CHECK-NEXT: 156
|
||||
// CHECK-NEXT: 157
|
||||
// CHECK-NEXT: 158
|
||||
// CHECK-NEXT: 159
|
||||
// CHECK-NEXT: 160
|
||||
// CHECK-NEXT: 161
|
||||
// CHECK-NEXT: 162
|
||||
// CHECK-NEXT: 163
|
||||
// CHECK-NEXT: 164
|
||||
// CHECK-NEXT: 165
|
||||
// CHECK-NEXT: 166
|
||||
// CHECK-NEXT: 167
|
||||
// CHECK-NEXT: 168
|
||||
// CHECK-NEXT: 169
|
||||
// CHECK-NEXT: 170
|
||||
// CHECK-NEXT: 171
|
||||
// CHECK-NEXT: 172
|
||||
// CHECK-NEXT: 173
|
||||
// CHECK-NEXT: 174
|
||||
// CHECK-NEXT: 175
|
||||
// CHECK-NEXT: 176
|
||||
// CHECK-NEXT: 177
|
||||
// CHECK-NEXT: 178
|
||||
// CHECK-NEXT: 179
|
||||
// CHECK-NEXT: 180
|
||||
// CHECK-NEXT: 181
|
||||
// CHECK-NEXT: 182
|
||||
// CHECK-NEXT: 183
|
||||
// CHECK-NEXT: 184
|
||||
// CHECK-NEXT: 185
|
||||
// CHECK-NEXT: 186
|
||||
// CHECK-NEXT: 187
|
||||
// CHECK-NEXT: 188
|
||||
// CHECK-NEXT: 189
|
||||
// CHECK-NEXT: 190
|
||||
// CHECK-NEXT: 191
|
||||
// CHECK-NEXT: 192
|
||||
// CHECK-NEXT: 193
|
||||
// CHECK-NEXT: 194
|
||||
// CHECK-NEXT: 195
|
||||
// CHECK-NEXT: 196
|
||||
// CHECK-NEXT: 197
|
||||
// CHECK-NEXT: 198
|
||||
// CHECK-NEXT: 199
|
||||
// CHECK-NEXT: 200
|
||||
// CHECK-NEXT: 201
|
||||
// CHECK-NEXT: 202
|
||||
// CHECK-NEXT: 203
|
||||
// CHECK-NEXT: 204
|
||||
// CHECK-NEXT: 205
|
||||
// CHECK-NEXT: 206
|
||||
// CHECK-NEXT: 207
|
||||
// CHECK-NEXT: 208
|
||||
// CHECK-NEXT: 209
|
||||
// CHECK-NEXT: 210
|
||||
// CHECK-NEXT: 211
|
||||
// CHECK-NEXT: 212
|
||||
// CHECK-NEXT: 213
|
||||
// CHECK-NEXT: 214
|
||||
// CHECK-NEXT: 215
|
||||
// CHECK-NEXT: 216
|
||||
// CHECK-NEXT: 217
|
||||
// CHECK-NEXT: 218
|
||||
// CHECK-NEXT: 219
|
||||
// CHECK-NEXT: 220
|
||||
// CHECK-NEXT: 221
|
||||
// CHECK-NEXT: 222
|
||||
// CHECK-NEXT: 223
|
||||
// CHECK-NEXT: 224
|
||||
// CHECK-NEXT: 225
|
||||
// CHECK-NEXT: 226
|
||||
// CHECK-NEXT: 227
|
||||
// CHECK-NEXT: 228
|
||||
// CHECK-NEXT: 229
|
||||
// CHECK-NEXT: 230
|
||||
// CHECK-NEXT: 231
|
||||
// CHECK-NEXT: 232
|
||||
// CHECK-NEXT: 233
|
||||
// CHECK-NEXT: 234
|
||||
// CHECK-NEXT: 235
|
||||
// CHECK-NEXT: 236
|
||||
// CHECK-NEXT: 237
|
||||
// CHECK-NEXT: 238
|
||||
// CHECK-NEXT: 239
|
||||
// CHECK-NEXT: 240
|
||||
// CHECK-NEXT: 241
|
||||
// CHECK-NEXT: 242
|
||||
// CHECK-NEXT: 243
|
||||
// CHECK-NEXT: 244
|
||||
// CHECK-NEXT: 245
|
||||
// CHECK-NEXT: 246
|
||||
// CHECK-NEXT: 247
|
||||
// CHECK-NEXT: 248
|
||||
// CHECK-NEXT: 249
|
||||
// CHECK-NEXT: 250
|
||||
// CHECK-NEXT: 251
|
||||
// CHECK-NEXT: 252
|
||||
// CHECK-NEXT: 253
|
||||
// CHECK-NEXT: 254
|
||||
|
||||
package main
|
||||
|
||||
func testBasics() {
|
||||
var i [2]int
|
||||
j := &i
|
||||
i[0] = 123
|
||||
i[1] = 456
|
||||
println(i[0], i[1])
|
||||
println(j[0], j[1])
|
||||
i[0]++
|
||||
i[1]--
|
||||
println(i[0], i[1])
|
||||
println(j[0], j[1])
|
||||
}
|
||||
|
||||
func testByteIndex() {
|
||||
var a [255]int
|
||||
for i := 0; i < len(a); i++ {
|
||||
a[i] = i
|
||||
}
|
||||
for i := byte(0); i < byte(len(a)); i++ {
|
||||
println(a[i])
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
//testBasics()
|
||||
testByteIndex()
|
||||
}
|
23
llgo/test/execution/arrays/range.go
Normal file
23
llgo/test/execution/arrays/range.go
Normal file
@ -0,0 +1,23 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0 0 0
|
||||
// CHECK-NEXT: 1 1 1
|
||||
// CHECK-NEXT: 2 2 2
|
||||
// CHECK-NEXT: 3 0 0
|
||||
// CHECK-NEXT: 4 4 4
|
||||
// CHECK-NEXT: 0 10
|
||||
// CHECK-NEXT: 1 20
|
||||
// CHECK-NEXT: 2 30
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := [...]int{1: 1, 2: 2, 4: 4}
|
||||
for i, val := range a {
|
||||
println(i, val, a[i])
|
||||
}
|
||||
for i, val := range [...]int{10, 20, 30} {
|
||||
println(i, val)
|
||||
}
|
||||
}
|
14
llgo/test/execution/arrays/slice.go
Normal file
14
llgo/test/execution/arrays/slice.go
Normal file
@ -0,0 +1,14 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 10
|
||||
// CHECK-NEXT: 9
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var a [10]int
|
||||
b := a[1:]
|
||||
println(len(a))
|
||||
println(len(b))
|
||||
}
|
22
llgo/test/execution/assignment/arrays.go
Normal file
22
llgo/test/execution/assignment/arrays.go
Normal file
@ -0,0 +1,22 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: +1.000000e+000
|
||||
// CHECK-NEXT: +2.000000e+000
|
||||
// CHECK-NEXT: +3.000000e+000
|
||||
|
||||
package main
|
||||
|
||||
var a1 = [...]float32{1.0, 2.0, 3.0}
|
||||
|
||||
func main() {
|
||||
var a2 [3]float32
|
||||
a2 = a1
|
||||
println(a2[0])
|
||||
println(a2[1])
|
||||
println(a2[2])
|
||||
|
||||
// broken due to lack of promotion of
|
||||
// stack to heap.
|
||||
//println(a2[0], a2[1], a2[2])
|
||||
}
|
21
llgo/test/execution/assignment/binop.go
Normal file
21
llgo/test/execution/assignment/binop.go
Normal file
@ -0,0 +1,21 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 246
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 124
|
||||
// CHECK-NEXT: 123
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
x := 123
|
||||
x *= 2
|
||||
println(x)
|
||||
x /= 2
|
||||
println(x)
|
||||
x += 1
|
||||
println(x)
|
||||
x -= 1
|
||||
println(x)
|
||||
}
|
13
llgo/test/execution/assignment/dereferencing.go
Normal file
13
llgo/test/execution/assignment/dereferencing.go
Normal file
@ -0,0 +1,13 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 123
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var x int
|
||||
px := &x
|
||||
*px = 123
|
||||
println(x)
|
||||
}
|
40
llgo/test/execution/assignment/multi.go
Normal file
40
llgo/test/execution/assignment/multi.go
Normal file
@ -0,0 +1,40 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 123 456
|
||||
// CHECK-NEXT: 456 123
|
||||
// CHECK-NEXT: 456 123
|
||||
// CHECK-NEXT: 123 456
|
||||
// CHECK-NEXT: 123 456
|
||||
|
||||
package main
|
||||
|
||||
func xyz() (int, int) {
|
||||
return 123, 456
|
||||
}
|
||||
|
||||
func abc() (int, int) {
|
||||
var a, b = xyz()
|
||||
return a, b
|
||||
}
|
||||
|
||||
type S struct {
|
||||
a int
|
||||
b int
|
||||
}
|
||||
|
||||
func main() {
|
||||
a, b := xyz()
|
||||
println(a, b)
|
||||
b, a = abc()
|
||||
println(a, b)
|
||||
|
||||
// swap
|
||||
println(a, b)
|
||||
a, b = b, a
|
||||
println(a, b)
|
||||
|
||||
var s S
|
||||
s.a, s.b = a, b
|
||||
println(s.a, s.b)
|
||||
}
|
42
llgo/test/execution/assignment/namedresult.go
Normal file
42
llgo/test/execution/assignment/namedresult.go
Normal file
@ -0,0 +1,42 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 123
|
||||
// CHECK-NEXT: 456
|
||||
// CHECK-NEXT: 1 2
|
||||
// CHECK-NEXT: 666 0
|
||||
|
||||
package main
|
||||
|
||||
func f1() (x int) {
|
||||
x = 123
|
||||
return
|
||||
}
|
||||
|
||||
func f2() (x int) {
|
||||
return 456
|
||||
}
|
||||
|
||||
func f3() (x, y int) {
|
||||
y, x = 2, 1
|
||||
return
|
||||
}
|
||||
|
||||
func f4() (x, _ int) {
|
||||
x = 666
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := f1()
|
||||
println(x)
|
||||
x = f2()
|
||||
println(x)
|
||||
|
||||
var y int
|
||||
x, y = f3()
|
||||
println(x, y)
|
||||
|
||||
x, y = f4()
|
||||
println(x, y)
|
||||
}
|
43
llgo/test/execution/branching/goto.go
Normal file
43
llgo/test/execution/branching/goto.go
Normal file
@ -0,0 +1,43 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: 3
|
||||
// CHECK-NEXT: 4
|
||||
// CHECK-NEXT: 5
|
||||
// CHECK-NEXT: 6
|
||||
// CHECK-NEXT: 7
|
||||
// CHECK-NEXT: 8
|
||||
// CHECK-NEXT: 9
|
||||
// CHECK-NEXT: done
|
||||
// CHECK-NEXT: !
|
||||
|
||||
package main
|
||||
|
||||
func f1() {
|
||||
goto labeled
|
||||
labeled:
|
||||
goto done
|
||||
return
|
||||
done:
|
||||
println("!")
|
||||
}
|
||||
|
||||
func main() {
|
||||
i := 0
|
||||
start:
|
||||
if i < 10 {
|
||||
println(i)
|
||||
i++
|
||||
goto start
|
||||
} else {
|
||||
goto end
|
||||
}
|
||||
return
|
||||
end:
|
||||
println("done")
|
||||
f1()
|
||||
return
|
||||
}
|
22
llgo/test/execution/branching/labeled.go
Normal file
22
llgo/test/execution/branching/labeled.go
Normal file
@ -0,0 +1,22 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0
|
||||
|
||||
package main
|
||||
|
||||
func labeledBreak() {
|
||||
var i int
|
||||
L:
|
||||
for ; i < 10; i++ {
|
||||
switch {
|
||||
default:
|
||||
break L
|
||||
}
|
||||
}
|
||||
println(i)
|
||||
}
|
||||
|
||||
func main() {
|
||||
labeledBreak()
|
||||
}
|
41
llgo/test/execution/chan/buffered.go
Normal file
41
llgo/test/execution/chan/buffered.go
Normal file
@ -0,0 +1,41 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0 0
|
||||
// CHECK-NEXT: 0 1
|
||||
// CHECK-NEXT: 10
|
||||
// CHECK-NEXT: 20
|
||||
// CHECK-NEXT: 30
|
||||
// CHECK-NEXT: 40
|
||||
// CHECK-NEXT: 50
|
||||
// CHECK-NEXT: 60
|
||||
// CHECK-NEXT: 70
|
||||
// CHECK-NEXT: 80
|
||||
// CHECK-NEXT: 90
|
||||
// CHECK-NEXT: 100
|
||||
// CHECK-NEXT: -1
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
c := make(chan int)
|
||||
println(len(c), cap(c))
|
||||
c1 := make(chan int, 1)
|
||||
println(len(c1), cap(c1))
|
||||
f := func() {
|
||||
n, ok := <-c
|
||||
if ok {
|
||||
c1 <- n * 10
|
||||
} else {
|
||||
c1 <- -1
|
||||
}
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
go f()
|
||||
c <- i + 1
|
||||
println(<-c1)
|
||||
}
|
||||
go f()
|
||||
close(c)
|
||||
println(<-c1)
|
||||
}
|
28
llgo/test/execution/chan/range.go
Normal file
28
llgo/test/execution/chan/range.go
Normal file
@ -0,0 +1,28 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: 3
|
||||
// CHECK-NEXT: 4
|
||||
// CHECK-NEXT: 5
|
||||
// CHECK-NEXT: 6
|
||||
// CHECK-NEXT: 7
|
||||
// CHECK-NEXT: 8
|
||||
// CHECK-NEXT: 9
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
ch := make(chan int)
|
||||
go func() {
|
||||
for i := 0; i < 10; i++ {
|
||||
ch <- i
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
for n := range ch {
|
||||
println(n)
|
||||
}
|
||||
}
|
27
llgo/test/execution/chan/select.go
Normal file
27
llgo/test/execution/chan/select.go
Normal file
@ -0,0 +1,27 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: sent a value
|
||||
// CHECK-NEXT: received 123
|
||||
// CHECK-NEXT: default
|
||||
|
||||
package main
|
||||
|
||||
func f1() {
|
||||
c := make(chan int, 1)
|
||||
for i := 0; i < 3; i++ {
|
||||
select {
|
||||
case n, _ := <-c:
|
||||
println("received", n)
|
||||
c = nil
|
||||
case c <- 123:
|
||||
println("sent a value")
|
||||
default:
|
||||
println("default")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f1()
|
||||
}
|
20
llgo/test/execution/chan/self.go
Normal file
20
llgo/test/execution/chan/self.go
Normal file
@ -0,0 +1,20 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 1
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: true
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
ch := make(chan int, uint8(1))
|
||||
|
||||
ch <- 1
|
||||
println(<-ch)
|
||||
|
||||
ch <- 2
|
||||
x, ok := <-ch
|
||||
println(x)
|
||||
println(ok)
|
||||
}
|
17
llgo/test/execution/circulartype.go
Normal file
17
llgo/test/execution/circulartype.go
Normal file
@ -0,0 +1,17 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | count 0
|
||||
|
||||
package main
|
||||
|
||||
type A struct {
|
||||
b1, b2 B
|
||||
}
|
||||
|
||||
type B struct {
|
||||
a1, a2 *A
|
||||
}
|
||||
|
||||
func main() {
|
||||
var a A
|
||||
_ = a
|
||||
}
|
15
llgo/test/execution/closures/basic.go
Normal file
15
llgo/test/execution/closures/basic.go
Normal file
@ -0,0 +1,15 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: abc
|
||||
|
||||
package main
|
||||
|
||||
func cat(a, b string) func(string) string {
|
||||
return func(c string) string { return a + b + c }
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := cat("a", "b")
|
||||
println(f("c"))
|
||||
}
|
15
llgo/test/execution/closures/issue176.go
Normal file
15
llgo/test/execution/closures/issue176.go
Normal file
@ -0,0 +1,15 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: false
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
a := false
|
||||
f := func() {
|
||||
make(chan *bool, 1) <- &a
|
||||
}
|
||||
f()
|
||||
println(a)
|
||||
}
|
24
llgo/test/execution/complex.go
Normal file
24
llgo/test/execution/complex.go
Normal file
@ -0,0 +1,24 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: (+1.000000e+000+2.000000e+000i)
|
||||
// CHECK-NEXT: (-1.000000e+000-2.000000e+000i)
|
||||
// CHECK-NEXT: true
|
||||
// CHECK-NEXT: (+1.000000e+000+2.000000e+000i)
|
||||
// CHECK-NEXT: (-1.000000e+000-2.000000e+000i)
|
||||
// CHECK-NEXT: true
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var f32 float32 = 1
|
||||
var f64 float64 = 1
|
||||
c64 := complex(f32, f32+1)
|
||||
println(c64)
|
||||
println(-c64)
|
||||
println(c64 == c64)
|
||||
c128 := complex(f64, f64+1)
|
||||
println(c128)
|
||||
println(-c128)
|
||||
println(c128 == c128)
|
||||
}
|
78
llgo/test/execution/const.go
Normal file
78
llgo/test/execution/const.go
Normal file
@ -0,0 +1,78 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 1 1
|
||||
// CHECK-NEXT: 1 1 1 4
|
||||
// CHECK-NEXT: 2147483647
|
||||
// CHECK-NEXT: -2147483648
|
||||
// CHECK-NEXT: 2147483647
|
||||
// CHECK-NEXT: -127
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: 10000000000
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 3
|
||||
|
||||
package main
|
||||
|
||||
import "runtime"
|
||||
|
||||
const (
|
||||
a = iota * 2
|
||||
A = 1
|
||||
B
|
||||
C
|
||||
D = Z + iota
|
||||
)
|
||||
|
||||
const (
|
||||
Z = iota
|
||||
Big = 1<<31 - 1
|
||||
Big2 = -2147483648
|
||||
Big3 = 2147483647
|
||||
)
|
||||
|
||||
const (
|
||||
expbits32 uint = 8
|
||||
bias32 = -1<<(expbits32-1) + 1
|
||||
darwinAMD64 = runtime.GOOS == "darwin" && runtime.GOARCH == "amd64"
|
||||
)
|
||||
|
||||
func f1() float32 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func constArrayLen() {
|
||||
a := [...]int{1, 2, 3}
|
||||
const x = len(a)
|
||||
println(x)
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(a)
|
||||
println(B)
|
||||
println(A, A)
|
||||
println(A, B, C, D)
|
||||
println(Big)
|
||||
println(Big2)
|
||||
println(Big3)
|
||||
println(bias32)
|
||||
|
||||
// Currently fails, due to difference in C printf and Go's println
|
||||
// formatting of the exponent.
|
||||
//println(10 * 1e9)
|
||||
println(darwinAMD64)
|
||||
|
||||
// Test conversion.
|
||||
println(int64(10) * 1e9)
|
||||
|
||||
// Ensure consts work just as well when declared inside a function.
|
||||
const (
|
||||
x_ = iota
|
||||
y_
|
||||
)
|
||||
println(y_)
|
||||
|
||||
constArrayLen()
|
||||
}
|
15
llgo/test/execution/conversions/complex.go
Normal file
15
llgo/test/execution/conversions/complex.go
Normal file
@ -0,0 +1,15 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | count 0
|
||||
|
||||
package main
|
||||
|
||||
func constIntToComplex() complex128 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
var c64 complex64
|
||||
var c128 complex128
|
||||
c128 = complex128(c64)
|
||||
c64 = complex64(c128)
|
||||
}
|
103
llgo/test/execution/conversions/float.go
Normal file
103
llgo/test/execution/conversions/float.go
Normal file
@ -0,0 +1,103 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: -123
|
||||
// CHECK-NEXT: -123
|
||||
// CHECK-NEXT: -123
|
||||
// CHECK-NEXT: -123
|
||||
// CHECK-NEXT: 133
|
||||
// CHECK-NEXT: 65413
|
||||
// CHECK-NEXT: 4294967173
|
||||
// CHECK-NEXT: 18446744073709551493
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: -123
|
||||
// CHECK-NEXT: -123
|
||||
// CHECK-NEXT: -123
|
||||
// CHECK-NEXT: -123
|
||||
// CHECK-NEXT: 133
|
||||
// CHECK-NEXT: 65413
|
||||
// CHECK-NEXT: 4294967173
|
||||
// CHECK-NEXT: 18446744073709551493
|
||||
// CHECK-NEXT: +1.230000e+002
|
||||
// CHECK-NEXT: +1.230000e+002
|
||||
// CHECK-NEXT: +1.230000e+002
|
||||
// CHECK-NEXT: +1.230000e+002
|
||||
// CHECK-NEXT: +1.234500e+004
|
||||
// CHECK-NEXT: +1.234500e+004
|
||||
// CHECK-NEXT: +1.234500e+004
|
||||
// CHECK-NEXT: +1.234500e+004
|
||||
// CHECK-NEXT: +1.234560e+005
|
||||
// CHECK-NEXT: +1.234560e+005
|
||||
// CHECK-NEXT: +1.234560e+005
|
||||
// CHECK-NEXT: +1.234560e+005
|
||||
// CHECK-NEXT: +1.234568e+010
|
||||
// CHECK-NEXT: +1.234568e+010
|
||||
// CHECK-NEXT: +1.234568e+010
|
||||
// CHECK-NEXT: +1.234568e+010
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
// float to int
|
||||
for _, f32 := range []float32{123.456, -123.456} {
|
||||
println(int8(f32))
|
||||
println(int16(f32))
|
||||
println(int32(f32))
|
||||
println(int64(f32))
|
||||
println(uint8(f32))
|
||||
println(uint16(f32))
|
||||
println(uint32(f32))
|
||||
println(uint64(f32))
|
||||
}
|
||||
for _, f64 := range []float64{123.456, -123.456} {
|
||||
println(int8(f64))
|
||||
println(int16(f64))
|
||||
println(int32(f64))
|
||||
println(int64(f64))
|
||||
println(uint8(f64))
|
||||
println(uint16(f64))
|
||||
println(uint32(f64))
|
||||
println(uint64(f64))
|
||||
}
|
||||
|
||||
// int to float
|
||||
var i8 int8 = 123
|
||||
println(float32(i8))
|
||||
println(float64(i8))
|
||||
var ui8 uint8 = 123
|
||||
println(float32(ui8))
|
||||
println(float64(ui8))
|
||||
var i16 int32 = 12345
|
||||
println(float32(i16))
|
||||
println(float64(i16))
|
||||
var ui16 uint32 = 12345
|
||||
println(float32(ui16))
|
||||
println(float64(ui16))
|
||||
var i32 int32 = 123456
|
||||
println(float32(i32))
|
||||
println(float64(i32))
|
||||
var ui32 uint32 = 123456
|
||||
println(float32(ui32))
|
||||
println(float64(ui32))
|
||||
var i64 int64 = 12345678910
|
||||
println(float32(i64))
|
||||
println(float64(i64))
|
||||
var ui64 uint64 = 12345678910
|
||||
println(float32(ui64))
|
||||
println(float64(ui64))
|
||||
}
|
44
llgo/test/execution/conversions/int.go
Normal file
44
llgo/test/execution/conversions/int.go
Normal file
@ -0,0 +1,44 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 2147483647
|
||||
// CHECK-NEXT: 2147483647
|
||||
// CHECK-NEXT: 2147483647
|
||||
// CHECK-NEXT: 2147483648
|
||||
// CHECK-NEXT: -2147483648
|
||||
// CHECK-NEXT: 18446744071562067968
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: -1
|
||||
// CHECK-NEXT: 4294967295
|
||||
// CHECK-NEXT: 4294967295
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 1
|
||||
|
||||
package main
|
||||
|
||||
func signed(i32 int32) {
|
||||
println(uint32(i32))
|
||||
println(int64(i32))
|
||||
println(uint64(i32))
|
||||
}
|
||||
|
||||
func unsigned(u32 uint32) {
|
||||
println(int32(u32))
|
||||
println(int64(u32))
|
||||
println(uint64(u32))
|
||||
}
|
||||
|
||||
func main() {
|
||||
signed(1<<31 - 1)
|
||||
signed(-1 << 31)
|
||||
signed(0)
|
||||
unsigned(1<<32 - 1)
|
||||
unsigned(0)
|
||||
unsigned(1)
|
||||
}
|
15
llgo/test/execution/conversions/sameunderlying.go
Normal file
15
llgo/test/execution/conversions/sameunderlying.go
Normal file
@ -0,0 +1,15 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | count 0
|
||||
|
||||
package main
|
||||
|
||||
type X struct{}
|
||||
type Y X
|
||||
|
||||
func main() {
|
||||
var x X
|
||||
px := &x
|
||||
py := (*Y)(&x)
|
||||
py = (*Y)(px)
|
||||
_ = py
|
||||
}
|
125
llgo/test/execution/defer.go
Normal file
125
llgo/test/execution/defer.go
Normal file
@ -0,0 +1,125 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: f2.1
|
||||
// CHECK-NEXT: f5
|
||||
// CHECK-NEXT: recovered no error
|
||||
// CHECK-NEXT: f5
|
||||
// CHECK-NEXT: recovered: meep meep
|
||||
// CHECK-NEXT: 888
|
||||
// CHECK-NEXT: f5
|
||||
// CHECK-NEXT: recovered no error
|
||||
// CHECK-NEXT: f5
|
||||
// CHECK-NEXT: recovered no error
|
||||
// CHECK-NEXT: 888
|
||||
// CHECK-NEXT: 456
|
||||
// CHECK-NEXT: 999
|
||||
// CHECK-NEXT: 999
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: 999
|
||||
// CHECK-NEXT: 999
|
||||
// CHECK-NEXT: 246
|
||||
// CHECK-NEXT: f2.2
|
||||
// CHECK-NEXT: f2.3
|
||||
// CHECK-NEXT: f1.1
|
||||
// CHECK-NEXT: f1.2
|
||||
// CHECK-NEXT: recovered: second
|
||||
// CHECK-NEXT: ahoy
|
||||
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
value int
|
||||
}
|
||||
|
||||
type T1 struct {
|
||||
T
|
||||
}
|
||||
|
||||
func (t T) abc() {
|
||||
println(t.value)
|
||||
}
|
||||
|
||||
func (t *T) def() {
|
||||
println(t.value)
|
||||
}
|
||||
|
||||
func (t *T) ghi(v int) {
|
||||
println(v)
|
||||
}
|
||||
|
||||
func printerr(err interface{}) {
|
||||
if err != nil {
|
||||
println("recovered:", err.(string))
|
||||
} else {
|
||||
println("recovered no error")
|
||||
}
|
||||
}
|
||||
|
||||
func f6() {
|
||||
defer func() { printerr(recover()) }()
|
||||
defer func() { panic("second") }()
|
||||
panic("first")
|
||||
}
|
||||
|
||||
func f5(panic_ bool) {
|
||||
var t1 T1
|
||||
t1.T.value = 888
|
||||
defer t1.abc()
|
||||
var f func(int)
|
||||
f = func(recursion int) {
|
||||
if recursion > 0 {
|
||||
f(recursion - 1)
|
||||
return
|
||||
}
|
||||
println("f5")
|
||||
printerr(recover())
|
||||
}
|
||||
defer f(0) // will recover (after f(1))
|
||||
defer f(1) // won't recover
|
||||
if panic_ {
|
||||
panic("meep meep")
|
||||
}
|
||||
}
|
||||
|
||||
func f4() {
|
||||
var a T = T{999}
|
||||
var b *T = &a
|
||||
defer a.abc()
|
||||
defer a.def()
|
||||
defer a.ghi(123)
|
||||
defer b.abc()
|
||||
defer b.def()
|
||||
defer b.ghi(456)
|
||||
f5(true)
|
||||
f5(false) // verify the recover in f5 works
|
||||
}
|
||||
|
||||
func f3() (a int) {
|
||||
defer func() { a *= 2 }()
|
||||
f4()
|
||||
return 123
|
||||
}
|
||||
|
||||
func f2() {
|
||||
defer func() { println("f2.3") }()
|
||||
defer func(s string) { println(s) }("f2.2")
|
||||
println("f2.1")
|
||||
println(f3())
|
||||
}
|
||||
|
||||
func f1() {
|
||||
defer func() { println("f1.2") }()
|
||||
defer func() { println("f1.1") }()
|
||||
f2()
|
||||
}
|
||||
|
||||
func builtins() {
|
||||
defer println("ahoy")
|
||||
}
|
||||
|
||||
func main() {
|
||||
f1()
|
||||
f6()
|
||||
builtins()
|
||||
}
|
11
llgo/test/execution/errors/recover.go
Normal file
11
llgo/test/execution/errors/recover.go
Normal file
@ -0,0 +1,11 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: (0x0,0x0)
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
err := recover()
|
||||
println(err)
|
||||
}
|
41
llgo/test/execution/for/branch.go
Normal file
41
llgo/test/execution/for/branch.go
Normal file
@ -0,0 +1,41 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: 3
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: 3
|
||||
// CHECK-NEXT: 4
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
for i := 0; true; i++ {
|
||||
println(i)
|
||||
if i == 2 {
|
||||
println(3)
|
||||
break
|
||||
}
|
||||
println(1)
|
||||
i++
|
||||
continue
|
||||
println("unreachable")
|
||||
}
|
||||
|
||||
nums := [...]int{0, 1, 2, 3, 4, 5}
|
||||
for n := range nums {
|
||||
if n == 1 {
|
||||
continue
|
||||
}
|
||||
println(n)
|
||||
if n == 4 {
|
||||
{
|
||||
break
|
||||
}
|
||||
println("!")
|
||||
}
|
||||
}
|
||||
}
|
28
llgo/test/execution/fun.go
Normal file
28
llgo/test/execution/fun.go
Normal file
@ -0,0 +1,28 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 246
|
||||
// CHECK-NEXT: 123 true false
|
||||
|
||||
// vim: set ft=go :
|
||||
|
||||
package main
|
||||
|
||||
func test() func() int {
|
||||
return blah
|
||||
}
|
||||
|
||||
func blah() int {
|
||||
return 123
|
||||
}
|
||||
|
||||
func sret() (int, bool, bool) {
|
||||
return 123, true, false
|
||||
}
|
||||
|
||||
func main() {
|
||||
f := test()
|
||||
println(2 * f())
|
||||
a, b, c := sret()
|
||||
println(a, b, c)
|
||||
}
|
26
llgo/test/execution/functions/compare.go
Normal file
26
llgo/test/execution/functions/compare.go
Normal file
@ -0,0 +1,26 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: true
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: true
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: true
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: true
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var f func()
|
||||
println(f == nil)
|
||||
println(f != nil)
|
||||
println(nil == f)
|
||||
println(nil != f)
|
||||
f = func() {}
|
||||
println(f == nil)
|
||||
println(f != nil)
|
||||
println(nil == f)
|
||||
println(nil != f)
|
||||
}
|
28
llgo/test/execution/functions/multivalue.go
Normal file
28
llgo/test/execution/functions/multivalue.go
Normal file
@ -0,0 +1,28 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 1
|
||||
// CHECK-NEXT: 20
|
||||
// CHECK-NEXT: extra: 10
|
||||
|
||||
package main
|
||||
|
||||
func swap(a, b int) (int, int) {
|
||||
return b, a
|
||||
}
|
||||
|
||||
func sub(a, b int) int {
|
||||
return a - b
|
||||
}
|
||||
|
||||
func printint(a int, extra ...int) {
|
||||
println(a)
|
||||
for _, b := range extra {
|
||||
println("extra:", b)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(sub(swap(1, 2)))
|
||||
printint(swap(10, 20))
|
||||
}
|
53
llgo/test/execution/functions/unreachable.go
Normal file
53
llgo/test/execution/functions/unreachable.go
Normal file
@ -0,0 +1,53 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: f1
|
||||
// CHECK-NEXT: f2
|
||||
// CHECK-NEXT: f3
|
||||
// CHECK-NEXT: f4
|
||||
// CHECK-NEXT: 123
|
||||
|
||||
package main
|
||||
|
||||
func f1() {
|
||||
if true {
|
||||
println("f1")
|
||||
return
|
||||
}
|
||||
for {
|
||||
}
|
||||
}
|
||||
|
||||
func f2() {
|
||||
defer func() { println("f2") }()
|
||||
if true {
|
||||
return
|
||||
}
|
||||
for {
|
||||
}
|
||||
}
|
||||
|
||||
func f3() int {
|
||||
if true {
|
||||
println("f3")
|
||||
return 123
|
||||
}
|
||||
for {
|
||||
}
|
||||
}
|
||||
|
||||
func f4() int {
|
||||
defer func() { println("f4") }()
|
||||
if true {
|
||||
return 123
|
||||
}
|
||||
for {
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
f1()
|
||||
f2()
|
||||
f3()
|
||||
println(f4())
|
||||
}
|
34
llgo/test/execution/go.go
Normal file
34
llgo/test/execution/go.go
Normal file
@ -0,0 +1,34 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: hello from T 1
|
||||
// CHECK-NEXT: hello from T 2
|
||||
|
||||
package main
|
||||
|
||||
type T struct {
|
||||
val int
|
||||
}
|
||||
|
||||
func (t T) Hello(done chan bool) {
|
||||
println("hello from T", t.val)
|
||||
done <- true
|
||||
}
|
||||
|
||||
type I interface {
|
||||
Hello(chan bool)
|
||||
}
|
||||
|
||||
func main() {
|
||||
done := make(chan bool)
|
||||
|
||||
t := T{1}
|
||||
go t.Hello(done)
|
||||
<-done
|
||||
|
||||
var i I = T{2}
|
||||
go i.Hello(done)
|
||||
<-done
|
||||
|
||||
go println("hello builtin")
|
||||
}
|
46
llgo/test/execution/if/lazy.go
Normal file
46
llgo/test/execution/if/lazy.go
Normal file
@ -0,0 +1,46 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: False()
|
||||
// CHECK-NEXT: False()
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: False()
|
||||
// CHECK-NEXT: True()
|
||||
// CHECK-NEXT: true
|
||||
// CHECK-NEXT: True()
|
||||
// CHECK-NEXT: true
|
||||
// CHECK-NEXT: True()
|
||||
// CHECK-NEXT: true
|
||||
// CHECK-NEXT: False()
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: False()
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: True()
|
||||
// CHECK-NEXT: False()
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: True()
|
||||
// CHECK-NEXT: True()
|
||||
// CHECK-NEXT: true
|
||||
|
||||
package main
|
||||
|
||||
func False() bool {
|
||||
println("False()")
|
||||
return false
|
||||
}
|
||||
|
||||
func True() bool {
|
||||
println("True()")
|
||||
return true
|
||||
}
|
||||
|
||||
func main() {
|
||||
println(False() || False())
|
||||
println(False() || True())
|
||||
println(True() || False())
|
||||
println(True() || True())
|
||||
println(False() && False())
|
||||
println(False() && True())
|
||||
println(True() && False())
|
||||
println(True() && True())
|
||||
}
|
17
llgo/test/execution/init.go
Normal file
17
llgo/test/execution/init.go
Normal file
@ -0,0 +1,17 @@
|
||||
// RUN: llgo -o %t %s %p/Inputs/init2.go
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
package main
|
||||
|
||||
// CHECK-DAG: do some other stuff before main
|
||||
//func init()
|
||||
|
||||
// CHECK-DAG: do some stuff before main
|
||||
func init() {
|
||||
println("do some stuff before main")
|
||||
}
|
||||
|
||||
// CHECK: main has been called
|
||||
func main() {
|
||||
println("main has been called")
|
||||
}
|
61
llgo/test/execution/interfaces/assert.go
Normal file
61
llgo/test/execution/interfaces/assert.go
Normal file
@ -0,0 +1,61 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: x is nil
|
||||
// CHECK-NEXT: i2v: 123456
|
||||
// CHECK-NEXT: !
|
||||
// CHECK-NEXT: (*X).F1: 123456
|
||||
|
||||
package main
|
||||
|
||||
type X struct{ x int }
|
||||
|
||||
func (x *X) F1() { println("(*X).F1:", x.x) }
|
||||
func (x *X) F2() { println("(*X).F2") }
|
||||
|
||||
type I interface {
|
||||
F1()
|
||||
F2()
|
||||
}
|
||||
|
||||
func main() {
|
||||
var x interface{}
|
||||
|
||||
// x is nil. Let's make sure an assertion on it
|
||||
// won't cause a panic.
|
||||
if x, ok := x.(int32); ok {
|
||||
println("i2v:", x)
|
||||
}
|
||||
if x == nil {
|
||||
println("x is nil")
|
||||
}
|
||||
|
||||
x = int32(123456)
|
||||
|
||||
// Let's try an interface-to-value assertion.
|
||||
if x, ok := x.(int32); ok {
|
||||
println("i2v:", x)
|
||||
}
|
||||
if x, ok := x.(int64); ok {
|
||||
println("i2v:", x)
|
||||
}
|
||||
|
||||
// This will fail the assertion.
|
||||
if i, ok := x.(I); ok {
|
||||
i.F1()
|
||||
_ = i
|
||||
} else {
|
||||
println("!")
|
||||
}
|
||||
|
||||
// Assign an *X, which should pass the assertion.
|
||||
x_ := new(X)
|
||||
x_.x = 123456
|
||||
x = x_ //&X{x: 123456}
|
||||
if i, ok := x.(I); ok {
|
||||
i.F1()
|
||||
_ = i
|
||||
} else {
|
||||
println("!")
|
||||
}
|
||||
}
|
43
llgo/test/execution/interfaces/basic.go
Normal file
43
llgo/test/execution/interfaces/basic.go
Normal file
@ -0,0 +1,43 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: expected: y != z
|
||||
|
||||
package main
|
||||
|
||||
type any interface{}
|
||||
|
||||
type Stringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type lessThanAWord struct {
|
||||
a byte
|
||||
}
|
||||
|
||||
func (l lessThanAWord) String() string {
|
||||
return "!"
|
||||
}
|
||||
|
||||
func makeAStringer() Stringer {
|
||||
return lessThanAWord{}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var x1, x2 int = 1, 2
|
||||
var y any = x1
|
||||
var z any = x2
|
||||
if y != z {
|
||||
println("expected: y != z")
|
||||
} else {
|
||||
println("unexpected: y == z")
|
||||
}
|
||||
/*
|
||||
if y == x1 {
|
||||
println("expected: y == x1")
|
||||
} else {
|
||||
println("unexpected: y == x1")
|
||||
}
|
||||
*/
|
||||
//println(y.(int))
|
||||
}
|
30
llgo/test/execution/interfaces/comparei2i.go
Normal file
30
llgo/test/execution/interfaces/comparei2i.go
Normal file
@ -0,0 +1,30 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: true
|
||||
// CHECK-NEXT: true
|
||||
|
||||
package main
|
||||
|
||||
import "unsafe"
|
||||
|
||||
type I interface {
|
||||
X()
|
||||
}
|
||||
|
||||
type T int
|
||||
|
||||
func (t T) X() {
|
||||
}
|
||||
|
||||
func main() {
|
||||
var highbit uint32 = 1 << 31
|
||||
var pos0 float32 = 0
|
||||
var neg0 float32 = *(*float32)(unsafe.Pointer(&highbit))
|
||||
var i1 interface{} = pos0
|
||||
var i2 interface{} = neg0
|
||||
println(i1 == i2)
|
||||
var i3 interface{} = T(123)
|
||||
var i4 I = T(123)
|
||||
println(i3 == i4)
|
||||
}
|
13
llgo/test/execution/interfaces/comparei2v.go
Normal file
13
llgo/test/execution/interfaces/comparei2v.go
Normal file
@ -0,0 +1,13 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: true
|
||||
// CHECK-NEXT: false
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
var x interface{} = 123
|
||||
println(x == 123)
|
||||
println(x != 123)
|
||||
}
|
22
llgo/test/execution/interfaces/e2i_conversion.go
Normal file
22
llgo/test/execution/interfaces/e2i_conversion.go
Normal file
@ -0,0 +1,22 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | count 0
|
||||
|
||||
package main
|
||||
|
||||
import "io"
|
||||
|
||||
type rdr struct{}
|
||||
|
||||
func (r rdr) Read(b []byte) (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func F(i interface{}) {
|
||||
_ = i.(io.Reader)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var r rdr
|
||||
F(r)
|
||||
F(&r)
|
||||
}
|
32
llgo/test/execution/interfaces/embedded.go
Normal file
32
llgo/test/execution/interfaces/embedded.go
Normal file
@ -0,0 +1,32 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: A
|
||||
// CHECK-NEXT: B
|
||||
|
||||
package main
|
||||
|
||||
type BI interface {
|
||||
B()
|
||||
}
|
||||
|
||||
type AI interface {
|
||||
A()
|
||||
BI
|
||||
}
|
||||
|
||||
type S struct{}
|
||||
|
||||
func (s S) A() {
|
||||
println("A")
|
||||
}
|
||||
|
||||
func (s S) B() {
|
||||
println("B")
|
||||
}
|
||||
|
||||
func main() {
|
||||
var ai AI = S{}
|
||||
ai.A()
|
||||
ai.B()
|
||||
}
|
43
llgo/test/execution/interfaces/error.go
Normal file
43
llgo/test/execution/interfaces/error.go
Normal file
@ -0,0 +1,43 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: !!!! 123
|
||||
// CHECK-NEXT: errno 123
|
||||
|
||||
package main
|
||||
|
||||
var errors = [...]string{}
|
||||
|
||||
func itoa(val int) string { // do it here rather than with fmt to avoid dependency
|
||||
if val < 0 {
|
||||
return "-" + itoa(-val)
|
||||
}
|
||||
var buf [32]byte // big enough for int64
|
||||
i := len(buf) - 1
|
||||
for val >= 10 {
|
||||
buf[i] = byte(val%10 + '0')
|
||||
i--
|
||||
val /= 10
|
||||
}
|
||||
buf[i] = byte(val + '0')
|
||||
return string(buf[i:])
|
||||
}
|
||||
|
||||
type Errno uintptr
|
||||
|
||||
func (e Errno) Error() string {
|
||||
println("!!!!", uintptr(e))
|
||||
if 0 <= int(e) && int(e) < len(errors) {
|
||||
s := errors[e]
|
||||
if s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return "errno " + itoa(int(e))
|
||||
}
|
||||
|
||||
func main() {
|
||||
e := Errno(123)
|
||||
i := (interface{})(e)
|
||||
println(i.(error).Error())
|
||||
}
|
33
llgo/test/execution/interfaces/i2i_conversion.go
Normal file
33
llgo/test/execution/interfaces/i2i_conversion.go
Normal file
@ -0,0 +1,33 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 666
|
||||
// CHECK-NEXT: The Beast
|
||||
|
||||
package main
|
||||
|
||||
type Numbered interface {
|
||||
Number() int
|
||||
}
|
||||
|
||||
type Named interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
type Beast struct{}
|
||||
|
||||
func (b *Beast) Number() int {
|
||||
return 666
|
||||
}
|
||||
|
||||
func (b *Beast) Name() string {
|
||||
return "The Beast"
|
||||
}
|
||||
|
||||
func main() {
|
||||
var b Beast
|
||||
var numbered Numbered = &b
|
||||
var named Named = numbered.(Named)
|
||||
println(numbered.Number())
|
||||
println(named.Name())
|
||||
}
|
16
llgo/test/execution/interfaces/import.go
Normal file
16
llgo/test/execution/interfaces/import.go
Normal file
@ -0,0 +1,16 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
package main
|
||||
|
||||
import "syscall"
|
||||
|
||||
type Signal interface {
|
||||
Signal()
|
||||
}
|
||||
|
||||
func main() {
|
||||
var s Signal = syscall.SIGINT
|
||||
// CHECK: ({{.*}},{{.*}})
|
||||
println(s)
|
||||
}
|
53
llgo/test/execution/interfaces/methods.go
Normal file
53
llgo/test/execution/interfaces/methods.go
Normal file
@ -0,0 +1,53 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: X()
|
||||
// CHECK-NEXT: Y()
|
||||
// CHECK-NEXT: X()
|
||||
// CHECK-NEXT: Y()
|
||||
// CHECK-NEXT: X()
|
||||
|
||||
package main
|
||||
|
||||
type Stringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type X int
|
||||
type Y int
|
||||
|
||||
type Z1 struct {
|
||||
X
|
||||
}
|
||||
|
||||
type Z2 struct {
|
||||
Stringer
|
||||
}
|
||||
|
||||
func (x X) String() string {
|
||||
return "X()"
|
||||
}
|
||||
|
||||
func (y *Y) String() string {
|
||||
return "Y()"
|
||||
}
|
||||
|
||||
func makeX() X {
|
||||
return X(0)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var z Stringer = X(0)
|
||||
println(z.String())
|
||||
|
||||
z = new(Y)
|
||||
println(z.String())
|
||||
|
||||
z = Z1{}
|
||||
println(z.String())
|
||||
|
||||
z = Z2{new(Y)}
|
||||
println(z.String())
|
||||
|
||||
println(makeX().String())
|
||||
}
|
35
llgo/test/execution/interfaces/static_conversion.go
Normal file
35
llgo/test/execution/interfaces/static_conversion.go
Normal file
@ -0,0 +1,35 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 666
|
||||
// CHECK-NEXT: 3
|
||||
|
||||
package main
|
||||
|
||||
type Blah interface{}
|
||||
type Numbered interface {
|
||||
Blah
|
||||
Number() int
|
||||
}
|
||||
|
||||
type Beast struct{}
|
||||
|
||||
func (b *Beast) Number() int {
|
||||
return 666
|
||||
}
|
||||
|
||||
type MagicNumber int
|
||||
|
||||
func (m MagicNumber) Number() int {
|
||||
return int(m)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var b Beast
|
||||
var m MagicNumber = 3
|
||||
var n Numbered = &b
|
||||
println(n.Number())
|
||||
|
||||
n = m
|
||||
println(n.Number())
|
||||
}
|
40
llgo/test/execution/interfaces/wordsize.go
Normal file
40
llgo/test/execution/interfaces/wordsize.go
Normal file
@ -0,0 +1,40 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: StringStringer(abc)
|
||||
// CHECK-NEXT: abc 1 2 3
|
||||
|
||||
package main
|
||||
|
||||
type Stringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type StringStringer string
|
||||
|
||||
func (s StringStringer) String() string {
|
||||
return "StringStringer(" + string(s) + ")"
|
||||
}
|
||||
|
||||
func (s StringStringer) MethodWithArgs(a, b, c int) {
|
||||
println(s, a, b, c)
|
||||
}
|
||||
|
||||
type I interface {
|
||||
MethodWithArgs(a, b, c int)
|
||||
}
|
||||
|
||||
func testLargerThanWord() {
|
||||
// string is larger than a word. Make sure it works
|
||||
// well as a method receiver when using interfaces.
|
||||
var s Stringer = StringStringer("abc")
|
||||
println(s.String())
|
||||
|
||||
// Test calling a method which takes parameters
|
||||
// beyond the receiver.
|
||||
s.(I).MethodWithArgs(1, 2, 3)
|
||||
}
|
||||
|
||||
func main() {
|
||||
testLargerThanWord()
|
||||
}
|
78
llgo/test/execution/literals/array.go
Normal file
78
llgo/test/execution/literals/array.go
Normal file
@ -0,0 +1,78 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 9223372036854775808 -63 false
|
||||
// CHECK-NEXT: 11529215046068469760 -60 false
|
||||
// CHECK-NEXT: 14411518807585587200 -57 false
|
||||
// CHECK-NEXT: 18014398509481984000 -54 false
|
||||
// CHECK-NEXT: 11258999068426240000 -50 false
|
||||
// CHECK-NEXT: 14073748835532800000 -47 false
|
||||
// CHECK-NEXT: 17592186044416000000 -44 false
|
||||
// CHECK-NEXT: 10995116277760000000 -40 false
|
||||
// CHECK-NEXT: 0 0
|
||||
// CHECK-NEXT: 1 0
|
||||
// CHECK-NEXT: 2 1
|
||||
// CHECK-NEXT: 3 0
|
||||
// CHECK-NEXT: 4 2
|
||||
// CHECK-NEXT: 5 0
|
||||
// CHECK-NEXT: 6 3
|
||||
// CHECK-NEXT: 7 0
|
||||
// CHECK-NEXT: 8 4
|
||||
// CHECK-NEXT: 9 0
|
||||
// CHECK-NEXT: 0 1
|
||||
// CHECK-NEXT: 1 2
|
||||
|
||||
package main
|
||||
|
||||
// An extFloat represents an extended floating-point number, with more
|
||||
// precision than a float64. It does not try to save bits: the
|
||||
// number represented by the structure is mant*(2^exp), with a negative
|
||||
// sign if neg is true.
|
||||
type extFloat struct {
|
||||
mant uint64
|
||||
exp int
|
||||
neg bool
|
||||
}
|
||||
|
||||
var smallPowersOfTen = [...]extFloat{
|
||||
{1 << 63, -63, false}, // 1
|
||||
{0xa << 60, -60, false}, // 1e1
|
||||
{0x64 << 57, -57, false}, // 1e2
|
||||
{0x3e8 << 54, -54, false}, // 1e3
|
||||
{0x2710 << 50, -50, false}, // 1e4
|
||||
{0x186a0 << 47, -47, false}, // 1e5
|
||||
{0xf4240 << 44, -44, false}, // 1e6
|
||||
{0x989680 << 40, -40, false}, // 1e7
|
||||
}
|
||||
|
||||
var arrayWithHoles = [10]int{
|
||||
2: 1,
|
||||
4: 2,
|
||||
6: 3,
|
||||
8: 4,
|
||||
}
|
||||
|
||||
type namedInt int32
|
||||
|
||||
const N0 namedInt = 0
|
||||
const N1 namedInt = 1
|
||||
|
||||
var arrayWithNamedIndices = [...]int{
|
||||
N0: 1,
|
||||
N1: 2,
|
||||
}
|
||||
|
||||
func main() {
|
||||
for i := range smallPowersOfTen {
|
||||
s := smallPowersOfTen[i]
|
||||
println(s.mant, s.exp, s.neg)
|
||||
}
|
||||
|
||||
for i, value := range arrayWithHoles {
|
||||
println(i, value)
|
||||
}
|
||||
|
||||
for i, value := range arrayWithNamedIndices {
|
||||
println(i, value)
|
||||
}
|
||||
}
|
15
llgo/test/execution/literals/func.go
Normal file
15
llgo/test/execution/literals/func.go
Normal file
@ -0,0 +1,15 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: true
|
||||
// CHECK-NEXT: false
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
f := func(x bool) {
|
||||
println(x)
|
||||
}
|
||||
f(true)
|
||||
f(false)
|
||||
}
|
24
llgo/test/execution/literals/map.go
Normal file
24
llgo/test/execution/literals/map.go
Normal file
@ -0,0 +1,24 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: false
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: 1 0 3
|
||||
// CHECK-NEXT: 0.1
|
||||
// CHECK-NEXT: 0.2
|
||||
// CHECK-NEXT: 0.3
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
type IntMap map[int]int
|
||||
m := IntMap{0: 1, 2: 3}
|
||||
println(m == nil)
|
||||
println(len(m))
|
||||
println(m[0], m[1], m[2])
|
||||
|
||||
f32tostr := map[float32]string{0.1: "0.1", 0.2: "0.2", 0.3: "0.3"}
|
||||
println(f32tostr[0.1])
|
||||
println(f32tostr[0.2])
|
||||
println(f32tostr[0.3])
|
||||
}
|
21
llgo/test/execution/literals/slice.go
Normal file
21
llgo/test/execution/literals/slice.go
Normal file
@ -0,0 +1,21 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: abc
|
||||
// CHECK-NEXT: 123
|
||||
// CHECK-NEXT: abc
|
||||
// CHECK-NEXT: 123
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
x := []string{"abc", "123"}
|
||||
println(x[0])
|
||||
println(x[1])
|
||||
|
||||
// Elements are composite literals, so the '&' can be elided.
|
||||
type S struct{ string }
|
||||
y := []*S{{"abc"}, {"123"}}
|
||||
println(y[0].string)
|
||||
println(y[1].string)
|
||||
}
|
62
llgo/test/execution/literals/struct.go
Normal file
62
llgo/test/execution/literals/struct.go
Normal file
@ -0,0 +1,62 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 1 2
|
||||
// CHECK-NEXT: 1 2
|
||||
// CHECK-NEXT: 0 1 2
|
||||
// CHECK-NEXT: 1 2
|
||||
// CHECK-NEXT: 3 4
|
||||
|
||||
package main
|
||||
|
||||
type E struct {
|
||||
e *E
|
||||
}
|
||||
|
||||
type S struct {
|
||||
*E
|
||||
a, b int
|
||||
}
|
||||
|
||||
type File struct {
|
||||
}
|
||||
|
||||
type Reader struct {
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
}
|
||||
|
||||
type reader struct {
|
||||
*Reader
|
||||
fd *File
|
||||
resp *Response
|
||||
}
|
||||
|
||||
type Range32 struct {
|
||||
Lo uint32
|
||||
Hi uint32
|
||||
Stride uint32
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := &S{nil, 1, 2}
|
||||
println(s.a, s.b)
|
||||
s = &S{a: 1, b: 2}
|
||||
println(s.a, s.b)
|
||||
|
||||
_ = &reader{}
|
||||
|
||||
r := Range32{
|
||||
Lo: 0,
|
||||
Stride: 2,
|
||||
Hi: 1,
|
||||
}
|
||||
println(r.Lo, r.Hi, r.Stride)
|
||||
|
||||
// slice of structs
|
||||
ss := []S{{nil, 1, 2}, {nil, 3, 4}}
|
||||
for _, s := range ss {
|
||||
println(s.a, s.b)
|
||||
}
|
||||
}
|
19
llgo/test/execution/maps/delete.go
Normal file
19
llgo/test/execution/maps/delete.go
Normal file
@ -0,0 +1,19 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 1
|
||||
// CHECK-NEXT: 1 1
|
||||
// CHECK-NEXT: 0 0
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
m := make(map[int]int)
|
||||
delete(m, 0) // no-op
|
||||
m[0] = 1
|
||||
println(len(m))
|
||||
delete(m, 1) // no-op
|
||||
println(len(m), m[0])
|
||||
delete(m, 0) // delete element in map
|
||||
println(len(m), m[0])
|
||||
}
|
29
llgo/test/execution/maps/insert.go
Normal file
29
llgo/test/execution/maps/insert.go
Normal file
@ -0,0 +1,29 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 456
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 789
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
{
|
||||
var m map[int]int
|
||||
println(len(m)) // 0
|
||||
println(m[123]) // 0, despite map being nil
|
||||
}
|
||||
|
||||
{
|
||||
m := make(map[int]int)
|
||||
m[123] = 456
|
||||
println(len(m)) // 1
|
||||
println(m[123])
|
||||
m[123] = 789
|
||||
println(len(m)) // 1
|
||||
println(m[123])
|
||||
}
|
||||
}
|
30
llgo/test/execution/maps/lookup.go
Normal file
30
llgo/test/execution/maps/lookup.go
Normal file
@ -0,0 +1,30 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0 false
|
||||
// CHECK-NEXT: 1 true
|
||||
// CHECK-NEXT: 1 true
|
||||
// CHECK-NEXT: 1 true
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
m := make(map[int]int)
|
||||
v, ok := m[8]
|
||||
println(v, ok)
|
||||
m[8] = 1
|
||||
v, ok = m[8]
|
||||
println(v, ok)
|
||||
|
||||
type S struct{ s1, s2 string }
|
||||
sm := make(map[S]int)
|
||||
sm[S{"ABC", "DEF"}] = 1
|
||||
sv, ok := sm[S{string([]byte{65, 66, 67}), string([]byte{68, 69, 70})}]
|
||||
println(sv, ok)
|
||||
|
||||
type A [2]string
|
||||
am := make(map[A]int)
|
||||
am[A{"ABC", "DEF"}] = 1
|
||||
av, ok := am[A{string([]byte{65, 66, 67}), string([]byte{68, 69, 70})}]
|
||||
println(av, ok)
|
||||
}
|
48
llgo/test/execution/maps/range.go
Normal file
48
llgo/test/execution/maps/range.go
Normal file
@ -0,0 +1,48 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: 0
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: 2
|
||||
// CHECK-NEXT: 3
|
||||
// CHECK-NEXT: 4
|
||||
// CHECK-NEXT: 5
|
||||
// CHECK-NEXT: 0 3
|
||||
// CHECK-NEXT: 1 4
|
||||
// CHECK-NEXT: 2 5
|
||||
// CHECK-NEXT: 1
|
||||
// CHECK-NEXT: done
|
||||
|
||||
package main
|
||||
|
||||
func main() {
|
||||
defer println("done")
|
||||
m := make(map[int]int)
|
||||
m[0] = 3
|
||||
m[1] = 4
|
||||
m[2] = 5
|
||||
for k := range m {
|
||||
println(k)
|
||||
}
|
||||
for k, _ := range m {
|
||||
println(k)
|
||||
}
|
||||
for _, v := range m {
|
||||
println(v)
|
||||
}
|
||||
for k, v := range m {
|
||||
println(k, v)
|
||||
}
|
||||
|
||||
// test deletion.
|
||||
i := 0
|
||||
for k, _ := range m {
|
||||
i++
|
||||
delete(m, (k+1)%3)
|
||||
delete(m, (k+2)%3)
|
||||
}
|
||||
println(i)
|
||||
}
|
64
llgo/test/execution/methods/methodvalues.go
Normal file
64
llgo/test/execution/methods/methodvalues.go
Normal file
@ -0,0 +1,64 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: 246
|
||||
// CHECK-NEXT: T2.f()
|
||||
// CHECK-NEXT: 10
|
||||
// CHECK-NEXT: abc
|
||||
|
||||
package main
|
||||
|
||||
type T1 struct {
|
||||
value int
|
||||
}
|
||||
|
||||
func (t *T1) f(m int) int {
|
||||
return m * t.value
|
||||
}
|
||||
|
||||
func f1() {
|
||||
var t T1
|
||||
var f func(int) int = t.f
|
||||
t.value = 2
|
||||
println(f(123))
|
||||
}
|
||||
|
||||
type T2 struct{}
|
||||
|
||||
func (T2) f() {
|
||||
println("T2.f()")
|
||||
}
|
||||
|
||||
func f2() {
|
||||
var f func() = T2{}.f
|
||||
f()
|
||||
}
|
||||
|
||||
type T3 complex128
|
||||
|
||||
func (t T3) f() int {
|
||||
return int(real(t))
|
||||
}
|
||||
|
||||
func f3() {
|
||||
var f func() int = T3(10).f
|
||||
println(f())
|
||||
}
|
||||
|
||||
type T4 string
|
||||
|
||||
func (t T4) f() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
func f4() {
|
||||
var f func() string = T4("abc").f
|
||||
println(f())
|
||||
}
|
||||
|
||||
func main() {
|
||||
f1()
|
||||
f2()
|
||||
f3()
|
||||
f4()
|
||||
}
|
31
llgo/test/execution/methods/nilrecv.go
Normal file
31
llgo/test/execution/methods/nilrecv.go
Normal file
@ -0,0 +1,31 @@
|
||||
// RUN: llgo -o %t %s
|
||||
// RUN: %t 2>&1 | FileCheck %s
|
||||
|
||||
// CHECK: true
|
||||
// CHECK-NEXT: false
|
||||
// CHECK-NEXT: true
|
||||
// CHECK-NEXT: false
|
||||
|
||||
package main
|
||||
|
||||
type T1 int
|
||||
|
||||
func (t *T1) t1() { println(t == nil) }
|
||||
|
||||
func constNilRecv() {
|
||||
(*T1)(nil).t1()
|
||||
}
|
||||
|
||||
func nonConstNilRecv() {
|
||||
var v1 T1
|
||||
v1.t1()
|
||||
var v2 *T1
|
||||
v2.t1()
|
||||
v2 = &v1
|
||||
v2.t1()
|
||||
}
|
||||
|
||||
func main() {
|
||||
constNilRecv()
|
||||
nonConstNilRecv()
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user