Initial commit of llgo.

llvm-svn: 222857
This commit is contained in:
Peter Collingbourne 2014-11-27 00:06:42 +00:00
parent 5468e86469
commit ad9841e8ac
148 changed files with 15789 additions and 0 deletions

170
llgo/CMakeLists.txt Normal file
View 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
View 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
View 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
View 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
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

97
llgo/irgen/strings.go Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

22
llgo/irgen/types.go Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

19
llgo/llgo-go.sh Normal file
View 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
View 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
View 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
View 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")

View File

@ -0,0 +1,7 @@
// RUN: llgo -c -o /dev/null -g %s
package main
//line :1
func main() {
}

View 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'

View File

@ -0,0 +1,5 @@
package main
func init() {
println("do some other stuff before main")
}

View 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)
}

View 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()
}

View 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)
}
}

View 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))
}

View 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])
}

View 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)
}

View 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)
}

View 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)
}

View 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)
}

View 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
}

View 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()
}

View 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)
}

View 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)
}
}

View 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()
}

View 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)
}

View 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
}

View 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"))
}

View 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)
}

View 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)
}

View 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()
}

View 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)
}

View 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))
}

View 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)
}

View 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
}

View 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()
}

View 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)
}

View 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("!")
}
}
}

View 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)
}

View 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)
}

View 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))
}

View 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
View 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")
}

View 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())
}

View 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")
}

View 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("!")
}
}

View 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))
}

View 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)
}

View 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)
}

View 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)
}

View 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()
}

View 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())
}

View 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())
}

View 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)
}

View 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())
}

View 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())
}

View 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()
}

View 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)
}
}

View 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)
}

View 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])
}

View 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)
}

View 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)
}
}

View 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])
}

View 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])
}
}

View 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)
}

View 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)
}

View 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()
}

View 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