Index: cmake/modules/AddLLVM.cmake =================================================================== --- cmake/modules/AddLLVM.cmake +++ cmake/modules/AddLLVM.cmake @@ -603,6 +603,31 @@ endif () endfunction() +function(llvm_add_go_executable binary pkgpath) + cmake_parse_arguments(ARG "ALL" "" "DEPENDS;GOFLAGS" ${ARGN}) + + if(LLVM_BINDINGS MATCHES "go") + # FIXME: This should depend only on the libraries Go needs. + get_property(llvmlibs GLOBAL PROPERTY LLVM_LIBS) + set(binpath ${CMAKE_BINARY_DIR}/bin/${binary}${CMAKE_EXECUTABLE_SUFFIX}) + set(cc "${CMAKE_C_COMPILER} ${CMAKE_C_COMPILER_ARG1}") + set(cxx "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1}") + set(ldflags "${CMAKE_EXE_LINKER_FLAGS}") + add_custom_command(OUTPUT ${binpath} + COMMAND ${CMAKE_BINARY_DIR}/bin/llvm-go "cc=${cc}" "cxx=${cxx}" "ldflags=${ldflags}" + ${ARG_GOFLAGS} build -o ${binpath} ${pkgpath} + DEPENDS llvm-config ${CMAKE_BINARY_DIR}/bin/llvm-go${CMAKE_EXECUTABLE_SUFFIX} + ${llvmlibs} ${ARG_DEPENDS} + COMMENT "Building Go executable ${binary}" + VERBATIM) + if (ARG_ALL) + add_custom_target(${binary} ALL DEPENDS ${binpath}) + else() + add_custom_target(${binary} DEPENDS ${binpath}) + endif() + endif() +endfunction() + # This function provides an automatic way to 'configure'-like generate a file # based on a set of common and custom variables, specifically targeting the # variables needed for the 'lit.site.cfg' files. This function bundles the Index: tools/CMakeLists.txt =================================================================== --- tools/CMakeLists.txt +++ tools/CMakeLists.txt @@ -81,6 +81,7 @@ endif() add_llvm_external_project(clang) +add_llvm_external_project(llgo) if( NOT LLVM_INCLUDE_TOOLS STREQUAL "bootstrap-only" ) add_llvm_external_project(lld) Index: tools/llgo/.gitignore =================================================================== --- /dev/null +++ tools/llgo/.gitignore @@ -0,0 +1,9 @@ +*.o +*.so +*.a +*.[68] +_obj +bin +cmd/gllgo/gllgo +test/**/Output +workdir Index: tools/llgo/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llgo/CMakeLists.txt @@ -0,0 +1,171 @@ +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 + COMMAND ${CMAKE_COMMAND} -E make_directory + 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 /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 /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 /configure --disable-multilib --without-libatomic --prefix= "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" + ${cmake_3_2_USES_TERMINAL}) + +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) Index: tools/llgo/LICENSE.TXT =================================================================== --- /dev/null +++ tools/llgo/LICENSE.TXT @@ -0,0 +1,62 @@ +============================================================================== +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. + +============================================================================== +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 + Index: tools/llgo/README.TXT =================================================================== --- /dev/null +++ tools/llgo/README.TXT @@ -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 . + +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. Index: tools/llgo/build/context.go =================================================================== --- /dev/null +++ tools/llgo/build/context.go @@ -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 +} Index: tools/llgo/cmd/cc-wrapper/main.go =================================================================== --- /dev/null +++ tools/llgo/cmd/cc-wrapper/main.go @@ -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...)) +} Index: tools/llgo/cmd/gllgo/gllgo.go =================================================================== --- /dev/null +++ tools/llgo/cmd/gllgo/gllgo.go @@ -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) + } +} Index: tools/llgo/debug/debug.go =================================================================== --- /dev/null +++ tools/llgo/debug/debug.go @@ -0,0 +1,458 @@ +//===- 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 { + return d.descriptorBasic(types.Typ[types.Uintptr], name) +} + +func (d *DIBuilder) descriptorChan(t *types.Chan, name string) llvm.Value { + 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, + }) +} Index: tools/llgo/irgen/annotations.go =================================================================== --- /dev/null +++ tools/llgo/irgen/annotations.go @@ -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 ( + "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" + "go/ast" + "go/token" + "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...) + } + } + } + } +} Index: tools/llgo/irgen/attribute.go =================================================================== --- /dev/null +++ tools/llgo/irgen/attribute.go @@ -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) +} Index: tools/llgo/irgen/builtins.go =================================================================== --- /dev/null +++ tools/llgo/irgen/builtins.go @@ -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() +} Index: tools/llgo/irgen/cabi.go =================================================================== --- /dev/null +++ tools/llgo/irgen/cabi.go @@ -0,0 +1,654 @@ +//===- 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 + + 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) + } + + default: + argTypes = append(argTypes, tm.classifyEightbyte([]offsetedType{{bt, 0}}, &numInt, &numSSE)) + argAttrs = append(argAttrs, 0) + } + + 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) +} Index: tools/llgo/irgen/call.go =================================================================== --- /dev/null +++ tools/llgo/irgen/call.go @@ -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 +} Index: tools/llgo/irgen/channels.go =================================================================== --- /dev/null +++ tools/llgo/irgen/channels.go @@ -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 +} Index: tools/llgo/irgen/closures.go =================================================================== --- /dev/null +++ tools/llgo/irgen/closures.go @@ -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()) +} Index: tools/llgo/irgen/compiler.go =================================================================== --- /dev/null +++ tools/llgo/irgen/compiler.go @@ -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 : Index: tools/llgo/irgen/errors.go =================================================================== --- /dev/null +++ tools/llgo/irgen/errors.go @@ -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) +} Index: tools/llgo/irgen/indirect.go =================================================================== --- /dev/null +++ tools/llgo/irgen/indirect.go @@ -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 +} Index: tools/llgo/irgen/interfaces.go =================================================================== --- /dev/null +++ tools/llgo/irgen/interfaces.go @@ -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) +} Index: tools/llgo/irgen/maps.go =================================================================== --- /dev/null +++ tools/llgo/irgen/maps.go @@ -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)} +} Index: tools/llgo/irgen/parser.go =================================================================== --- /dev/null +++ tools/llgo/irgen/parser.go @@ -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 +} Index: tools/llgo/irgen/predicates.go =================================================================== --- /dev/null +++ tools/llgo/irgen/predicates.go @@ -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]) +} Index: tools/llgo/irgen/println.go =================================================================== --- /dev/null +++ tools/llgo/irgen/println.go @@ -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) + } +} Index: tools/llgo/irgen/runtime.go =================================================================== --- /dev/null +++ tools/llgo/irgen/runtime.go @@ -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}, "") +} Index: tools/llgo/irgen/slice.go =================================================================== --- /dev/null +++ tools/llgo/irgen/slice.go @@ -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 +} Index: tools/llgo/irgen/ssa.go =================================================================== --- /dev/null +++ tools/llgo/irgen/ssa.go @@ -0,0 +1,1303 @@ +//===- ssa.go - IR generation from go/ssa ---------------------------------===// +// +// 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 top-level LLVM IR generation from go/ssa form. +// +//===----------------------------------------------------------------------===// + +package irgen + +import ( + "fmt" + "go/ast" + "go/token" + "os" + "sort" + + "llvm.org/llgo/third_party/go.tools/go/ssa" + "llvm.org/llgo/third_party/go.tools/go/ssa/ssautil" + "llvm.org/llgo/third_party/go.tools/go/types" + "llvm.org/llgo/ssaopt" + "llvm.org/llvm/bindings/go/llvm" +) + +// A globalInit is used to temporarily store a global's initializer until +// we are ready to build it. +type globalInit struct { + val llvm.Value + elems []globalInit +} + +func (gi *globalInit) update(typ llvm.Type, indices []uint32, val llvm.Value) { + if len(indices) == 0 { + gi.val = val + return + } + + if gi.val.C != nil { + gi.val = llvm.ConstInsertValue(gi.val, val, indices) + } + + tk := typ.TypeKind() + + if len(gi.elems) == 0 { + switch tk { + case llvm.StructTypeKind: + gi.elems = make([]globalInit, typ.StructElementTypesCount()) + case llvm.ArrayTypeKind: + gi.elems = make([]globalInit, typ.ArrayLength()) + default: + panic("unexpected type") + } + } + + var eltyp llvm.Type + switch tk { + case llvm.StructTypeKind: + eltyp = typ.StructElementTypes()[indices[0]] + case llvm.ArrayTypeKind: + eltyp = typ.ElementType() + default: + panic("unexpected type") + } + + gi.elems[indices[0]].update(eltyp, indices[1:], val) +} + +func (gi *globalInit) build(typ llvm.Type) llvm.Value { + if gi.val.C != nil { + return gi.val + } + if len(gi.elems) == 0 { + return llvm.ConstNull(typ) + } + + switch typ.TypeKind() { + case llvm.StructTypeKind: + eltypes := typ.StructElementTypes() + elems := make([]llvm.Value, len(eltypes)) + for i, eltyp := range eltypes { + elems[i] = gi.elems[i].build(eltyp) + } + return llvm.ConstStruct(elems, false) + case llvm.ArrayTypeKind: + eltyp := typ.ElementType() + elems := make([]llvm.Value, len(gi.elems)) + for i := range gi.elems { + elems[i] = gi.elems[i].build(eltyp) + } + return llvm.ConstArray(eltyp, elems) + default: + panic("unexpected type") + } +} + +type unit struct { + *compiler + pkg *ssa.Package + globals map[ssa.Value]llvm.Value + globalInits map[llvm.Value]*globalInit + + // funcDescriptors maps *ssa.Functions to function descriptors, + // the first-class representation of functions. + funcDescriptors map[*ssa.Function]llvm.Value + + // undefinedFuncs contains functions that have been resolved + // (declared) but not defined. + undefinedFuncs map[*ssa.Function]bool + + gcRoots []llvm.Value +} + +func newUnit(c *compiler, pkg *ssa.Package) *unit { + u := &unit{ + compiler: c, + pkg: pkg, + globals: make(map[ssa.Value]llvm.Value), + globalInits: make(map[llvm.Value]*globalInit), + funcDescriptors: make(map[*ssa.Function]llvm.Value), + undefinedFuncs: make(map[*ssa.Function]bool), + } + return u +} + +type byMemberName []ssa.Member + +func (ms byMemberName) Len() int { return len(ms) } +func (ms byMemberName) Swap(i, j int) { + ms[i], ms[j] = ms[j], ms[i] +} +func (ms byMemberName) Less(i, j int) bool { + return ms[i].Name() < ms[j].Name() +} + +type byFunctionString []*ssa.Function + +func (fns byFunctionString) Len() int { return len(fns) } +func (fns byFunctionString) Swap(i, j int) { + fns[i], fns[j] = fns[j], fns[i] +} +func (fns byFunctionString) Less(i, j int) bool { + return fns[i].String() < fns[j].String() +} + +// Emit functions in order of their fully qualified names. This is so that a +// bootstrap build can be verified by comparing the stage2 and stage3 binaries. +func (u *unit) defineFunctionsInOrder(functions map[*ssa.Function]bool) { + fns := []*ssa.Function{} + for f, _ := range functions { + fns = append(fns, f) + } + sort.Sort(byFunctionString(fns)) + for _, f := range fns { + u.defineFunction(f) + } +} + +// translatePackage translates an *ssa.Package into an LLVM module, and returns +// the translation unit information. +func (u *unit) translatePackage(pkg *ssa.Package) { + ms := make([]ssa.Member, len(pkg.Members)) + i := 0 + for _, m := range pkg.Members { + ms[i] = m + i++ + } + + sort.Sort(byMemberName(ms)) + + // Initialize global storage and type descriptors for this package. + // We must create globals regardless of whether they're referenced, + // hence the duplication in frame.value. + for _, m := range ms { + switch v := m.(type) { + case *ssa.Global: + elemtyp := deref(v.Type()) + llelemtyp := u.llvmtypes.ToLLVM(elemtyp) + vname := u.types.mc.mangleGlobalName(v) + global := llvm.AddGlobal(u.module.Module, llelemtyp, vname) + if !v.Object().Exported() { + global.SetLinkage(llvm.InternalLinkage) + } + u.addGlobal(global, elemtyp) + global = llvm.ConstBitCast(global, u.llvmtypes.ToLLVM(v.Type())) + u.globals[v] = global + case *ssa.Type: + u.types.getTypeDescriptorPointer(v.Type()) + } + } + + // Define functions. + u.defineFunctionsInOrder(ssautil.AllFunctions(pkg.Prog)) + + // Emit initializers for type descriptors, which may trigger + // the resolution of additional functions. + u.types.emitTypeDescInitializers() + + // Define remaining functions that were resolved during + // runtime type mapping, but not defined. + u.defineFunctionsInOrder(u.undefinedFuncs) + + // Set initializers for globals. + for global, init := range u.globalInits { + initval := init.build(global.Type().ElementType()) + global.SetInitializer(initval) + } +} + +func (u *unit) addGlobal(global llvm.Value, ty types.Type) { + u.globalInits[global] = new(globalInit) + + if hasPointers(ty) { + global = llvm.ConstBitCast(global, llvm.PointerType(llvm.Int8Type(), 0)) + size := llvm.ConstInt(u.types.inttype, uint64(u.types.Sizeof(ty)), false) + root := llvm.ConstStruct([]llvm.Value{global, size}, false) + u.gcRoots = append(u.gcRoots, root) + } +} + +// ResolveMethod implements MethodResolver.ResolveMethod. +func (u *unit) ResolveMethod(s *types.Selection) *govalue { + m := u.pkg.Prog.Method(s) + llfn := u.resolveFunctionGlobal(m) + llfn = llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0)) + return newValue(llfn, m.Signature) +} + +// resolveFunctionDescriptorGlobal returns a reference to the LLVM global +// storing the function's descriptor. +func (u *unit) resolveFunctionDescriptorGlobal(f *ssa.Function) llvm.Value { + llfd, ok := u.funcDescriptors[f] + if !ok { + name := u.types.mc.mangleFunctionName(f) + "$descriptor" + llfd = llvm.AddGlobal(u.module.Module, llvm.PointerType(llvm.Int8Type(), 0), name) + llfd.SetGlobalConstant(true) + u.funcDescriptors[f] = llfd + } + return llfd +} + +// resolveFunctionDescriptor returns a function's +// first-class value representation. +func (u *unit) resolveFunctionDescriptor(f *ssa.Function) *govalue { + llfd := u.resolveFunctionDescriptorGlobal(f) + llfd = llvm.ConstBitCast(llfd, llvm.PointerType(llvm.Int8Type(), 0)) + return newValue(llfd, f.Signature) +} + +// resolveFunctionGlobal returns an llvm.Value for a function global. +func (u *unit) resolveFunctionGlobal(f *ssa.Function) llvm.Value { + if v, ok := u.globals[f]; ok { + return v + } + name := u.types.mc.mangleFunctionName(f) + // It's possible that the function already exists in the module; + // for example, if it's a runtime intrinsic that the compiler + // has already referenced. + llvmFunction := u.module.Module.NamedFunction(name) + if llvmFunction.IsNil() { + fti := u.llvmtypes.getSignatureInfo(f.Signature) + llvmFunction = fti.declare(u.module.Module, name) + u.undefinedFuncs[f] = true + } + u.globals[f] = llvmFunction + return llvmFunction +} + +func (u *unit) getFunctionLinkage(f *ssa.Function) llvm.Linkage { + switch { + case f.Pkg == nil: + // Synthetic functions outside packages may appear in multiple packages. + return llvm.LinkOnceODRLinkage + + case f.Parent() != nil: + // Anonymous. + return llvm.InternalLinkage + + case f.Signature.Recv() == nil && !ast.IsExported(f.Name()) && + !(f.Name() == "main" && f.Pkg.Object.Path() == "main") && + f.Name() != "init": + // Unexported methods may be referenced as part of an interface method + // table in another package. TODO(pcc): detect when this cannot happen. + return llvm.InternalLinkage + + default: + return llvm.ExternalLinkage + } +} + +func (u *unit) defineFunction(f *ssa.Function) { + // Only define functions from this package, or synthetic + // wrappers (which do not have a package). + if f.Pkg != nil && f.Pkg != u.pkg { + return + } + + llfn := u.resolveFunctionGlobal(f) + linkage := u.getFunctionLinkage(f) + + isMethod := f.Signature.Recv() != nil + + // Methods cannot be referred to via a descriptor. + if !isMethod { + llfd := u.resolveFunctionDescriptorGlobal(f) + llfd.SetInitializer(llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0))) + llfd.SetLinkage(linkage) + } + + // We only need to emit a descriptor for functions without bodies. + if len(f.Blocks) == 0 { + return + } + + ssaopt.LowerAllocsToStack(f) + + if u.DumpSSA { + f.WriteTo(os.Stderr) + } + + fr := newFrame(u, llfn) + defer fr.dispose() + fr.addCommonFunctionAttrs(fr.function) + fr.function.SetLinkage(linkage) + + fr.logf("Define function: %s", f.String()) + fti := u.llvmtypes.getSignatureInfo(f.Signature) + delete(u.undefinedFuncs, f) + fr.retInf = fti.retInf + + // Push the compile unit and function onto the debug context. + if u.GenerateDebug { + u.debug.PushFunction(fr.function, f.Signature, f.Pos()) + defer u.debug.PopFunction() + u.debug.SetLocation(fr.builder, f.Pos()) + } + + // If a function calls recover, we create a separate function to + // hold the real function, and this function calls __go_can_recover + // and bridges to it. + if callsRecover(f) { + fr = fr.bridgeRecoverFunc(fr.function, fti) + } + + fr.blocks = make([]llvm.BasicBlock, len(f.Blocks)) + fr.lastBlocks = make([]llvm.BasicBlock, len(f.Blocks)) + for i, block := range f.Blocks { + fr.blocks[i] = llvm.AddBasicBlock(fr.function, fmt.Sprintf(".%d.%s", i, block.Comment)) + } + fr.builder.SetInsertPointAtEnd(fr.blocks[0]) + + prologueBlock := llvm.InsertBasicBlock(fr.blocks[0], "prologue") + fr.builder.SetInsertPointAtEnd(prologueBlock) + + // Map parameter positions to indices. We use this + // when processing locals to map back to parameters + // when generating debug metadata. + paramPos := make(map[token.Pos]int) + for i, param := range f.Params { + paramPos[param.Pos()] = i + llparam := fti.argInfos[i].decode(llvm.GlobalContext(), fr.builder, fr.builder) + if isMethod && i == 0 { + if _, ok := param.Type().Underlying().(*types.Pointer); !ok { + llparam = fr.builder.CreateBitCast(llparam, llvm.PointerType(fr.types.ToLLVM(param.Type()), 0), "") + llparam = fr.builder.CreateLoad(llparam, "") + } + } + fr.env[param] = newValue(llparam, param.Type()) + } + + // Load closure, extract free vars. + if len(f.FreeVars) > 0 { + for _, fv := range f.FreeVars { + fr.env[fv] = newValue(llvm.ConstNull(u.llvmtypes.ToLLVM(fv.Type())), fv.Type()) + } + elemTypes := make([]llvm.Type, len(f.FreeVars)+1) + elemTypes[0] = llvm.PointerType(llvm.Int8Type(), 0) // function pointer + for i, fv := range f.FreeVars { + elemTypes[i+1] = u.llvmtypes.ToLLVM(fv.Type()) + } + structType := llvm.StructType(elemTypes, false) + closure := fr.runtime.getClosure.call(fr)[0] + closure = fr.builder.CreateBitCast(closure, llvm.PointerType(structType, 0), "") + for i, fv := range f.FreeVars { + ptr := fr.builder.CreateStructGEP(closure, i+1, "") + ptr = fr.builder.CreateLoad(ptr, "") + fr.env[fv] = newValue(ptr, fv.Type()) + } + } + + // Allocate stack space for locals in the prologue block. + for _, local := range f.Locals { + typ := fr.llvmtypes.ToLLVM(deref(local.Type())) + alloca := fr.builder.CreateAlloca(typ, local.Comment) + fr.memsetZero(alloca, llvm.SizeOf(typ)) + bcalloca := fr.builder.CreateBitCast(alloca, llvm.PointerType(llvm.Int8Type(), 0), "") + value := newValue(bcalloca, local.Type()) + fr.env[local] = value + if fr.GenerateDebug { + paramIndex, ok := paramPos[local.Pos()] + if !ok { + paramIndex = -1 + } + fr.debug.Declare(fr.builder, local, alloca, paramIndex) + } + } + + // If this is the "init" function, enable init-specific optimizations. + if !isMethod && f.Name() == "init" { + fr.isInit = true + } + + // If the function contains any defers, we must first create + // an unwind block. We can short-circuit the check for defers with + // f.Recover != nil. + if f.Recover != nil || hasDefer(f) { + fr.unwindBlock = llvm.AddBasicBlock(fr.function, "") + fr.frameptr = fr.builder.CreateAlloca(llvm.Int8Type(), "") + } + + term := fr.builder.CreateBr(fr.blocks[0]) + fr.allocaBuilder.SetInsertPointBefore(term) + + for _, block := range f.DomPreorder() { + fr.translateBlock(block, fr.blocks[block.Index]) + } + + fr.fixupPhis() + + if !fr.unwindBlock.IsNil() { + fr.setupUnwindBlock(f.Recover, f.Signature.Results()) + } + + // The init function needs to register the GC roots first. We do this + // after generating code for it because allocations may have caused + // additional GC roots to be created. + if fr.isInit { + fr.builder.SetInsertPointBefore(prologueBlock.FirstInstruction()) + fr.registerGcRoots() + } +} + +type pendingPhi struct { + ssa *ssa.Phi + llvm llvm.Value +} + +type frame struct { + *unit + function llvm.Value + builder, allocaBuilder llvm.Builder + retInf retInfo + blocks []llvm.BasicBlock + lastBlocks []llvm.BasicBlock + runtimeErrorBlocks [gccgoRuntimeErrorCount]llvm.BasicBlock + unwindBlock llvm.BasicBlock + frameptr llvm.Value + env map[ssa.Value]*govalue + ptr map[ssa.Value]llvm.Value + tuples map[ssa.Value][]*govalue + phis []pendingPhi + canRecover llvm.Value + isInit bool +} + +func newFrame(u *unit, fn llvm.Value) *frame { + return &frame{ + unit: u, + function: fn, + builder: llvm.GlobalContext().NewBuilder(), + allocaBuilder: llvm.GlobalContext().NewBuilder(), + env: make(map[ssa.Value]*govalue), + ptr: make(map[ssa.Value]llvm.Value), + tuples: make(map[ssa.Value][]*govalue), + } +} + +func (fr *frame) dispose() { + fr.builder.Dispose() + fr.allocaBuilder.Dispose() +} + +// bridgeRecoverFunc creates a function that may call recover(), and creates +// a call to it from the current frame. The created function will be called +// with a boolean parameter that indicates whether it may call recover(). +// +// The created function will have the same name as the current frame's function +// with "$recover" appended, having the same return types and parameters with +// an additional boolean parameter appended. +// +// A new frame will be returned for the newly created function. +func (fr *frame) bridgeRecoverFunc(llfn llvm.Value, fti functionTypeInfo) *frame { + // The bridging function must not be inlined, or the return address + // may not correspond to the source function. + llfn.AddFunctionAttr(llvm.NoInlineAttribute) + + // Call __go_can_recover, passing in the function's return address. + entry := llvm.AddBasicBlock(llfn, "entry") + fr.builder.SetInsertPointAtEnd(entry) + canRecover := fr.runtime.canRecover.call(fr, fr.returnAddress(0))[0] + returnType := fti.functionType.ReturnType() + argTypes := fti.functionType.ParamTypes() + argTypes = append(argTypes, canRecover.Type()) + + // Create and call the $recover function. + ftiRecover := fti + ftiRecover.functionType = llvm.FunctionType(returnType, argTypes, false) + llfnRecover := ftiRecover.declare(fr.module.Module, llfn.Name()+"$recover") + fr.addCommonFunctionAttrs(llfnRecover) + llfnRecover.SetLinkage(llvm.InternalLinkage) + args := make([]llvm.Value, len(argTypes)-1, len(argTypes)) + for i := range args { + args[i] = llfn.Param(i) + } + args = append(args, canRecover) + result := fr.builder.CreateCall(llfnRecover, args, "") + if returnType.TypeKind() == llvm.VoidTypeKind { + fr.builder.CreateRetVoid() + } else { + fr.builder.CreateRet(result) + } + + // The $recover function must condition calls to __go_recover on + // the result of __go_can_recover passed in as an argument. + fr = newFrame(fr.unit, llfnRecover) + fr.retInf = ftiRecover.retInf + fr.canRecover = fr.function.Param(len(argTypes) - 1) + return fr +} + +func (fr *frame) registerGcRoots() { + if len(fr.gcRoots) != 0 { + rootty := fr.gcRoots[0].Type() + roots := append(fr.gcRoots, llvm.ConstNull(rootty)) + rootsarr := llvm.ConstArray(rootty, roots) + rootsstruct := llvm.ConstStruct([]llvm.Value{llvm.ConstNull(llvm.PointerType(llvm.Int8Type(), 0)), rootsarr}, false) + + rootsglobal := llvm.AddGlobal(fr.module.Module, rootsstruct.Type(), "") + rootsglobal.SetInitializer(rootsstruct) + rootsglobal.SetLinkage(llvm.InternalLinkage) + fr.runtime.registerGcRoots.callOnly(fr, llvm.ConstBitCast(rootsglobal, llvm.PointerType(llvm.Int8Type(), 0))) + } +} + +func (fr *frame) fixupPhis() { + for _, phi := range fr.phis { + values := make([]llvm.Value, len(phi.ssa.Edges)) + blocks := make([]llvm.BasicBlock, len(phi.ssa.Edges)) + block := phi.ssa.Block() + for i, edge := range phi.ssa.Edges { + values[i] = fr.llvmvalue(edge) + blocks[i] = fr.lastBlock(block.Preds[i]) + } + phi.llvm.AddIncoming(values, blocks) + } +} + +func (fr *frame) createLandingPad(cleanup bool) llvm.Value { + lp := fr.builder.CreateLandingPad(fr.runtime.gccgoExceptionType, fr.runtime.gccgoPersonality, 0, "") + if cleanup { + lp.SetCleanup(true) + } else { + lp.AddClause(llvm.ConstNull(llvm.PointerType(llvm.Int8Type(), 0))) + } + return lp +} + +// Runs defers. If a defer panics, check for recovers in later defers. +func (fr *frame) runDefers() { + loopbb := llvm.AddBasicBlock(fr.function, "") + fr.builder.CreateBr(loopbb) + + retrylpad := llvm.AddBasicBlock(fr.function, "") + fr.builder.SetInsertPointAtEnd(retrylpad) + fr.createLandingPad(false) + fr.runtime.checkDefer.callOnly(fr, fr.frameptr) + fr.builder.CreateBr(loopbb) + + fr.builder.SetInsertPointAtEnd(loopbb) + fr.runtime.undefer.invoke(fr, retrylpad, fr.frameptr) +} + +func (fr *frame) setupUnwindBlock(rec *ssa.BasicBlock, results *types.Tuple) { + recoverbb := llvm.AddBasicBlock(fr.function, "") + if rec != nil { + fr.translateBlock(rec, recoverbb) + } else if results.Len() == 0 || results.At(0).Anonymous() { + // TODO(pcc): Remove this code after https://codereview.appspot.com/87210044/ lands + fr.builder.SetInsertPointAtEnd(recoverbb) + values := make([]llvm.Value, results.Len()) + for i := range values { + values[i] = llvm.ConstNull(fr.llvmtypes.ToLLVM(results.At(i).Type())) + } + fr.retInf.encode(llvm.GlobalContext(), fr.allocaBuilder, fr.builder, values) + } else { + fr.builder.SetInsertPointAtEnd(recoverbb) + fr.builder.CreateUnreachable() + } + + checkunwindbb := llvm.AddBasicBlock(fr.function, "") + fr.builder.SetInsertPointAtEnd(checkunwindbb) + exc := fr.createLandingPad(true) + fr.runDefers() + + frame := fr.builder.CreateLoad(fr.frameptr, "") + shouldresume := fr.builder.CreateIsNull(frame, "") + + resumebb := llvm.AddBasicBlock(fr.function, "") + fr.builder.CreateCondBr(shouldresume, resumebb, recoverbb) + + fr.builder.SetInsertPointAtEnd(resumebb) + fr.builder.CreateResume(exc) + + fr.builder.SetInsertPointAtEnd(fr.unwindBlock) + fr.createLandingPad(false) + fr.runtime.checkDefer.invoke(fr, checkunwindbb, fr.frameptr) + fr.runDefers() + fr.builder.CreateBr(recoverbb) +} + +func (fr *frame) translateBlock(b *ssa.BasicBlock, llb llvm.BasicBlock) { + fr.builder.SetInsertPointAtEnd(llb) + for _, instr := range b.Instrs { + fr.instruction(instr) + } + fr.lastBlocks[b.Index] = fr.builder.GetInsertBlock() +} + +func (fr *frame) block(b *ssa.BasicBlock) llvm.BasicBlock { + return fr.blocks[b.Index] +} + +func (fr *frame) lastBlock(b *ssa.BasicBlock) llvm.BasicBlock { + return fr.lastBlocks[b.Index] +} + +func (fr *frame) value(v ssa.Value) (result *govalue) { + switch v := v.(type) { + case nil: + return nil + case *ssa.Function: + return fr.resolveFunctionDescriptor(v) + case *ssa.Const: + return fr.newValueFromConst(v.Value, v.Type()) + case *ssa.Global: + if g, ok := fr.globals[v]; ok { + return newValue(g, v.Type()) + } + // Create an external global. Globals for this package are defined + // on entry to translatePackage, and have initialisers. + llelemtyp := fr.llvmtypes.ToLLVM(deref(v.Type())) + vname := fr.types.mc.mangleGlobalName(v) + llglobal := llvm.AddGlobal(fr.module.Module, llelemtyp, vname) + llglobal = llvm.ConstBitCast(llglobal, fr.llvmtypes.ToLLVM(v.Type())) + fr.globals[v] = llglobal + return newValue(llglobal, v.Type()) + } + if value, ok := fr.env[v]; ok { + return value + } + + panic("Instruction not visited yet") +} + +func (fr *frame) llvmvalue(v ssa.Value) llvm.Value { + if gv := fr.value(v); gv != nil { + return gv.value + } else { + return llvm.Value{nil} + } +} + +func (fr *frame) isNonNull(v ssa.Value) bool { + switch v.(type) { + case + // Globals have a fixed (non-nil) address. + *ssa.Global, + // The language does not specify what happens if an allocation fails. + *ssa.Alloc, + // These have already been nil checked. + *ssa.FieldAddr, *ssa.IndexAddr: + return true + default: + return false + } +} + +func (fr *frame) nilCheck(v ssa.Value, llptr llvm.Value) { + if !fr.isNonNull(v) { + ptrnull := fr.builder.CreateIsNull(llptr, "") + fr.condBrRuntimeError(ptrnull, gccgoRuntimeErrorNIL_DEREFERENCE) + } +} + +func (fr *frame) canAvoidElementLoad(refs []ssa.Instruction) bool { + for _, ref := range refs { + switch ref.(type) { + case *ssa.Field, *ssa.Index: + // ok + default: + return false + } + } + + return true +} + +// If this value is sufficiently large, look through referrers to see if we can +// avoid a load. +func (fr *frame) canAvoidLoad(instr *ssa.UnOp, op llvm.Value) bool { + if fr.types.Sizeof(instr.Type()) < 16 { + // Don't bother with small values. + return false + } + + // Keep track of whether our pointer may escape. We conservatively assume + // that MakeInterfaces will escape. + esc := false + + // We only know how to avoid loads if they are used to create an interface + // or read an element of the structure. If we see any other referrer, abort. + for _, ref := range *instr.Referrers() { + switch ref.(type) { + case *ssa.MakeInterface: + esc = true + case *ssa.Field, *ssa.Index: + // ok + default: + return false + } + } + + var opcopy llvm.Value + if esc { + opcopy = fr.createTypeMalloc(instr.Type()) + } else { + opcopy = fr.allocaBuilder.CreateAlloca(fr.types.ToLLVM(instr.Type()), "") + } + fr.memcpy(opcopy, op, llvm.ConstInt(fr.types.inttype, uint64(fr.types.Sizeof(instr.Type())), false)) + + fr.ptr[instr] = opcopy + return true +} + +// Return true iff we think it might be beneficial to turn this alloc instruction +// into a statically allocated global. +// Precondition: we are compiling the init function. +func (fr *frame) shouldStaticallyAllocate(alloc *ssa.Alloc) bool { + // First, see if the allocated type is an array or struct, and if so determine + // the number of elements in the type. If the type is anything else, we + // statically allocate unconditionally. + var numElems int64 + switch ty := deref(alloc.Type()).Underlying().(type) { + case *types.Array: + numElems = ty.Len() + case *types.Struct: + numElems = int64(ty.NumFields()) + default: + return true + } + + // We treat the number of referrers to the alloc instruction as a rough + // proxy for the number of elements initialized. If the data structure + // is densely initialized (> 1/4 elements initialized), enable the + // optimization. + return int64(len(*alloc.Referrers()))*4 > numElems +} + +// If val is a constant and addr refers to a global variable which is defined in +// this module or an element thereof, simulate the effect of storing val at addr +// in the global variable's initializer and return true, otherwise return false. +// Precondition: we are compiling the init function. +func (fr *frame) maybeStoreInInitializer(val, addr llvm.Value) bool { + if val.IsAConstant().IsNil() { + return false + } + + if !addr.IsAConstantExpr().IsNil() && addr.OperandsCount() >= 2 && + // TODO(pcc): Explicitly check that this is a constant GEP. + // I don't think there are any other kinds of constantexpr which + // satisfy the conditions we test for here, so this is probably safe. + !addr.Operand(0).IsAGlobalVariable().IsNil() && + addr.Operand(1).IsNull() { + gv := addr.Operand(0) + globalInit, ok := fr.globalInits[gv] + if !ok { + return false + } + indices := make([]uint32, addr.OperandsCount()-2) + for i := range indices { + op := addr.Operand(i + 2) + if op.IsAConstantInt().IsNil() { + return false + } + indices[i] = uint32(op.ZExtValue()) + } + globalInit.update(gv.Type().ElementType(), indices, val) + return true + } else if !addr.IsAGlobalVariable().IsNil() { + if globalInit, ok := fr.globalInits[addr]; ok { + globalInit.update(addr.Type().ElementType(), nil, val) + return true + } + return false + } else { + return false + } +} + +func (fr *frame) instruction(instr ssa.Instruction) { + fr.logf("[%T] %v @ %s\n", instr, instr, fr.pkg.Prog.Fset.Position(instr.Pos())) + if fr.GenerateDebug { + fr.debug.SetLocation(fr.builder, instr.Pos()) + } + + switch instr := instr.(type) { + case *ssa.Alloc: + typ := deref(instr.Type()) + llvmtyp := fr.llvmtypes.ToLLVM(typ) + var value llvm.Value + if !instr.Heap { + value = fr.env[instr].value + fr.memsetZero(value, llvm.SizeOf(llvmtyp)) + } else if fr.isInit && fr.shouldStaticallyAllocate(instr) { + // If this is the init function and we think it may be beneficial, + // allocate memory statically in the object file rather than on the + // heap. This allows us to optimize constant stores into such + // variables as static initializations. + global := llvm.AddGlobal(fr.module.Module, llvmtyp, "") + global.SetLinkage(llvm.InternalLinkage) + fr.addGlobal(global, typ) + ptr := llvm.ConstBitCast(global, llvm.PointerType(llvm.Int8Type(), 0)) + fr.env[instr] = newValue(ptr, instr.Type()) + } else { + value = fr.createTypeMalloc(typ) + value.SetName(instr.Comment) + value = fr.builder.CreateBitCast(value, llvm.PointerType(llvm.Int8Type(), 0), "") + fr.env[instr] = newValue(value, instr.Type()) + } + + case *ssa.BinOp: + lhs, rhs := fr.value(instr.X), fr.value(instr.Y) + fr.env[instr] = fr.binaryOp(lhs, instr.Op, rhs) + + case *ssa.Call: + tuple := fr.callInstruction(instr) + if len(tuple) == 1 { + fr.env[instr] = tuple[0] + } else { + fr.tuples[instr] = tuple + } + + case *ssa.ChangeInterface: + x := fr.value(instr.X) + // The source type must be a non-empty interface, + // as ChangeInterface cannot fail (E2I may fail). + if instr.Type().Underlying().(*types.Interface).NumMethods() > 0 { + x = fr.changeInterface(x, instr.Type(), false) + } else { + x = fr.convertI2E(x) + } + fr.env[instr] = x + + case *ssa.ChangeType: + value := fr.llvmvalue(instr.X) + if _, ok := instr.Type().Underlying().(*types.Pointer); ok { + value = fr.builder.CreateBitCast(value, fr.llvmtypes.ToLLVM(instr.Type()), "") + } + fr.env[instr] = newValue(value, instr.Type()) + + case *ssa.Convert: + v := fr.value(instr.X) + fr.env[instr] = fr.convert(v, instr.Type()) + + case *ssa.Defer: + fn, arg := fr.createThunk(instr) + fr.runtime.Defer.call(fr, fr.frameptr, fn, arg) + + case *ssa.Extract: + var elem llvm.Value + if t, ok := fr.tuples[instr.Tuple]; ok { + elem = t[instr.Index].value + } else { + tuple := fr.llvmvalue(instr.Tuple) + elem = fr.builder.CreateExtractValue(tuple, instr.Index, instr.Name()) + } + elemtyp := instr.Type() + fr.env[instr] = newValue(elem, elemtyp) + + case *ssa.Field: + fieldtyp := instr.Type() + if p, ok := fr.ptr[instr.X]; ok { + field := fr.builder.CreateStructGEP(p, instr.Field, instr.Name()) + if fr.canAvoidElementLoad(*instr.Referrers()) { + fr.ptr[instr] = field + } else { + fr.env[instr] = newValue(fr.builder.CreateLoad(field, ""), fieldtyp) + } + } else { + value := fr.llvmvalue(instr.X) + field := fr.builder.CreateExtractValue(value, instr.Field, instr.Name()) + fr.env[instr] = newValue(field, fieldtyp) + } + + case *ssa.FieldAddr: + ptr := fr.llvmvalue(instr.X) + fr.nilCheck(instr.X, ptr) + xtyp := instr.X.Type().Underlying().(*types.Pointer).Elem() + ptrtyp := llvm.PointerType(fr.llvmtypes.ToLLVM(xtyp), 0) + ptr = fr.builder.CreateBitCast(ptr, ptrtyp, "") + fieldptr := fr.builder.CreateStructGEP(ptr, instr.Field, instr.Name()) + fieldptr = fr.builder.CreateBitCast(fieldptr, llvm.PointerType(llvm.Int8Type(), 0), "") + fieldptrtyp := instr.Type() + fr.env[instr] = newValue(fieldptr, fieldptrtyp) + + case *ssa.Go: + fn, arg := fr.createThunk(instr) + fr.runtime.Go.call(fr, fn, arg) + + case *ssa.If: + cond := fr.llvmvalue(instr.Cond) + block := instr.Block() + trueBlock := fr.block(block.Succs[0]) + falseBlock := fr.block(block.Succs[1]) + cond = fr.builder.CreateTrunc(cond, llvm.Int1Type(), "") + fr.builder.CreateCondBr(cond, trueBlock, falseBlock) + + case *ssa.Index: + var arrayptr llvm.Value + + if ptr, ok := fr.ptr[instr.X]; ok { + arrayptr = ptr + } else { + array := fr.llvmvalue(instr.X) + arrayptr = fr.allocaBuilder.CreateAlloca(array.Type(), "") + + fr.builder.CreateStore(array, arrayptr) + } + index := fr.llvmvalue(instr.Index) + + arraytyp := instr.X.Type().Underlying().(*types.Array) + arraylen := llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false) + + // The index may not have been promoted to int (for example, if it + // came from a composite literal). + index = fr.createZExtOrTrunc(index, fr.types.inttype, "") + + // Bounds checking: 0 <= index < len + zero := llvm.ConstNull(fr.types.inttype) + i0 := fr.builder.CreateICmp(llvm.IntSLT, index, zero, "") + li := fr.builder.CreateICmp(llvm.IntSLE, arraylen, index, "") + + cond := fr.builder.CreateOr(i0, li, "") + + fr.condBrRuntimeError(cond, gccgoRuntimeErrorARRAY_INDEX_OUT_OF_BOUNDS) + + addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{zero, index}, "") + if fr.canAvoidElementLoad(*instr.Referrers()) { + fr.ptr[instr] = addr + } else { + fr.env[instr] = newValue(fr.builder.CreateLoad(addr, ""), instr.Type()) + } + + case *ssa.IndexAddr: + x := fr.llvmvalue(instr.X) + index := fr.llvmvalue(instr.Index) + var arrayptr, arraylen llvm.Value + var elemtyp types.Type + var errcode uint64 + switch typ := instr.X.Type().Underlying().(type) { + case *types.Slice: + elemtyp = typ.Elem() + arrayptr = fr.builder.CreateExtractValue(x, 0, "") + arraylen = fr.builder.CreateExtractValue(x, 1, "") + errcode = gccgoRuntimeErrorSLICE_INDEX_OUT_OF_BOUNDS + case *types.Pointer: // *array + arraytyp := typ.Elem().Underlying().(*types.Array) + elemtyp = arraytyp.Elem() + fr.nilCheck(instr.X, x) + arrayptr = x + arraylen = llvm.ConstInt(fr.llvmtypes.inttype, uint64(arraytyp.Len()), false) + errcode = gccgoRuntimeErrorARRAY_INDEX_OUT_OF_BOUNDS + } + + // The index may not have been promoted to int (for example, if it + // came from a composite literal). + index = fr.createZExtOrTrunc(index, fr.types.inttype, "") + + // Bounds checking: 0 <= index < len + zero := llvm.ConstNull(fr.types.inttype) + i0 := fr.builder.CreateICmp(llvm.IntSLT, index, zero, "") + li := fr.builder.CreateICmp(llvm.IntSLE, arraylen, index, "") + + cond := fr.builder.CreateOr(i0, li, "") + + fr.condBrRuntimeError(cond, errcode) + + ptrtyp := llvm.PointerType(fr.llvmtypes.ToLLVM(elemtyp), 0) + arrayptr = fr.builder.CreateBitCast(arrayptr, ptrtyp, "") + addr := fr.builder.CreateGEP(arrayptr, []llvm.Value{index}, "") + addr = fr.builder.CreateBitCast(addr, llvm.PointerType(llvm.Int8Type(), 0), "") + fr.env[instr] = newValue(addr, types.NewPointer(elemtyp)) + + case *ssa.Jump: + succ := instr.Block().Succs[0] + fr.builder.CreateBr(fr.block(succ)) + + case *ssa.Lookup: + x := fr.value(instr.X) + index := fr.value(instr.Index) + if isString(x.Type().Underlying()) { + fr.env[instr] = fr.stringIndex(x, index) + } else { + v, ok := fr.mapLookup(x, index) + if instr.CommaOk { + fr.tuples[instr] = []*govalue{v, ok} + } else { + fr.env[instr] = v + } + } + + case *ssa.MakeChan: + fr.env[instr] = fr.makeChan(instr.Type(), fr.value(instr.Size)) + + case *ssa.MakeClosure: + llfn := fr.resolveFunctionGlobal(instr.Fn.(*ssa.Function)) + llfn = llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0)) + fn := newValue(llfn, instr.Fn.(*ssa.Function).Signature) + bindings := make([]*govalue, len(instr.Bindings)) + for i, binding := range instr.Bindings { + bindings[i] = fr.value(binding) + } + fr.env[instr] = fr.makeClosure(fn, bindings) + + case *ssa.MakeInterface: + // fr.ptr[instr.X] will be set if a pointer load was elided by canAvoidLoad + if ptr, ok := fr.ptr[instr.X]; ok { + fr.env[instr] = fr.makeInterfaceFromPointer(ptr, instr.X.Type(), instr.Type()) + } else { + receiver := fr.llvmvalue(instr.X) + fr.env[instr] = fr.makeInterface(receiver, instr.X.Type(), instr.Type()) + } + + case *ssa.MakeMap: + fr.env[instr] = fr.makeMap(instr.Type(), fr.value(instr.Reserve)) + + case *ssa.MakeSlice: + length := fr.value(instr.Len) + capacity := fr.value(instr.Cap) + fr.env[instr] = fr.makeSlice(instr.Type(), length, capacity) + + case *ssa.MapUpdate: + m := fr.value(instr.Map) + k := fr.value(instr.Key) + v := fr.value(instr.Value) + fr.mapUpdate(m, k, v) + + case *ssa.Next: + iter := fr.tuples[instr.Iter] + if instr.IsString { + fr.tuples[instr] = fr.stringIterNext(iter) + } else { + fr.tuples[instr] = fr.mapIterNext(iter) + } + + case *ssa.Panic: + arg := fr.value(instr.X) + fr.callPanic(arg) + + case *ssa.Phi: + typ := instr.Type() + phi := fr.builder.CreatePHI(fr.llvmtypes.ToLLVM(typ), instr.Comment) + fr.env[instr] = newValue(phi, typ) + fr.phis = append(fr.phis, pendingPhi{instr, phi}) + + case *ssa.Range: + x := fr.value(instr.X) + switch x.Type().Underlying().(type) { + case *types.Map: + fr.tuples[instr] = fr.mapIterInit(x) + case *types.Basic: // string + fr.tuples[instr] = fr.stringIterInit(x) + default: + panic(fmt.Sprintf("unhandled range for type %T", x.Type())) + } + + case *ssa.Return: + vals := make([]llvm.Value, len(instr.Results)) + for i, res := range instr.Results { + vals[i] = fr.llvmvalue(res) + } + fr.retInf.encode(llvm.GlobalContext(), fr.allocaBuilder, fr.builder, vals) + + case *ssa.RunDefers: + fr.runDefers() + + case *ssa.Select: + states := make([]selectState, len(instr.States)) + for i, state := range instr.States { + states[i] = selectState{ + Dir: state.Dir, + Chan: fr.value(state.Chan), + Send: fr.value(state.Send), + } + } + index, recvOk, recvElems := fr.chanSelect(states, instr.Blocking) + tuple := append([]*govalue{index, recvOk}, recvElems...) + fr.tuples[instr] = tuple + + case *ssa.Send: + fr.chanSend(fr.value(instr.Chan), fr.value(instr.X)) + + case *ssa.Slice: + x := fr.llvmvalue(instr.X) + low := fr.llvmvalue(instr.Low) + high := fr.llvmvalue(instr.High) + max := fr.llvmvalue(instr.Max) + slice := fr.slice(x, instr.X.Type(), low, high, max) + fr.env[instr] = newValue(slice, instr.Type()) + + case *ssa.Store: + addr := fr.llvmvalue(instr.Addr) + value := fr.llvmvalue(instr.Val) + addr = fr.builder.CreateBitCast(addr, llvm.PointerType(value.Type(), 0), "") + // If this is the init function, see if we can simulate the effect + // of the store in a global's initializer, in which case we can avoid + // generating code for it. + if !fr.isInit || !fr.maybeStoreInInitializer(value, addr) { + fr.nilCheck(instr.Addr, addr) + fr.builder.CreateStore(value, addr) + } + + case *ssa.TypeAssert: + x := fr.value(instr.X) + if instr.CommaOk { + v, ok := fr.interfaceTypeCheck(x, instr.AssertedType) + fr.tuples[instr] = []*govalue{v, ok} + } else { + fr.env[instr] = fr.interfaceTypeAssert(x, instr.AssertedType) + } + + case *ssa.UnOp: + operand := fr.value(instr.X) + switch instr.Op { + case token.ARROW: + x, ok := fr.chanRecv(operand, instr.CommaOk) + if instr.CommaOk { + fr.tuples[instr] = []*govalue{x, ok} + } else { + fr.env[instr] = x + } + case token.MUL: + fr.nilCheck(instr.X, operand.value) + if !fr.canAvoidLoad(instr, operand.value) { + // The bitcast is necessary to handle recursive pointer loads. + llptr := fr.builder.CreateBitCast(operand.value, llvm.PointerType(fr.llvmtypes.ToLLVM(instr.Type()), 0), "") + fr.env[instr] = newValue(fr.builder.CreateLoad(llptr, ""), instr.Type()) + } + default: + fr.env[instr] = fr.unaryOp(operand, instr.Op) + } + + default: + panic(fmt.Sprintf("unhandled: %v", instr)) + } +} + +func (fr *frame) callBuiltin(typ types.Type, builtin *ssa.Builtin, args []ssa.Value) []*govalue { + switch builtin.Name() { + case "print", "println": + llargs := make([]*govalue, len(args)) + for i, arg := range args { + llargs[i] = fr.value(arg) + } + fr.printValues(builtin.Name() == "println", llargs...) + return nil + + case "panic": + fr.callPanic(fr.value(args[0])) + return nil + + case "recover": + return []*govalue{fr.callRecover(false)} + + case "append": + return []*govalue{fr.callAppend(fr.value(args[0]), fr.value(args[1]))} + + case "close": + fr.chanClose(fr.value(args[0])) + return nil + + case "cap": + return []*govalue{fr.callCap(fr.value(args[0]))} + + case "len": + return []*govalue{fr.callLen(fr.value(args[0]))} + + case "copy": + return []*govalue{fr.callCopy(fr.value(args[0]), fr.value(args[1]))} + + case "delete": + fr.mapDelete(fr.value(args[0]), fr.value(args[1])) + return nil + + case "real": + return []*govalue{fr.extractRealValue(fr.value(args[0]))} + + case "imag": + return []*govalue{fr.extractImagValue(fr.value(args[0]))} + + case "complex": + r := fr.llvmvalue(args[0]) + i := fr.llvmvalue(args[1]) + cmplx := llvm.Undef(fr.llvmtypes.ToLLVM(typ)) + cmplx = fr.builder.CreateInsertValue(cmplx, r, 0, "") + cmplx = fr.builder.CreateInsertValue(cmplx, i, 1, "") + return []*govalue{newValue(cmplx, typ)} + + case "ssa:wrapnilchk": + ptr := fr.value(args[0]) + fr.nilCheck(args[0], ptr.value) + return []*govalue{ptr} + + default: + panic("unimplemented: " + builtin.Name()) + } +} + +// callInstruction translates function call instructions. +func (fr *frame) callInstruction(instr ssa.CallInstruction) []*govalue { + call := instr.Common() + if builtin, ok := call.Value.(*ssa.Builtin); ok { + var typ types.Type + if v := instr.Value(); v != nil { + typ = v.Type() + } + return fr.callBuiltin(typ, builtin, call.Args) + } + + args := make([]*govalue, len(call.Args)) + for i, arg := range call.Args { + args[i] = fr.value(arg) + } + + var fn *govalue + if call.IsInvoke() { + var recv *govalue + fn, recv = fr.interfaceMethod(fr.llvmvalue(call.Value), call.Value.Type(), call.Method) + args = append([]*govalue{recv}, args...) + } else { + if ssafn, ok := call.Value.(*ssa.Function); ok { + llfn := fr.resolveFunctionGlobal(ssafn) + llfn = llvm.ConstBitCast(llfn, llvm.PointerType(llvm.Int8Type(), 0)) + fn = newValue(llfn, ssafn.Type()) + } else { + // First-class function values are stored as *{*fnptr}, so + // we must extract the function pointer. We must also + // call __go_set_closure, in case the function is a closure. + fn = fr.value(call.Value) + fr.runtime.setClosure.call(fr, fn.value) + fnptr := fr.builder.CreateBitCast(fn.value, llvm.PointerType(fn.value.Type(), 0), "") + fnptr = fr.builder.CreateLoad(fnptr, "") + fn = newValue(fnptr, fn.Type()) + } + if recv := call.Signature().Recv(); recv != nil { + if _, ok := recv.Type().Underlying().(*types.Pointer); !ok { + recvalloca := fr.allocaBuilder.CreateAlloca(args[0].value.Type(), "") + fr.builder.CreateStore(args[0].value, recvalloca) + args[0] = newValue(recvalloca, types.NewPointer(args[0].Type())) + } + } + } + return fr.createCall(fn, args) +} + +func hasDefer(f *ssa.Function) bool { + for _, b := range f.Blocks { + for _, instr := range b.Instrs { + if _, ok := instr.(*ssa.Defer); ok { + return true + } + } + } + return false +} + +func callsRecover(f *ssa.Function) bool { + for _, b := range f.Blocks { + for _, instr := range b.Instrs { + if instr, ok := instr.(ssa.CallInstruction); ok { + b, ok := instr.Common().Value.(*ssa.Builtin) + if ok && b.Name() == "recover" { + return true + } + } + } + } + return false +} Index: tools/llgo/irgen/strings.go =================================================================== --- /dev/null +++ tools/llgo/irgen/strings.go @@ -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 ( + "llvm.org/llgo/third_party/go.tools/go/types" + "go/token" + "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]) +} Index: tools/llgo/irgen/targets.go =================================================================== --- /dev/null +++ tools/llgo/irgen/targets.go @@ -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" +} Index: tools/llgo/irgen/typemap.go =================================================================== --- /dev/null +++ tools/llgo/irgen/typemap.go @@ -0,0 +1,2033 @@ +//===- typemap.go - type and type descriptor mapping ----------------------===// +// +// 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 mapping from go/types types to LLVM types and to +// type descriptors. +// +//===----------------------------------------------------------------------===// + +package irgen + +import ( + "bytes" + "fmt" + "sort" + "strconv" + "strings" + + "llvm.org/llgo/third_party/go.tools/go/ssa" + "llvm.org/llgo/third_party/go.tools/go/ssa/ssautil" + "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" +) + +type MethodResolver interface { + ResolveMethod(*types.Selection) *govalue +} + +// llvmTypeMap is provides a means of mapping from a types.Map +// to llgo's corresponding LLVM type representation. +type llvmTypeMap struct { + sizes *types.StdSizes + ctx llvm.Context + target llvm.TargetData + inttype llvm.Type + stringType llvm.Type + + types typeutil.Map +} + +type typeDescInfo struct { + global llvm.Value + commonTypePtr llvm.Value + mapDescPtr llvm.Value + gc, gcPtr llvm.Value + + interfaceMethodTables typeutil.Map +} + +type TypeMap struct { + *llvmTypeMap + mc manglerContext + + module llvm.Module + pkgpath string + types, algs typeutil.Map + runtime *runtimeInterface + methodResolver MethodResolver + types.MethodSetCache + + commonTypeType, uncommonTypeType, ptrTypeType, funcTypeType, arrayTypeType, sliceTypeType, mapTypeType, chanTypeType, interfaceTypeType, structTypeType llvm.Type + mapDescType llvm.Type + + methodType, imethodType, structFieldType llvm.Type + + typeSliceType, methodSliceType, imethodSliceType, structFieldSliceType llvm.Type + + hashFnType, equalFnType llvm.Type + + hashFnEmptyInterface, hashFnInterface, hashFnFloat, hashFnComplex, hashFnString, hashFnIdentity, hashFnError llvm.Value + equalFnEmptyInterface, equalFnInterface, equalFnFloat, equalFnComplex, equalFnString, equalFnIdentity, equalFnError llvm.Value + + zeroType llvm.Type + zeroValue llvm.Value +} + +func NewLLVMTypeMap(ctx llvm.Context, target llvm.TargetData) *llvmTypeMap { + // spec says int is either 32-bit or 64-bit. + // ABI currently requires sizeof(int) == sizeof(uint) == sizeof(uintptr). + inttype := ctx.IntType(8 * target.PointerSize()) + + i8ptr := llvm.PointerType(llvm.Int8Type(), 0) + elements := []llvm.Type{i8ptr, inttype} + stringType := llvm.StructType(elements, false) + + return &llvmTypeMap{ + ctx: ctx, + sizes: &types.StdSizes{ + WordSize: int64(target.PointerSize()), + MaxAlign: 8, + }, + target: target, + inttype: inttype, + stringType: stringType, + } +} + +func NewTypeMap(pkg *ssa.Package, llvmtm *llvmTypeMap, module llvm.Module, r *runtimeInterface, mr MethodResolver) *TypeMap { + tm := &TypeMap{ + llvmTypeMap: llvmtm, + module: module, + pkgpath: pkg.Object.Path(), + runtime: r, + methodResolver: mr, + } + + tm.mc.init(pkg.Prog, &tm.MethodSetCache) + + uintptrType := tm.inttype + voidPtrType := llvm.PointerType(tm.ctx.Int8Type(), 0) + boolType := llvm.Int8Type() + stringPtrType := llvm.PointerType(tm.stringType, 0) + + // Create runtime algorithm function types. + params := []llvm.Type{voidPtrType, uintptrType} + tm.hashFnType = llvm.FunctionType(uintptrType, params, false) + params = []llvm.Type{voidPtrType, voidPtrType, uintptrType} + tm.equalFnType = llvm.FunctionType(boolType, params, false) + + tm.hashFnEmptyInterface = llvm.AddFunction(tm.module, "__go_type_hash_empty_interface", tm.hashFnType) + tm.hashFnInterface = llvm.AddFunction(tm.module, "__go_type_hash_interface", tm.hashFnType) + tm.hashFnFloat = llvm.AddFunction(tm.module, "__go_type_hash_float", tm.hashFnType) + tm.hashFnComplex = llvm.AddFunction(tm.module, "__go_type_hash_complex", tm.hashFnType) + tm.hashFnString = llvm.AddFunction(tm.module, "__go_type_hash_string", tm.hashFnType) + tm.hashFnIdentity = llvm.AddFunction(tm.module, "__go_type_hash_identity", tm.hashFnType) + tm.hashFnError = llvm.AddFunction(tm.module, "__go_type_hash_error", tm.hashFnType) + + tm.equalFnEmptyInterface = llvm.AddFunction(tm.module, "__go_type_equal_empty_interface", tm.equalFnType) + tm.equalFnInterface = llvm.AddFunction(tm.module, "__go_type_equal_interface", tm.equalFnType) + tm.equalFnFloat = llvm.AddFunction(tm.module, "__go_type_equal_float", tm.equalFnType) + tm.equalFnComplex = llvm.AddFunction(tm.module, "__go_type_equal_complex", tm.equalFnType) + tm.equalFnString = llvm.AddFunction(tm.module, "__go_type_equal_string", tm.equalFnType) + tm.equalFnIdentity = llvm.AddFunction(tm.module, "__go_type_equal_identity", tm.equalFnType) + tm.equalFnError = llvm.AddFunction(tm.module, "__go_type_equal_error", tm.equalFnType) + + // The body of this type is set in emitTypeDescInitializers once we have scanned + // every type, as it needs to be as large and well aligned as the + // largest/most aligned type. + tm.zeroType = tm.ctx.StructCreateNamed("zero") + tm.zeroValue = llvm.AddGlobal(tm.module, tm.zeroType, "go$zerovalue") + tm.zeroValue.SetLinkage(llvm.CommonLinkage) + tm.zeroValue.SetInitializer(llvm.ConstNull(tm.zeroType)) + + tm.commonTypeType = tm.ctx.StructCreateNamed("commonType") + commonTypeTypePtr := llvm.PointerType(tm.commonTypeType, 0) + + tm.methodType = tm.ctx.StructCreateNamed("method") + tm.methodType.StructSetBody([]llvm.Type{ + stringPtrType, // name + stringPtrType, // pkgPath + commonTypeTypePtr, // mtype (without receiver) + commonTypeTypePtr, // type (with receiver) + voidPtrType, // function + }, false) + + tm.methodSliceType = tm.makeNamedSliceType("methodSlice", tm.methodType) + + tm.uncommonTypeType = tm.ctx.StructCreateNamed("uncommonType") + tm.uncommonTypeType.StructSetBody([]llvm.Type{ + stringPtrType, // name + stringPtrType, // pkgPath + tm.methodSliceType, // methods + }, false) + + tm.commonTypeType.StructSetBody([]llvm.Type{ + tm.ctx.Int8Type(), // Kind + tm.ctx.Int8Type(), // align + tm.ctx.Int8Type(), // fieldAlign + uintptrType, // size + tm.ctx.Int32Type(), // hash + llvm.PointerType(tm.hashFnType, 0), // hashfn + llvm.PointerType(tm.equalFnType, 0), // equalfn + voidPtrType, // gc + stringPtrType, // string + llvm.PointerType(tm.uncommonTypeType, 0), // uncommonType + commonTypeTypePtr, // ptrToThis + llvm.PointerType(tm.zeroType, 0), // zero + }, false) + + tm.typeSliceType = tm.makeNamedSliceType("typeSlice", commonTypeTypePtr) + + tm.ptrTypeType = tm.ctx.StructCreateNamed("ptrType") + tm.ptrTypeType.StructSetBody([]llvm.Type{ + tm.commonTypeType, + commonTypeTypePtr, + }, false) + + tm.funcTypeType = tm.ctx.StructCreateNamed("funcType") + tm.funcTypeType.StructSetBody([]llvm.Type{ + tm.commonTypeType, + tm.ctx.Int8Type(), // dotdotdot + tm.typeSliceType, // in + tm.typeSliceType, // out + }, false) + + tm.arrayTypeType = tm.ctx.StructCreateNamed("arrayType") + tm.arrayTypeType.StructSetBody([]llvm.Type{ + tm.commonTypeType, + commonTypeTypePtr, // elem + commonTypeTypePtr, // slice + tm.inttype, // len + }, false) + + tm.sliceTypeType = tm.ctx.StructCreateNamed("sliceType") + tm.sliceTypeType.StructSetBody([]llvm.Type{ + tm.commonTypeType, + commonTypeTypePtr, // elem + }, false) + + tm.mapTypeType = tm.ctx.StructCreateNamed("mapType") + tm.mapTypeType.StructSetBody([]llvm.Type{ + tm.commonTypeType, + commonTypeTypePtr, // key + commonTypeTypePtr, // elem + }, false) + + tm.chanTypeType = tm.ctx.StructCreateNamed("chanType") + tm.chanTypeType.StructSetBody([]llvm.Type{ + tm.commonTypeType, + commonTypeTypePtr, // elem + tm.inttype, // dir + }, false) + + tm.imethodType = tm.ctx.StructCreateNamed("imethod") + tm.imethodType.StructSetBody([]llvm.Type{ + stringPtrType, // name + stringPtrType, // pkgPath + commonTypeTypePtr, // typ + }, false) + + tm.imethodSliceType = tm.makeNamedSliceType("imethodSlice", tm.imethodType) + + tm.interfaceTypeType = tm.ctx.StructCreateNamed("interfaceType") + tm.interfaceTypeType.StructSetBody([]llvm.Type{ + tm.commonTypeType, + tm.imethodSliceType, + }, false) + + tm.structFieldType = tm.ctx.StructCreateNamed("structField") + tm.structFieldType.StructSetBody([]llvm.Type{ + stringPtrType, // name + stringPtrType, // pkgPath + commonTypeTypePtr, // typ + stringPtrType, // tag + tm.inttype, // offset + }, false) + + tm.structFieldSliceType = tm.makeNamedSliceType("structFieldSlice", tm.structFieldType) + + tm.structTypeType = tm.ctx.StructCreateNamed("structType") + tm.structTypeType.StructSetBody([]llvm.Type{ + tm.commonTypeType, + tm.structFieldSliceType, // fields + }, false) + + tm.mapDescType = tm.ctx.StructCreateNamed("mapDesc") + tm.mapDescType.StructSetBody([]llvm.Type{ + commonTypeTypePtr, // map_descriptor + tm.inttype, // entry_size + tm.inttype, // key_offset + tm.inttype, // value_offset + }, false) + + return tm +} + +func (tm *llvmTypeMap) ToLLVM(t types.Type) llvm.Type { + return tm.toLLVM(t, "") +} + +func (tm *llvmTypeMap) toLLVM(t types.Type, name string) llvm.Type { + lt, ok := tm.types.At(t).(llvm.Type) + if !ok { + lt = tm.makeLLVMType(t, name) + if lt.IsNil() { + panic(fmt.Sprint("Failed to create LLVM type for: ", t)) + } + tm.types.Set(t, lt) + } + return lt +} + +func (tm *llvmTypeMap) makeLLVMType(t types.Type, name string) llvm.Type { + return tm.getBackendType(t).ToLLVM(tm.ctx) +} + +func (tm *llvmTypeMap) Offsetsof(fields []*types.Var) []int64 { + offsets := make([]int64, len(fields)) + var o int64 + for i, f := range fields { + a := tm.Alignof(f.Type()) + o = align(o, a) + offsets[i] = o + o += tm.Sizeof(f.Type()) + } + return offsets +} + +var basicSizes = [...]byte{ + types.Bool: 1, + types.Int8: 1, + types.Int16: 2, + types.Int32: 4, + types.Int64: 8, + types.Uint8: 1, + types.Uint16: 2, + types.Uint32: 4, + types.Uint64: 8, + types.Float32: 4, + types.Float64: 8, + types.Complex64: 8, + types.Complex128: 16, +} + +func (tm *llvmTypeMap) Sizeof(T types.Type) int64 { + switch t := T.Underlying().(type) { + case *types.Basic: + k := t.Kind() + if int(k) < len(basicSizes) { + if s := basicSizes[k]; s > 0 { + return int64(s) + } + } + if k == types.String { + return tm.sizes.WordSize * 2 + } + case *types.Array: + a := tm.Alignof(t.Elem()) + z := tm.Sizeof(t.Elem()) + return align(z, a) * t.Len() // may be 0 + case *types.Slice: + return tm.sizes.WordSize * 3 + case *types.Struct: + n := t.NumFields() + if n == 0 { + return 0 + } + fields := make([]*types.Var, t.NumFields()) + for i := 0; i != t.NumFields(); i++ { + fields[i] = t.Field(i) + } + offsets := tm.Offsetsof(fields) + return align(offsets[n-1]+tm.Sizeof(t.Field(n-1).Type()), tm.Alignof(t)) + case *types.Interface: + return tm.sizes.WordSize * 2 + } + return tm.sizes.WordSize // catch-all +} + +func (tm *llvmTypeMap) Alignof(t types.Type) int64 { + return tm.sizes.Alignof(t) +} + +/////////////////////////////////////////////////////////////////////////////// + +func (tm *TypeMap) ToRuntime(t types.Type) llvm.Value { + return llvm.ConstBitCast(tm.getTypeDescriptorPointer(t), llvm.PointerType(llvm.Int8Type(), 0)) +} + +type localNamedTypeInfo struct { + functionName string + scopeNum int +} + +type namedTypeInfo struct { + pkgname, pkgpath string + name string + localNamedTypeInfo +} + +type manglerContext struct { + ti map[*types.Named]localNamedTypeInfo + msc *types.MethodSetCache +} + +// Assembles the method set into the order that gccgo uses (unexported methods first). +// TODO(pcc): cache this. +func orderedMethodSet(ms *types.MethodSet) []*types.Selection { + oms := make([]*types.Selection, ms.Len()) + omsi := 0 + for i := 0; i != ms.Len(); i++ { + if sel := ms.At(i); !sel.Obj().Exported() { + oms[omsi] = sel + omsi++ + } + } + for i := 0; i != ms.Len(); i++ { + if sel := ms.At(i); sel.Obj().Exported() { + oms[omsi] = sel + omsi++ + } + } + return oms +} + +func (ctx *manglerContext) init(prog *ssa.Program, msc *types.MethodSetCache) { + ctx.msc = msc + ctx.ti = make(map[*types.Named]localNamedTypeInfo) + for f, _ := range ssautil.AllFunctions(prog) { + scopeNum := 0 + var addNamedTypesToMap func(*types.Scope) + addNamedTypesToMap = func(scope *types.Scope) { + hasNamedTypes := false + for _, n := range scope.Names() { + if tn, ok := scope.Lookup(n).(*types.TypeName); ok { + hasNamedTypes = true + ctx.ti[tn.Type().(*types.Named)] = localNamedTypeInfo{f.Name(), scopeNum} + } + } + if hasNamedTypes { + scopeNum++ + } + for i := 0; i != scope.NumChildren(); i++ { + addNamedTypesToMap(scope.Child(i)) + } + } + if fobj, ok := f.Object().(*types.Func); ok && fobj.Scope() != nil { + addNamedTypesToMap(fobj.Scope()) + } + } +} + +func (ctx *manglerContext) getNamedTypeInfo(t types.Type) (nti namedTypeInfo) { + switch t := t.(type) { + case *types.Basic: + switch t.Kind() { + case types.Byte: + nti.name = "uint8" + case types.Rune: + nti.name = "int32" + case types.UnsafePointer: + nti.pkgname = "unsafe" + nti.pkgpath = "unsafe" + nti.name = "Pointer" + default: + nti.name = t.Name() + } + + case *types.Named: + obj := t.Obj() + if pkg := obj.Pkg(); pkg != nil { + nti.pkgname = obj.Pkg().Name() + nti.pkgpath = obj.Pkg().Path() + } + nti.name = obj.Name() + nti.localNamedTypeInfo = ctx.ti[t] + + default: + panic("not a named type") + } + + return +} + +func (ctx *manglerContext) mangleSignature(s *types.Signature, recv *types.Var, b *bytes.Buffer) { + b.WriteRune('F') + if recv != nil { + b.WriteRune('m') + ctx.mangleType(recv.Type(), b) + } + + if p := s.Params(); p.Len() != 0 { + b.WriteRune('p') + for i := 0; i != p.Len(); i++ { + ctx.mangleType(p.At(i).Type(), b) + } + if s.Variadic() { + b.WriteRune('V') + } + b.WriteRune('e') + } + + if r := s.Results(); r.Len() != 0 { + b.WriteRune('r') + for i := 0; i != r.Len(); i++ { + ctx.mangleType(r.At(i).Type(), b) + } + b.WriteRune('e') + } + + b.WriteRune('e') +} + +func (ctx *manglerContext) manglePackagePath(pkgpath string, b *bytes.Buffer) { + pkgpath = strings.Replace(pkgpath, "/", "_", -1) + pkgpath = strings.Replace(pkgpath, ".", "_", -1) + b.WriteString(pkgpath) +} + +func (ctx *manglerContext) mangleType(t types.Type, b *bytes.Buffer) { + switch t := t.(type) { + case *types.Basic, *types.Named: + var nb bytes.Buffer + ti := ctx.getNamedTypeInfo(t) + if ti.pkgpath != "" { + ctx.manglePackagePath(ti.pkgpath, &nb) + nb.WriteRune('.') + } + if ti.functionName != "" { + nb.WriteString(ti.functionName) + nb.WriteRune('$') + if ti.scopeNum != 0 { + nb.WriteString(strconv.Itoa(ti.scopeNum)) + nb.WriteRune('$') + } + } + nb.WriteString(ti.name) + + b.WriteRune('N') + b.WriteString(strconv.Itoa(nb.Len())) + b.WriteRune('_') + b.WriteString(nb.String()) + + case *types.Pointer: + b.WriteRune('p') + ctx.mangleType(t.Elem(), b) + + case *types.Map: + b.WriteRune('M') + ctx.mangleType(t.Key(), b) + b.WriteString("__") + ctx.mangleType(t.Elem(), b) + + case *types.Chan: + b.WriteRune('C') + ctx.mangleType(t.Elem(), b) + switch t.Dir() { + case types.SendOnly: + b.WriteRune('s') + case types.RecvOnly: + b.WriteRune('r') + case types.SendRecv: + b.WriteString("sr") + } + b.WriteRune('e') + + case *types.Signature: + ctx.mangleSignature(t, t.Recv(), b) + + case *types.Array: + b.WriteRune('A') + ctx.mangleType(t.Elem(), b) + b.WriteString(strconv.FormatInt(t.Len(), 10)) + b.WriteRune('e') + + case *types.Slice: + b.WriteRune('A') + ctx.mangleType(t.Elem(), b) + b.WriteRune('e') + + case *types.Struct: + b.WriteRune('S') + for i := 0; i != t.NumFields(); i++ { + f := t.Field(i) + if f.Anonymous() { + b.WriteString("0_") + } else { + b.WriteString(strconv.Itoa(len(f.Name()))) + b.WriteRune('_') + b.WriteString(f.Name()) + } + ctx.mangleType(f.Type(), b) + // TODO: tags are mangled here + } + b.WriteRune('e') + + case *types.Interface: + b.WriteRune('I') + methodset := ctx.msc.MethodSet(t) + for _, m := range orderedMethodSet(methodset) { + method := m.Obj() + var nb bytes.Buffer + if !method.Exported() { + nb.WriteRune('.') + nb.WriteString(method.Pkg().Path()) + nb.WriteRune('.') + } + nb.WriteString(method.Name()) + + b.WriteString(strconv.Itoa(nb.Len())) + b.WriteRune('_') + b.WriteString(nb.String()) + + ctx.mangleSignature(method.Type().(*types.Signature), nil, b) + } + b.WriteRune('e') + + default: + panic(fmt.Sprintf("unhandled type: %#v", t)) + } +} + +func (ctx *manglerContext) mangleTypeDescriptorName(t types.Type, b *bytes.Buffer) { + switch t := t.(type) { + case *types.Basic, *types.Named: + b.WriteString("__go_tdn_") + ti := ctx.getNamedTypeInfo(t) + if ti.pkgpath != "" { + ctx.manglePackagePath(ti.pkgpath, b) + b.WriteRune('.') + } + if ti.functionName != "" { + b.WriteString(ti.functionName) + b.WriteRune('.') + if ti.scopeNum != 0 { + b.WriteString(strconv.Itoa(ti.scopeNum)) + b.WriteRune('.') + } + } + b.WriteString(ti.name) + + default: + b.WriteString("__go_td_") + ctx.mangleType(t, b) + } +} + +func (ctx *manglerContext) mangleMapDescriptorName(t types.Type, b *bytes.Buffer) { + b.WriteString("__go_map_") + ctx.mangleType(t, b) +} + +func (ctx *manglerContext) mangleImtName(srctype types.Type, targettype *types.Interface, b *bytes.Buffer) { + b.WriteString("__go_imt_") + ctx.mangleType(targettype, b) + b.WriteString("__") + ctx.mangleType(srctype, b) +} + +func (ctx *manglerContext) mangleHashFunctionName(t types.Type) string { + var b bytes.Buffer + b.WriteString("__go_type_hash_") + ctx.mangleType(t, &b) + return b.String() +} + +func (ctx *manglerContext) mangleEqualFunctionName(t types.Type) string { + var b bytes.Buffer + b.WriteString("__go_type_equal_") + ctx.mangleType(t, &b) + return b.String() +} + +func (ctx *manglerContext) mangleFunctionName(f *ssa.Function) string { + var b bytes.Buffer + + if f.Parent() != nil { + // Anonymous functions are not guaranteed to + // have unique identifiers at the global scope. + b.WriteString(ctx.mangleFunctionName(f.Parent())) + b.WriteRune(':') + b.WriteString(f.String()) + return b.String() + } + + pkg := f.Pkg + var pkgobj *types.Package + if pkg != nil { + pkgobj = pkg.Object + } else if f.Signature.Recv() != nil { + pkgobj = f.Signature.Recv().Pkg() + } else { + b.WriteString(f.String()) + return b.String() + } + + if pkg != nil { + ctx.manglePackagePath(pkgobj.Path(), &b) + b.WriteRune('.') + } + if f.Signature.Recv() == nil && f.Name() == "init" { + b.WriteString(".import") + } else { + b.WriteString(f.Name()) + } + if f.Signature.Recv() != nil { + b.WriteRune('.') + ctx.mangleType(f.Signature.Recv().Type(), &b) + } + + return b.String() +} + +func (ctx *manglerContext) mangleGlobalName(g *ssa.Global) string { + var b bytes.Buffer + + ctx.manglePackagePath(g.Pkg.Object.Path(), &b) + b.WriteRune('.') + b.WriteString(g.Name()) + + return b.String() +} + +const ( + // From gofrontend/types.h + gccgoTypeClassERROR = iota + gccgoTypeClassVOID + gccgoTypeClassBOOLEAN + gccgoTypeClassINTEGER + gccgoTypeClassFLOAT + gccgoTypeClassCOMPLEX + gccgoTypeClassSTRING + gccgoTypeClassSINK + gccgoTypeClassFUNCTION + gccgoTypeClassPOINTER + gccgoTypeClassNIL + gccgoTypeClassCALL_MULTIPLE_RESULT + gccgoTypeClassSTRUCT + gccgoTypeClassARRAY + gccgoTypeClassMAP + gccgoTypeClassCHANNEL + gccgoTypeClassINTERFACE + gccgoTypeClassNAMED + gccgoTypeClassFORWARD +) + +func getStringHash(s string, h uint32) uint32 { + for _, c := range []byte(s) { + h ^= uint32(c) + h += 16777619 + } + return h +} + +func (tm *TypeMap) getTypeHash(t types.Type) uint32 { + switch t := t.(type) { + case *types.Basic, *types.Named: + nti := tm.mc.getNamedTypeInfo(t) + h := getStringHash(nti.functionName+nti.name+nti.pkgpath, 0) + h ^= uint32(nti.scopeNum) + return gccgoTypeClassNAMED + h + + case *types.Signature: + var h uint32 + + p := t.Params() + for i := 0; i != p.Len(); i++ { + h += tm.getTypeHash(p.At(i).Type()) << uint32(i+1) + } + + r := t.Results() + for i := 0; i != r.Len(); i++ { + h += tm.getTypeHash(r.At(i).Type()) << uint32(i+2) + } + + if t.Variadic() { + h += 1 + } + h <<= 4 + return gccgoTypeClassFUNCTION + h + + case *types.Pointer: + return gccgoTypeClassPOINTER + (tm.getTypeHash(t.Elem()) << 4) + + case *types.Struct: + var h uint32 + for i := 0; i != t.NumFields(); i++ { + h = (h << 1) + tm.getTypeHash(t.Field(i).Type()) + } + h <<= 2 + return gccgoTypeClassSTRUCT + h + + case *types.Array: + return gccgoTypeClassARRAY + tm.getTypeHash(t.Elem()) + 1 + + case *types.Slice: + return gccgoTypeClassARRAY + tm.getTypeHash(t.Elem()) + 1 + + case *types.Map: + return gccgoTypeClassMAP + tm.getTypeHash(t.Key()) + tm.getTypeHash(t.Elem()) + 2 + + case *types.Chan: + var h uint32 + + switch t.Dir() { + case types.SendOnly: + h = 1 + case types.RecvOnly: + h = 2 + case types.SendRecv: + h = 3 + } + + h += tm.getTypeHash(t.Elem()) << 2 + h <<= 3 + return gccgoTypeClassCHANNEL + h + + case *types.Interface: + var h uint32 + for _, m := range orderedMethodSet(tm.MethodSet(t)) { + h = getStringHash(m.Obj().Name(), h) + h <<= 1 + } + return gccgoTypeClassINTERFACE + h + + default: + panic(fmt.Sprintf("unhandled type: %#v", t)) + } +} + +func (tm *TypeMap) writeType(typ types.Type, b *bytes.Buffer) { + switch t := typ.(type) { + case *types.Basic, *types.Named: + ti := tm.mc.getNamedTypeInfo(t) + if ti.pkgpath != "" { + b.WriteByte('\t') + tm.mc.manglePackagePath(ti.pkgpath, b) + b.WriteByte('\t') + b.WriteString(ti.pkgname) + b.WriteByte('.') + } + if ti.functionName != "" { + b.WriteByte('\t') + b.WriteString(ti.functionName) + b.WriteByte('$') + if ti.scopeNum != 0 { + b.WriteString(strconv.Itoa(ti.scopeNum)) + b.WriteByte('$') + } + b.WriteByte('\t') + } + b.WriteString(ti.name) + + case *types.Array: + fmt.Fprintf(b, "[%d]", t.Len()) + tm.writeType(t.Elem(), b) + + case *types.Slice: + b.WriteString("[]") + tm.writeType(t.Elem(), b) + + case *types.Struct: + if t.NumFields() == 0 { + b.WriteString("struct {}") + return + } + b.WriteString("struct { ") + for i := 0; i != t.NumFields(); i++ { + f := t.Field(i) + if i > 0 { + b.WriteString("; ") + } + if !f.Anonymous() { + b.WriteString(f.Name()) + b.WriteByte(' ') + } + tm.writeType(f.Type(), b) + if tag := t.Tag(i); tag != "" { + fmt.Fprintf(b, " %q", tag) + } + } + b.WriteString(" }") + + case *types.Pointer: + b.WriteByte('*') + tm.writeType(t.Elem(), b) + + case *types.Signature: + b.WriteString("func") + tm.writeSignature(t, b) + + case *types.Interface: + if t.NumMethods() == 0 && t.NumEmbeddeds() == 0 { + b.WriteString("interface {}") + return + } + // We write the source-level methods and embedded types rather + // than the actual method set since resolved method signatures + // may have non-printable cycles if parameters have anonymous + // interface types that (directly or indirectly) embed the + // current interface. For instance, consider the result type + // of m: + // + // type T interface{ + // m() interface{ T } + // } + // + b.WriteString("interface { ") + // print explicit interface methods and embedded types + for i := 0; i != t.NumMethods(); i++ { + m := t.Method(i) + if i > 0 { + b.WriteString("; ") + } + if !m.Exported() { + b.WriteString(m.Pkg().Path()) + b.WriteByte('.') + } + b.WriteString(m.Name()) + tm.writeSignature(m.Type().(*types.Signature), b) + } + for i := 0; i != t.NumEmbeddeds(); i++ { + typ := t.Embedded(i) + if i > 0 || t.NumMethods() > 0 { + b.WriteString("; ") + } + tm.writeType(typ, b) + } + b.WriteString(" }") + + case *types.Map: + b.WriteString("map[") + tm.writeType(t.Key(), b) + b.WriteByte(']') + tm.writeType(t.Elem(), b) + + case *types.Chan: + var s string + var parens bool + switch t.Dir() { + case types.SendRecv: + s = "chan " + // chan (<-chan T) requires parentheses + if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly { + parens = true + } + case types.SendOnly: + s = "chan<- " + case types.RecvOnly: + s = "<-chan " + default: + panic("unreachable") + } + b.WriteString(s) + if parens { + b.WriteByte('(') + } + tm.writeType(t.Elem(), b) + if parens { + b.WriteByte(')') + } + + default: + panic(fmt.Sprintf("unhandled type: %#v", t)) + } +} + +func (tm *TypeMap) writeTuple(tup *types.Tuple, variadic bool, b *bytes.Buffer) { + b.WriteByte('(') + if tup != nil { + for i := 0; i != tup.Len(); i++ { + v := tup.At(i) + if i > 0 { + b.WriteString(", ") + } + typ := v.Type() + if variadic && i == tup.Len()-1 { + b.WriteString("...") + typ = typ.(*types.Slice).Elem() + } + tm.writeType(typ, b) + } + } + b.WriteByte(')') +} + +func (tm *TypeMap) writeSignature(sig *types.Signature, b *bytes.Buffer) { + tm.writeTuple(sig.Params(), sig.Variadic(), b) + + n := sig.Results().Len() + if n == 0 { + // no result + return + } + + b.WriteByte(' ') + if n == 1 { + tm.writeType(sig.Results().At(0).Type(), b) + return + } + + // multiple results + tm.writeTuple(sig.Results(), false, b) +} + +func (tm *TypeMap) getTypeDescType(t types.Type) llvm.Type { + switch t.Underlying().(type) { + case *types.Basic: + return tm.commonTypeType + case *types.Pointer: + return tm.ptrTypeType + case *types.Signature: + return tm.funcTypeType + case *types.Array: + return tm.arrayTypeType + case *types.Slice: + return tm.sliceTypeType + case *types.Map: + return tm.mapTypeType + case *types.Chan: + return tm.chanTypeType + case *types.Struct: + return tm.structTypeType + case *types.Interface: + return tm.interfaceTypeType + default: + panic(fmt.Sprintf("unhandled type: %#v", t)) + } +} + +func (tm *TypeMap) getNamedTypeLinkage(nt *types.Named) (linkage llvm.Linkage, emit bool) { + if pkg := nt.Obj().Pkg(); pkg != nil { + linkage = llvm.ExternalLinkage + emit = pkg.Path() == tm.pkgpath + } else { + linkage = llvm.LinkOnceODRLinkage + emit = true + } + + return +} + +func (tm *TypeMap) getTypeDescLinkage(t types.Type) (linkage llvm.Linkage, emit bool) { + switch t := t.(type) { + case *types.Named: + linkage, emit = tm.getNamedTypeLinkage(t) + + case *types.Pointer: + elem := t.Elem() + if nt, ok := elem.(*types.Named); ok { + // Thanks to the ptrToThis member, pointers to named types appear + // in exactly the same objects as the named types themselves, so + // we can give them the same linkage. + linkage, emit = tm.getNamedTypeLinkage(nt) + return + } + linkage = llvm.LinkOnceODRLinkage + emit = true + + default: + linkage = llvm.LinkOnceODRLinkage + emit = true + } + + return +} + +type typeAndInfo struct { + typ types.Type + typeString string + tdi *typeDescInfo +} + +type byTypeName []typeAndInfo + +func (ts byTypeName) Len() int { return len(ts) } +func (ts byTypeName) Swap(i, j int) { + ts[i], ts[j] = ts[j], ts[i] +} +func (ts byTypeName) Less(i, j int) bool { + return ts[i].typeString < ts[j].typeString +} + +func (tm *TypeMap) emitTypeDescInitializers() { + var maxSize, maxAlign int64 + maxAlign = 1 + + for changed := true; changed; { + changed = false + + var ts []typeAndInfo + + tm.types.Iterate(func(key types.Type, value interface{}) { + tdi := value.(*typeDescInfo) + if tdi.global.Initializer().C == nil { + linkage, emit := tm.getTypeDescLinkage(key) + tdi.global.SetLinkage(linkage) + tdi.gc.SetLinkage(linkage) + if emit { + changed = true + ts = append(ts, typeAndInfo{key, key.String(), tdi}) + } + } + }) + + if changed { + sort.Sort(byTypeName(ts)) + for _, t := range ts { + tm.emitTypeDescInitializer(t.typ, t.tdi) + if size := tm.Sizeof(t.typ); size > maxSize { + maxSize = size + } + if align := tm.Alignof(t.typ); align > maxAlign { + maxAlign = align + } + } + } + } + + tm.zeroType.StructSetBody([]llvm.Type{llvm.ArrayType(tm.ctx.Int8Type(), int(maxSize))}, false) + tm.zeroValue.SetAlignment(int(maxAlign)) +} + +const ( + // From libgo/runtime/mgc0.h + gcOpcodeEND = iota + gcOpcodePTR + gcOpcodeAPTR + gcOpcodeARRAY_START + gcOpcodeARRAY_NEXT + gcOpcodeCALL + gcOpcodeCHAN_PTR + gcOpcodeSTRING + gcOpcodeEFACE + gcOpcodeIFACE + gcOpcodeSLICE + gcOpcodeREGION + + gcStackCapacity = 8 +) + +func (tm *TypeMap) makeGcInst(val int64) llvm.Value { + c := llvm.ConstInt(tm.inttype, uint64(val), false) + return llvm.ConstIntToPtr(c, llvm.PointerType(tm.ctx.Int8Type(), 0)) +} + +func (tm *TypeMap) appendGcInsts(insts []llvm.Value, t types.Type, offset, stackSize int64) []llvm.Value { + switch u := t.Underlying().(type) { + case *types.Basic: + switch u.Kind() { + case types.String: + insts = append(insts, tm.makeGcInst(gcOpcodeSTRING), tm.makeGcInst(offset)) + case types.UnsafePointer: + insts = append(insts, tm.makeGcInst(gcOpcodeAPTR), tm.makeGcInst(offset)) + } + case *types.Pointer: + insts = append(insts, tm.makeGcInst(gcOpcodePTR), tm.makeGcInst(offset), + tm.getGcPointer(u.Elem())) + case *types.Signature, *types.Map: + insts = append(insts, tm.makeGcInst(gcOpcodeAPTR), tm.makeGcInst(offset)) + case *types.Array: + if u.Len() == 0 { + return insts + } else if stackSize >= gcStackCapacity { + insts = append(insts, tm.makeGcInst(gcOpcodeREGION), tm.makeGcInst(offset), + tm.makeGcInst(tm.Sizeof(t)), tm.getGcPointer(t)) + } else { + insts = append(insts, tm.makeGcInst(gcOpcodeARRAY_START), tm.makeGcInst(offset), + tm.makeGcInst(u.Len()), tm.makeGcInst(tm.Sizeof(u.Elem()))) + insts = tm.appendGcInsts(insts, u.Elem(), 0, stackSize+1) + insts = append(insts, tm.makeGcInst(gcOpcodeARRAY_NEXT)) + } + case *types.Slice: + if tm.Sizeof(u.Elem()) == 0 { + insts = append(insts, tm.makeGcInst(gcOpcodeAPTR), tm.makeGcInst(offset)) + } else { + insts = append(insts, tm.makeGcInst(gcOpcodeSLICE), tm.makeGcInst(offset), + tm.getGcPointer(u.Elem())) + } + case *types.Chan: + insts = append(insts, tm.makeGcInst(gcOpcodeCHAN_PTR), tm.makeGcInst(offset), + tm.ToRuntime(t)) + case *types.Struct: + fields := make([]*types.Var, u.NumFields()) + for i := range fields { + fields[i] = u.Field(i) + } + offsets := tm.Offsetsof(fields) + + for i, field := range fields { + insts = tm.appendGcInsts(insts, field.Type(), offset+offsets[i], stackSize) + } + case *types.Interface: + if u.NumMethods() == 0 { + insts = append(insts, tm.makeGcInst(gcOpcodeEFACE), tm.makeGcInst(offset)) + } else { + insts = append(insts, tm.makeGcInst(gcOpcodeIFACE), tm.makeGcInst(offset)) + } + default: + panic(fmt.Sprintf("unhandled type: %#v", t)) + } + + return insts +} + +func (tm *TypeMap) emitTypeDescInitializer(t types.Type, tdi *typeDescInfo) { + // initialize type descriptor + tdi.global.SetInitializer(tm.makeTypeDescInitializer(t)) + + // initialize GC program + insts := []llvm.Value{tm.makeGcInst(tm.Sizeof(t))} + insts = tm.appendGcInsts(insts, t, 0, 0) + insts = append(insts, tm.makeGcInst(gcOpcodeEND)) + + i8ptr := llvm.PointerType(llvm.Int8Type(), 0) + instArray := llvm.ConstArray(i8ptr, insts) + + newGc := llvm.AddGlobal(tm.module, instArray.Type(), "") + newGc.SetGlobalConstant(true) + newGc.SetInitializer(instArray) + gcName := tdi.gc.Name() + tdi.gc.SetName("") + newGc.SetName(gcName) + newGc.SetLinkage(tdi.gc.Linkage()) + + tdi.gc.ReplaceAllUsesWith(llvm.ConstBitCast(newGc, tdi.gc.Type())) + tdi.gc.EraseFromParentAsGlobal() + tdi.gc = llvm.Value{nil} + tdi.gcPtr = llvm.ConstBitCast(newGc, i8ptr) +} + +func (tm *TypeMap) makeTypeDescInitializer(t types.Type) llvm.Value { + switch u := t.Underlying().(type) { + case *types.Basic: + return tm.makeBasicType(t, u) + case *types.Pointer: + return tm.makePointerType(t, u) + case *types.Signature: + return tm.makeFuncType(t, u) + case *types.Array: + return tm.makeArrayType(t, u) + case *types.Slice: + return tm.makeSliceType(t, u) + case *types.Map: + return tm.makeMapType(t, u) + case *types.Chan: + return tm.makeChanType(t, u) + case *types.Struct: + return tm.makeStructType(t, u) + case *types.Interface: + return tm.makeInterfaceType(t, u) + default: + panic(fmt.Sprintf("unhandled type: %#v", t)) + } +} + +type algorithmFns struct { + hash, equal llvm.Value +} + +func (tm *TypeMap) getStructAlgorithmFunctions(st *types.Struct) (hash, equal llvm.Value) { + if algs, ok := tm.algs.At(st).(algorithmFns); ok { + return algs.hash, algs.equal + } + + hashes := make([]llvm.Value, st.NumFields()) + equals := make([]llvm.Value, st.NumFields()) + + for i := range hashes { + fhash, fequal := tm.getAlgorithmFunctions(st.Field(i).Type()) + if fhash == tm.hashFnError { + return fhash, fequal + } + hashes[i], equals[i] = fhash, fequal + } + + i8ptr := llvm.PointerType(tm.ctx.Int8Type(), 0) + llsptrty := llvm.PointerType(tm.ToLLVM(st), 0) + + builder := tm.ctx.NewBuilder() + defer builder.Dispose() + + hash = llvm.AddFunction(tm.module, tm.mc.mangleHashFunctionName(st), tm.hashFnType) + hash.SetLinkage(llvm.LinkOnceODRLinkage) + builder.SetInsertPointAtEnd(llvm.AddBasicBlock(hash, "entry")) + sptr := builder.CreateBitCast(hash.Param(0), llsptrty, "") + + hashval := llvm.ConstNull(tm.inttype) + i33 := llvm.ConstInt(tm.inttype, 33, false) + + for i, fhash := range hashes { + fptr := builder.CreateStructGEP(sptr, i, "") + fptr = builder.CreateBitCast(fptr, i8ptr, "") + + fsize := llvm.ConstInt(tm.inttype, uint64(tm.sizes.Sizeof(st.Field(i).Type())), false) + + hashcall := builder.CreateCall(fhash, []llvm.Value{fptr, fsize}, "") + hashval = builder.CreateMul(hashval, i33, "") + hashval = builder.CreateAdd(hashval, hashcall, "") + } + + builder.CreateRet(hashval) + + equal = llvm.AddFunction(tm.module, tm.mc.mangleEqualFunctionName(st), tm.equalFnType) + equal.SetLinkage(llvm.LinkOnceODRLinkage) + eqentrybb := llvm.AddBasicBlock(equal, "entry") + eqretzerobb := llvm.AddBasicBlock(equal, "retzero") + + builder.SetInsertPointAtEnd(eqentrybb) + s1ptr := builder.CreateBitCast(equal.Param(0), llsptrty, "") + s2ptr := builder.CreateBitCast(equal.Param(1), llsptrty, "") + + zerobool := llvm.ConstNull(tm.ctx.Int8Type()) + onebool := llvm.ConstInt(tm.ctx.Int8Type(), 1, false) + + for i, fequal := range equals { + f1ptr := builder.CreateStructGEP(s1ptr, i, "") + f1ptr = builder.CreateBitCast(f1ptr, i8ptr, "") + f2ptr := builder.CreateStructGEP(s2ptr, i, "") + f2ptr = builder.CreateBitCast(f2ptr, i8ptr, "") + + fsize := llvm.ConstInt(tm.inttype, uint64(tm.sizes.Sizeof(st.Field(i).Type())), false) + + equalcall := builder.CreateCall(fequal, []llvm.Value{f1ptr, f2ptr, fsize}, "") + equaleqzero := builder.CreateICmp(llvm.IntEQ, equalcall, zerobool, "") + + contbb := llvm.AddBasicBlock(equal, "cont") + builder.CreateCondBr(equaleqzero, eqretzerobb, contbb) + + builder.SetInsertPointAtEnd(contbb) + } + + builder.CreateRet(onebool) + + builder.SetInsertPointAtEnd(eqretzerobb) + builder.CreateRet(zerobool) + + tm.algs.Set(st, algorithmFns{hash, equal}) + return +} + +func (tm *TypeMap) getArrayAlgorithmFunctions(at *types.Array) (hash, equal llvm.Value) { + if algs, ok := tm.algs.At(at).(algorithmFns); ok { + return algs.hash, algs.equal + } + + ehash, eequal := tm.getAlgorithmFunctions(at.Elem()) + if ehash == tm.hashFnError { + return ehash, eequal + } + + i8ptr := llvm.PointerType(tm.ctx.Int8Type(), 0) + llelemty := llvm.PointerType(tm.ToLLVM(at.Elem()), 0) + + i1 := llvm.ConstInt(tm.inttype, 1, false) + alen := llvm.ConstInt(tm.inttype, uint64(at.Len()), false) + esize := llvm.ConstInt(tm.inttype, uint64(tm.sizes.Sizeof(at.Elem())), false) + + builder := tm.ctx.NewBuilder() + defer builder.Dispose() + + hash = llvm.AddFunction(tm.module, tm.mc.mangleHashFunctionName(at), tm.hashFnType) + hash.SetLinkage(llvm.LinkOnceODRLinkage) + hashentrybb := llvm.AddBasicBlock(hash, "entry") + builder.SetInsertPointAtEnd(hashentrybb) + if at.Len() == 0 { + builder.CreateRet(llvm.ConstNull(tm.inttype)) + } else { + i33 := llvm.ConstInt(tm.inttype, 33, false) + + aptr := builder.CreateBitCast(hash.Param(0), llelemty, "") + loopbb := llvm.AddBasicBlock(hash, "loop") + builder.CreateBr(loopbb) + + exitbb := llvm.AddBasicBlock(hash, "exit") + + builder.SetInsertPointAtEnd(loopbb) + indexphi := builder.CreatePHI(tm.inttype, "") + index := indexphi + hashvalphi := builder.CreatePHI(tm.inttype, "") + hashval := hashvalphi + + eptr := builder.CreateGEP(aptr, []llvm.Value{index}, "") + eptr = builder.CreateBitCast(eptr, i8ptr, "") + + hashcall := builder.CreateCall(ehash, []llvm.Value{eptr, esize}, "") + hashval = builder.CreateMul(hashval, i33, "") + hashval = builder.CreateAdd(hashval, hashcall, "") + + index = builder.CreateAdd(index, i1, "") + + indexphi.AddIncoming( + []llvm.Value{llvm.ConstNull(tm.inttype), index}, + []llvm.BasicBlock{hashentrybb, loopbb}, + ) + hashvalphi.AddIncoming( + []llvm.Value{llvm.ConstNull(tm.inttype), hashval}, + []llvm.BasicBlock{hashentrybb, loopbb}, + ) + + exit := builder.CreateICmp(llvm.IntEQ, index, alen, "") + builder.CreateCondBr(exit, exitbb, loopbb) + + builder.SetInsertPointAtEnd(exitbb) + builder.CreateRet(hashval) + } + + zerobool := llvm.ConstNull(tm.ctx.Int8Type()) + onebool := llvm.ConstInt(tm.ctx.Int8Type(), 1, false) + + equal = llvm.AddFunction(tm.module, tm.mc.mangleEqualFunctionName(at), tm.equalFnType) + equal.SetLinkage(llvm.LinkOnceODRLinkage) + eqentrybb := llvm.AddBasicBlock(equal, "entry") + builder.SetInsertPointAtEnd(eqentrybb) + if at.Len() == 0 { + builder.CreateRet(onebool) + } else { + a1ptr := builder.CreateBitCast(equal.Param(0), llelemty, "") + a2ptr := builder.CreateBitCast(equal.Param(1), llelemty, "") + loopbb := llvm.AddBasicBlock(equal, "loop") + builder.CreateBr(loopbb) + + exitbb := llvm.AddBasicBlock(equal, "exit") + retzerobb := llvm.AddBasicBlock(equal, "retzero") + + builder.SetInsertPointAtEnd(loopbb) + indexphi := builder.CreatePHI(tm.inttype, "") + index := indexphi + + e1ptr := builder.CreateGEP(a1ptr, []llvm.Value{index}, "") + e1ptr = builder.CreateBitCast(e1ptr, i8ptr, "") + e2ptr := builder.CreateGEP(a2ptr, []llvm.Value{index}, "") + e2ptr = builder.CreateBitCast(e2ptr, i8ptr, "") + + equalcall := builder.CreateCall(eequal, []llvm.Value{e1ptr, e2ptr, esize}, "") + equaleqzero := builder.CreateICmp(llvm.IntEQ, equalcall, zerobool, "") + + contbb := llvm.AddBasicBlock(equal, "cont") + builder.CreateCondBr(equaleqzero, retzerobb, contbb) + + builder.SetInsertPointAtEnd(contbb) + + index = builder.CreateAdd(index, i1, "") + + indexphi.AddIncoming( + []llvm.Value{llvm.ConstNull(tm.inttype), index}, + []llvm.BasicBlock{eqentrybb, contbb}, + ) + + exit := builder.CreateICmp(llvm.IntEQ, index, alen, "") + builder.CreateCondBr(exit, exitbb, loopbb) + + builder.SetInsertPointAtEnd(exitbb) + builder.CreateRet(onebool) + + builder.SetInsertPointAtEnd(retzerobb) + builder.CreateRet(zerobool) + } + + tm.algs.Set(at, algorithmFns{hash, equal}) + return +} + +func (tm *TypeMap) getAlgorithmFunctions(t types.Type) (hash, equal llvm.Value) { + switch t := t.Underlying().(type) { + case *types.Interface: + if t.NumMethods() == 0 { + hash = tm.hashFnEmptyInterface + equal = tm.equalFnEmptyInterface + } else { + hash = tm.hashFnInterface + equal = tm.equalFnInterface + } + case *types.Basic: + switch t.Kind() { + case types.Float32, types.Float64: + hash = tm.hashFnFloat + equal = tm.equalFnFloat + case types.Complex64, types.Complex128: + hash = tm.hashFnComplex + equal = tm.equalFnComplex + case types.String: + hash = tm.hashFnString + equal = tm.equalFnString + default: + hash = tm.hashFnIdentity + equal = tm.equalFnIdentity + } + case *types.Signature, *types.Map, *types.Slice: + hash = tm.hashFnError + equal = tm.equalFnError + case *types.Struct: + hash, equal = tm.getStructAlgorithmFunctions(t) + case *types.Array: + hash, equal = tm.getArrayAlgorithmFunctions(t) + default: + hash = tm.hashFnIdentity + equal = tm.equalFnIdentity + } + + return +} + +func (tm *TypeMap) getTypeDescInfo(t types.Type) *typeDescInfo { + if tdi, ok := tm.types.At(t).(*typeDescInfo); ok { + return tdi + } + + var b bytes.Buffer + tm.mc.mangleTypeDescriptorName(t, &b) + + global := llvm.AddGlobal(tm.module, tm.getTypeDescType(t), b.String()) + global.SetGlobalConstant(true) + ptr := llvm.ConstBitCast(global, llvm.PointerType(tm.commonTypeType, 0)) + + gc := llvm.AddGlobal(tm.module, llvm.PointerType(llvm.Int8Type(), 0), b.String()+"$gc") + gc.SetGlobalConstant(true) + gcPtr := llvm.ConstBitCast(gc, llvm.PointerType(tm.ctx.Int8Type(), 0)) + + var mapDescPtr llvm.Value + if m, ok := t.Underlying().(*types.Map); ok { + var mapb bytes.Buffer + tm.mc.mangleMapDescriptorName(t, &mapb) + + mapDescPtr = llvm.AddGlobal(tm.module, tm.mapDescType, mapb.String()) + mapDescPtr.SetGlobalConstant(true) + mapDescPtr.SetLinkage(llvm.LinkOnceODRLinkage) + mapDescPtr.SetInitializer(tm.makeMapDesc(ptr, m)) + } + + tdi := &typeDescInfo{ + global: global, + commonTypePtr: ptr, + mapDescPtr: mapDescPtr, + gc: gc, + gcPtr: gcPtr, + } + tm.types.Set(t, tdi) + return tdi +} + +func (tm *TypeMap) getTypeDescriptorPointer(t types.Type) llvm.Value { + return tm.getTypeDescInfo(t).commonTypePtr +} + +func (tm *TypeMap) getMapDescriptorPointer(t types.Type) llvm.Value { + return tm.getTypeDescInfo(t).mapDescPtr +} + +func (tm *TypeMap) getGcPointer(t types.Type) llvm.Value { + return tm.getTypeDescInfo(t).gcPtr +} + +func (tm *TypeMap) getItabPointer(srctype types.Type, targettype *types.Interface) llvm.Value { + if targettype.NumMethods() == 0 { + return tm.ToRuntime(srctype) + } else { + return tm.getImtPointer(srctype, targettype) + } +} + +func (tm *TypeMap) getImtPointer(srctype types.Type, targettype *types.Interface) llvm.Value { + tdi := tm.getTypeDescInfo(srctype) + + if ptr, ok := tdi.interfaceMethodTables.At(targettype).(llvm.Value); ok { + return ptr + } + + srcms := tm.MethodSet(srctype) + targetms := tm.MethodSet(targettype) + + i8ptr := llvm.PointerType(llvm.Int8Type(), 0) + + elems := make([]llvm.Value, targetms.Len()+1) + elems[0] = tm.ToRuntime(srctype) + for i, targetm := range orderedMethodSet(targetms) { + srcm := srcms.Lookup(targetm.Obj().Pkg(), targetm.Obj().Name()) + + elems[i+1] = tm.methodResolver.ResolveMethod(srcm).value + } + imtinit := llvm.ConstArray(i8ptr, elems) + + var b bytes.Buffer + tm.mc.mangleImtName(srctype, targettype, &b) + imt := llvm.AddGlobal(tm.module, imtinit.Type(), b.String()) + imt.SetGlobalConstant(true) + imt.SetInitializer(imtinit) + imt.SetLinkage(llvm.LinkOnceODRLinkage) + + imtptr := llvm.ConstBitCast(imt, i8ptr) + tdi.interfaceMethodTables.Set(targettype, imtptr) + return imtptr +} + +const ( + // From gofrontend/types.h + gccgoRuntimeTypeKindBOOL = 1 + gccgoRuntimeTypeKindINT = 2 + gccgoRuntimeTypeKindINT8 = 3 + gccgoRuntimeTypeKindINT16 = 4 + gccgoRuntimeTypeKindINT32 = 5 + gccgoRuntimeTypeKindINT64 = 6 + gccgoRuntimeTypeKindUINT = 7 + gccgoRuntimeTypeKindUINT8 = 8 + gccgoRuntimeTypeKindUINT16 = 9 + gccgoRuntimeTypeKindUINT32 = 10 + gccgoRuntimeTypeKindUINT64 = 11 + gccgoRuntimeTypeKindUINTPTR = 12 + gccgoRuntimeTypeKindFLOAT32 = 13 + gccgoRuntimeTypeKindFLOAT64 = 14 + gccgoRuntimeTypeKindCOMPLEX64 = 15 + gccgoRuntimeTypeKindCOMPLEX128 = 16 + gccgoRuntimeTypeKindARRAY = 17 + gccgoRuntimeTypeKindCHAN = 18 + gccgoRuntimeTypeKindFUNC = 19 + gccgoRuntimeTypeKindINTERFACE = 20 + gccgoRuntimeTypeKindMAP = 21 + gccgoRuntimeTypeKindPTR = 22 + gccgoRuntimeTypeKindSLICE = 23 + gccgoRuntimeTypeKindSTRING = 24 + gccgoRuntimeTypeKindSTRUCT = 25 + gccgoRuntimeTypeKindUNSAFE_POINTER = 26 + gccgoRuntimeTypeKindNO_POINTERS = (1 << 7) +) + +func hasPointers(t types.Type) bool { + switch t := t.(type) { + case *types.Basic: + return t.Kind() == types.String || t.Kind() == types.UnsafePointer + + case *types.Signature, *types.Pointer, *types.Slice, *types.Map, *types.Chan, *types.Interface: + return true + + case *types.Struct: + for i := 0; i != t.NumFields(); i++ { + if hasPointers(t.Field(i).Type()) { + return true + } + } + return false + + case *types.Named: + return hasPointers(t.Underlying()) + + case *types.Array: + return hasPointers(t.Elem()) + + default: + panic("unrecognized type") + } +} + +func runtimeTypeKind(t types.Type) (k uint8) { + switch t := t.(type) { + case *types.Basic: + switch t.Kind() { + case types.Bool: + k = gccgoRuntimeTypeKindBOOL + case types.Int: + k = gccgoRuntimeTypeKindINT + case types.Int8: + k = gccgoRuntimeTypeKindINT8 + case types.Int16: + k = gccgoRuntimeTypeKindINT16 + case types.Int32: + k = gccgoRuntimeTypeKindINT32 + case types.Int64: + k = gccgoRuntimeTypeKindINT64 + case types.Uint: + k = gccgoRuntimeTypeKindUINT + case types.Uint8: + k = gccgoRuntimeTypeKindUINT8 + case types.Uint16: + k = gccgoRuntimeTypeKindUINT16 + case types.Uint32: + k = gccgoRuntimeTypeKindUINT32 + case types.Uint64: + k = gccgoRuntimeTypeKindUINT64 + case types.Uintptr: + k = gccgoRuntimeTypeKindUINTPTR + case types.Float32: + k = gccgoRuntimeTypeKindFLOAT32 + case types.Float64: + k = gccgoRuntimeTypeKindFLOAT64 + case types.Complex64: + k = gccgoRuntimeTypeKindCOMPLEX64 + case types.Complex128: + k = gccgoRuntimeTypeKindCOMPLEX128 + case types.String: + k = gccgoRuntimeTypeKindSTRING + case types.UnsafePointer: + k = gccgoRuntimeTypeKindUNSAFE_POINTER + default: + panic("unrecognized builtin type") + } + case *types.Array: + k = gccgoRuntimeTypeKindARRAY + case *types.Slice: + k = gccgoRuntimeTypeKindSLICE + case *types.Struct: + k = gccgoRuntimeTypeKindSTRUCT + case *types.Pointer: + k = gccgoRuntimeTypeKindPTR + case *types.Signature: + k = gccgoRuntimeTypeKindFUNC + case *types.Interface: + k = gccgoRuntimeTypeKindINTERFACE + case *types.Map: + k = gccgoRuntimeTypeKindMAP + case *types.Chan: + k = gccgoRuntimeTypeKindCHAN + case *types.Named: + return runtimeTypeKind(t.Underlying()) + default: + panic("unrecognized type") + } + + if !hasPointers(t) { + k |= gccgoRuntimeTypeKindNO_POINTERS + } + + return +} + +func (tm *TypeMap) makeCommonType(t types.Type) llvm.Value { + var vals [12]llvm.Value + vals[0] = llvm.ConstInt(tm.ctx.Int8Type(), uint64(runtimeTypeKind(t)), false) + vals[1] = llvm.ConstInt(tm.ctx.Int8Type(), uint64(tm.Alignof(t)), false) + vals[2] = vals[1] + vals[3] = llvm.ConstInt(tm.inttype, uint64(tm.Sizeof(t)), false) + vals[4] = llvm.ConstInt(tm.ctx.Int32Type(), uint64(tm.getTypeHash(t)), false) + hash, equal := tm.getAlgorithmFunctions(t) + vals[5] = hash + vals[6] = equal + vals[7] = tm.getGcPointer(t) + var b bytes.Buffer + tm.writeType(t, &b) + vals[8] = tm.globalStringPtr(b.String()) + vals[9] = tm.makeUncommonTypePtr(t) + if _, ok := t.(*types.Named); ok { + vals[10] = tm.getTypeDescriptorPointer(types.NewPointer(t)) + } else { + vals[10] = llvm.ConstPointerNull(llvm.PointerType(tm.commonTypeType, 0)) + } + vals[11] = tm.zeroValue + + return llvm.ConstNamedStruct(tm.commonTypeType, vals[:]) +} + +func (tm *TypeMap) makeBasicType(t types.Type, u *types.Basic) llvm.Value { + return tm.makeCommonType(t) +} + +func (tm *TypeMap) makeArrayType(t types.Type, a *types.Array) llvm.Value { + var vals [4]llvm.Value + vals[0] = tm.makeCommonType(t) + vals[1] = tm.getTypeDescriptorPointer(a.Elem()) + vals[2] = tm.getTypeDescriptorPointer(types.NewSlice(a.Elem())) + vals[3] = llvm.ConstInt(tm.inttype, uint64(a.Len()), false) + + return llvm.ConstNamedStruct(tm.arrayTypeType, vals[:]) +} + +func (tm *TypeMap) makeSliceType(t types.Type, s *types.Slice) llvm.Value { + var vals [2]llvm.Value + vals[0] = tm.makeCommonType(t) + vals[1] = tm.getTypeDescriptorPointer(s.Elem()) + + return llvm.ConstNamedStruct(tm.sliceTypeType, vals[:]) +} + +func (tm *TypeMap) makeStructType(t types.Type, s *types.Struct) llvm.Value { + var vals [2]llvm.Value + vals[0] = tm.makeCommonType(t) + + fieldVars := make([]*types.Var, s.NumFields()) + for i := range fieldVars { + fieldVars[i] = s.Field(i) + } + offsets := tm.Offsetsof(fieldVars) + structFields := make([]llvm.Value, len(fieldVars)) + for i, field := range fieldVars { + var sfvals [5]llvm.Value + if !field.Anonymous() { + sfvals[0] = tm.globalStringPtr(field.Name()) + } else { + sfvals[0] = llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0)) + } + if !field.Exported() && field.Pkg() != nil { + sfvals[1] = tm.globalStringPtr(field.Pkg().Path()) + } else { + sfvals[1] = llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0)) + } + sfvals[2] = tm.getTypeDescriptorPointer(field.Type()) + if tag := s.Tag(i); tag != "" { + sfvals[3] = tm.globalStringPtr(tag) + } else { + sfvals[3] = llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0)) + } + sfvals[4] = llvm.ConstInt(tm.inttype, uint64(offsets[i]), false) + + structFields[i] = llvm.ConstNamedStruct(tm.structFieldType, sfvals[:]) + } + vals[1] = tm.makeSlice(structFields, tm.structFieldSliceType) + + return llvm.ConstNamedStruct(tm.structTypeType, vals[:]) +} + +func (tm *TypeMap) makePointerType(t types.Type, p *types.Pointer) llvm.Value { + var vals [2]llvm.Value + vals[0] = tm.makeCommonType(t) + vals[1] = tm.getTypeDescriptorPointer(p.Elem()) + + return llvm.ConstNamedStruct(tm.ptrTypeType, vals[:]) +} + +func (tm *TypeMap) rtypeSlice(t *types.Tuple) llvm.Value { + rtypes := make([]llvm.Value, t.Len()) + for i := range rtypes { + rtypes[i] = tm.getTypeDescriptorPointer(t.At(i).Type()) + } + return tm.makeSlice(rtypes, tm.typeSliceType) +} + +func (tm *TypeMap) makeFuncType(t types.Type, f *types.Signature) llvm.Value { + var vals [4]llvm.Value + vals[0] = tm.makeCommonType(t) + // dotdotdot + variadic := 0 + if f.Variadic() { + variadic = 1 + } + vals[1] = llvm.ConstInt(llvm.Int8Type(), uint64(variadic), false) + // in + vals[2] = tm.rtypeSlice(f.Params()) + // out + vals[3] = tm.rtypeSlice(f.Results()) + + return llvm.ConstNamedStruct(tm.funcTypeType, vals[:]) +} + +func (tm *TypeMap) makeInterfaceType(t types.Type, i *types.Interface) llvm.Value { + var vals [2]llvm.Value + vals[0] = tm.makeCommonType(t) + + methodset := tm.MethodSet(i) + imethods := make([]llvm.Value, methodset.Len()) + for index, ms := range orderedMethodSet(methodset) { + method := ms.Obj() + var imvals [3]llvm.Value + imvals[0] = tm.globalStringPtr(method.Name()) + if !method.Exported() && method.Pkg() != nil { + imvals[1] = tm.globalStringPtr(method.Pkg().Path()) + } else { + imvals[1] = llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0)) + } + mtyp := method.Type().(*types.Signature) + mftyp := types.NewSignature(nil, nil, mtyp.Params(), mtyp.Results(), mtyp.Variadic()) + imvals[2] = tm.getTypeDescriptorPointer(mftyp) + + imethods[index] = llvm.ConstNamedStruct(tm.imethodType, imvals[:]) + } + vals[1] = tm.makeSlice(imethods, tm.imethodSliceType) + + return llvm.ConstNamedStruct(tm.interfaceTypeType, vals[:]) +} + +func (tm *TypeMap) makeMapType(t types.Type, m *types.Map) llvm.Value { + var vals [3]llvm.Value + vals[0] = tm.makeCommonType(t) + vals[1] = tm.getTypeDescriptorPointer(m.Key()) + vals[2] = tm.getTypeDescriptorPointer(m.Elem()) + + return llvm.ConstNamedStruct(tm.mapTypeType, vals[:]) +} + +func (tm *TypeMap) makeMapDesc(ptr llvm.Value, m *types.Map) llvm.Value { + mapEntryType := structBType{[]backendType{ + tm.getBackendType(types.Typ[types.UnsafePointer]), + tm.getBackendType(m.Key()), + tm.getBackendType(m.Elem()), + }}.ToLLVM(tm.ctx) + + var vals [4]llvm.Value + // map_descriptor + vals[0] = ptr + // entry_size + vals[1] = llvm.ConstInt(tm.inttype, tm.target.TypeAllocSize(mapEntryType), false) + // key_offset + vals[2] = llvm.ConstInt(tm.inttype, tm.target.ElementOffset(mapEntryType, 1), false) + // value_offset + vals[3] = llvm.ConstInt(tm.inttype, tm.target.ElementOffset(mapEntryType, 2), false) + + return llvm.ConstNamedStruct(tm.mapDescType, vals[:]) +} + +func (tm *TypeMap) makeChanType(t types.Type, c *types.Chan) llvm.Value { + var vals [3]llvm.Value + vals[0] = tm.makeCommonType(t) + vals[1] = tm.getTypeDescriptorPointer(c.Elem()) + + // From gofrontend/go/types.cc + // These bits must match the ones in libgo/runtime/go-type.h. + var dir int + switch c.Dir() { + case types.RecvOnly: + dir = 1 + case types.SendOnly: + dir = 2 + case types.SendRecv: + dir = 3 + } + vals[2] = llvm.ConstInt(tm.inttype, uint64(dir), false) + + return llvm.ConstNamedStruct(tm.chanTypeType, vals[:]) +} + +func (tm *TypeMap) makeUncommonTypePtr(t types.Type) llvm.Value { + _, isbasic := t.(*types.Basic) + _, isnamed := t.(*types.Named) + + var mset types.MethodSet + // We store interface methods on the interface type. + if _, ok := t.Underlying().(*types.Interface); !ok { + mset = *tm.MethodSet(t) + } + + if !isbasic && !isnamed && mset.Len() == 0 { + return llvm.ConstPointerNull(llvm.PointerType(tm.uncommonTypeType, 0)) + } + + var vals [3]llvm.Value + + nullStringPtr := llvm.ConstPointerNull(llvm.PointerType(tm.stringType, 0)) + vals[0] = nullStringPtr + vals[1] = nullStringPtr + + if isbasic || isnamed { + nti := tm.mc.getNamedTypeInfo(t) + vals[0] = tm.globalStringPtr(nti.name) + if nti.pkgpath != "" { + path := nti.pkgpath + if nti.functionName != "" { + path += "." + nti.functionName + if nti.scopeNum != 0 { + path += "$" + strconv.Itoa(nti.scopeNum) + } + } + vals[1] = tm.globalStringPtr(path) + } + } + + // Store methods. All methods must be stored, not only exported ones; + // this is to allow satisfying of interfaces with non-exported methods. + methods := make([]llvm.Value, mset.Len()) + omset := orderedMethodSet(&mset) + for i := range methods { + var mvals [5]llvm.Value + + sel := omset[i] + mname := sel.Obj().Name() + mfunc := tm.methodResolver.ResolveMethod(sel) + ftyp := mfunc.Type().(*types.Signature) + + // name + mvals[0] = tm.globalStringPtr(mname) + + // pkgPath + mvals[1] = nullStringPtr + if pkg := sel.Obj().Pkg(); pkg != nil && !sel.Obj().Exported() { + mvals[1] = tm.globalStringPtr(pkg.Path()) + } + + // mtyp (method type, no receiver) + mftyp := types.NewSignature(nil, nil, ftyp.Params(), ftyp.Results(), ftyp.Variadic()) + mvals[2] = tm.getTypeDescriptorPointer(mftyp) + + // typ (function type, with receiver) + recvparam := types.NewParam(0, nil, "", t) + params := ftyp.Params() + rfparams := make([]*types.Var, params.Len()+1) + rfparams[0] = recvparam + for i := 0; i != ftyp.Params().Len(); i++ { + rfparams[i+1] = params.At(i) + } + rftyp := types.NewSignature(nil, nil, types.NewTuple(rfparams...), ftyp.Results(), ftyp.Variadic()) + mvals[3] = tm.getTypeDescriptorPointer(rftyp) + + // function + mvals[4] = mfunc.value + + methods[i] = llvm.ConstNamedStruct(tm.methodType, mvals[:]) + } + + vals[2] = tm.makeSlice(methods, tm.methodSliceType) + + uncommonType := llvm.ConstNamedStruct(tm.uncommonTypeType, vals[:]) + + uncommonTypePtr := llvm.AddGlobal(tm.module, tm.uncommonTypeType, "") + uncommonTypePtr.SetGlobalConstant(true) + uncommonTypePtr.SetInitializer(uncommonType) + uncommonTypePtr.SetLinkage(llvm.InternalLinkage) + return uncommonTypePtr +} + +// globalStringPtr returns a *string with the specified value. +func (tm *TypeMap) globalStringPtr(value string) llvm.Value { + strval := llvm.ConstString(value, false) + strglobal := llvm.AddGlobal(tm.module, strval.Type(), "") + strglobal.SetGlobalConstant(true) + strglobal.SetLinkage(llvm.InternalLinkage) + strglobal.SetInitializer(strval) + strglobal = llvm.ConstBitCast(strglobal, llvm.PointerType(llvm.Int8Type(), 0)) + strlen := llvm.ConstInt(tm.inttype, uint64(len(value)), false) + str := llvm.ConstStruct([]llvm.Value{strglobal, strlen}, false) + g := llvm.AddGlobal(tm.module, str.Type(), "") + g.SetGlobalConstant(true) + g.SetLinkage(llvm.InternalLinkage) + g.SetInitializer(str) + return g +} + +func (tm *TypeMap) makeNamedSliceType(tname string, elemtyp llvm.Type) llvm.Type { + t := tm.ctx.StructCreateNamed(tname) + t.StructSetBody([]llvm.Type{ + llvm.PointerType(elemtyp, 0), + tm.inttype, + tm.inttype, + }, false) + return t +} + +func (tm *TypeMap) makeSlice(values []llvm.Value, slicetyp llvm.Type) llvm.Value { + ptrtyp := slicetyp.StructElementTypes()[0] + var globalptr llvm.Value + if len(values) > 0 { + array := llvm.ConstArray(ptrtyp.ElementType(), values) + globalptr = llvm.AddGlobal(tm.module, array.Type(), "") + globalptr.SetGlobalConstant(true) + globalptr.SetLinkage(llvm.InternalLinkage) + globalptr.SetInitializer(array) + globalptr = llvm.ConstBitCast(globalptr, ptrtyp) + } else { + globalptr = llvm.ConstNull(ptrtyp) + } + len_ := llvm.ConstInt(tm.inttype, uint64(len(values)), false) + slice := llvm.ConstNull(slicetyp) + slice = llvm.ConstInsertValue(slice, globalptr, []uint32{0}) + slice = llvm.ConstInsertValue(slice, len_, []uint32{1}) + slice = llvm.ConstInsertValue(slice, len_, []uint32{2}) + return slice +} + +func isGlobalObject(obj types.Object) bool { + pkg := obj.Pkg() + return pkg == nil || obj.Parent() == pkg.Scope() +} Index: tools/llgo/irgen/types.go =================================================================== --- /dev/null +++ tools/llgo/irgen/types.go @@ -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() +} Index: tools/llgo/irgen/utils.go =================================================================== --- /dev/null +++ tools/llgo/irgen/utils.go @@ -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) +} Index: tools/llgo/irgen/value.go =================================================================== --- /dev/null +++ tools/llgo/irgen/value.go @@ -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 ( + "llvm.org/llgo/third_party/go.tools/go/exact" + "llvm.org/llgo/third_party/go.tools/go/types" + "fmt" + "go/token" + "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()) +} Index: tools/llgo/irgen/version.go =================================================================== --- /dev/null +++ tools/llgo/irgen/version.go @@ -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 +} Index: tools/llgo/libgo-noext.diff =================================================================== --- /dev/null +++ tools/llgo/libgo-noext.diff @@ -0,0 +1,1788 @@ +diff -r 225a208260a6 libgo/runtime/chan.goc +--- a/libgo/runtime/chan.goc Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/chan.goc Tue Sep 23 15:59:57 2014 -0700 +@@ -115,7 +115,7 @@ + mysg.releasetime = -1; + } + +- runtime_lock(c); ++ runtime_lock(&c->lock); + if(raceenabled) + runtime_racereadpc(c, pc, chansend); + if(c->closed) +@@ -128,7 +128,7 @@ + if(sg != nil) { + if(raceenabled) + racesync(c, sg); +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + + gp = sg->g; + gp->param = sg; +@@ -141,7 +141,7 @@ + } + + if(!block) { +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + return false; + } + +@@ -150,10 +150,10 @@ + mysg.selectdone = nil; + g->param = nil; + enqueue(&c->sendq, &mysg); +- runtime_parkunlock(c, "chan send"); ++ runtime_parkunlock(&c->lock, "chan send"); + + if(g->param == nil) { +- runtime_lock(c); ++ runtime_lock(&c->lock); + if(!c->closed) + runtime_throw("chansend: spurious wakeup"); + goto closed; +@@ -170,16 +170,16 @@ + + if(c->qcount >= c->dataqsiz) { + if(!block) { +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + return false; + } + mysg.g = g; + mysg.elem = nil; + mysg.selectdone = nil; + enqueue(&c->sendq, &mysg); +- runtime_parkunlock(c, "chan send"); ++ runtime_parkunlock(&c->lock, "chan send"); + +- runtime_lock(c); ++ runtime_lock(&c->lock); + goto asynch; + } + +@@ -196,18 +196,18 @@ + sg = dequeue(&c->recvq); + if(sg != nil) { + gp = sg->g; +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + if(sg->releasetime) + sg->releasetime = runtime_cputicks(); + runtime_ready(gp); + } else +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + if(mysg.releasetime > 0) + runtime_blockevent(mysg.releasetime - t0, 2); + return true; + + closed: +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + runtime_panicstring("send on closed channel"); + return false; // not reached + } +@@ -247,7 +247,7 @@ + mysg.releasetime = -1; + } + +- runtime_lock(c); ++ runtime_lock(&c->lock); + if(c->dataqsiz > 0) + goto asynch; + +@@ -258,7 +258,7 @@ + if(sg != nil) { + if(raceenabled) + racesync(c, sg); +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + + if(ep != nil) + runtime_memmove(ep, sg->elem, c->elemsize); +@@ -274,7 +274,7 @@ + } + + if(!block) { +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + return false; + } + +@@ -283,10 +283,10 @@ + mysg.selectdone = nil; + g->param = nil; + enqueue(&c->recvq, &mysg); +- runtime_parkunlock(c, "chan receive"); ++ runtime_parkunlock(&c->lock, "chan receive"); + + if(g->param == nil) { +- runtime_lock(c); ++ runtime_lock(&c->lock); + if(!c->closed) + runtime_throw("chanrecv: spurious wakeup"); + goto closed; +@@ -304,7 +304,7 @@ + goto closed; + + if(!block) { +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + if(received != nil) + *received = false; + return false; +@@ -313,9 +313,9 @@ + mysg.elem = nil; + mysg.selectdone = nil; + enqueue(&c->recvq, &mysg); +- runtime_parkunlock(c, "chan receive"); ++ runtime_parkunlock(&c->lock, "chan receive"); + +- runtime_lock(c); ++ runtime_lock(&c->lock); + goto asynch; + } + +@@ -334,12 +334,12 @@ + sg = dequeue(&c->sendq); + if(sg != nil) { + gp = sg->g; +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + if(sg->releasetime) + sg->releasetime = runtime_cputicks(); + runtime_ready(gp); + } else +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + + if(received != nil) + *received = true; +@@ -354,7 +354,7 @@ + *received = false; + if(raceenabled) + runtime_raceacquire(c); +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + if(mysg.releasetime > 0) + runtime_blockevent(mysg.releasetime - t0, 2); + return true; +@@ -628,7 +628,7 @@ + c0 = sel->lockorder[i]; + if(c0 && c0 != c) { + c = sel->lockorder[i]; +- runtime_lock(c); ++ runtime_lock(&c->lock); + } + } + } +@@ -656,7 +656,7 @@ + c = sel->lockorder[i]; + if(i>0 && sel->lockorder[i-1] == c) + continue; // will unlock it on the next iteration +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + } + } + +@@ -1071,9 +1071,9 @@ + if(runtime_gcwaiting()) + runtime_gosched(); + +- runtime_lock(c); ++ runtime_lock(&c->lock); + if(c->closed) { +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + runtime_panicstring("close of closed channel"); + } + +@@ -1108,7 +1108,7 @@ + runtime_ready(gp); + } + +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + } + + void +diff -r 225a208260a6 libgo/runtime/chan.h +--- a/libgo/runtime/chan.h Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/chan.h Tue Sep 23 15:59:57 2014 -0700 +@@ -39,7 +39,7 @@ + uintgo recvx; // receive index + WaitQ recvq; // list of recv waiters + WaitQ sendq; // list of send waiters +- Lock; ++ Lock lock; + }; + + // Buffer follows Hchan immediately in memory. +diff -r 225a208260a6 libgo/runtime/heapdump.c +--- a/libgo/runtime/heapdump.c Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/heapdump.c Tue Sep 23 15:59:57 2014 -0700 +@@ -387,7 +387,7 @@ + if(sp->kind != KindSpecialFinalizer) + continue; + spf = (SpecialFinalizer*)sp; +- p = (byte*)((s->start << PageShift) + spf->offset); ++ p = (byte*)((s->start << PageShift) + spf->special.offset); + dumpfinalizer(p, spf->fn, spf->ft, spf->ot); + } + } +@@ -566,7 +566,7 @@ + if(sp->kind != KindSpecialProfile) + continue; + spp = (SpecialProfile*)sp; +- p = (byte*)((s->start << PageShift) + spp->offset); ++ p = (byte*)((s->start << PageShift) + spp->special.offset); + dumpint(TagAllocSample); + dumpint((uintptr)p); + dumpint((uintptr)spp->b); +diff -r 225a208260a6 libgo/runtime/malloc.goc +--- a/libgo/runtime/malloc.goc Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/malloc.goc Tue Sep 23 15:59:57 2014 -0700 +@@ -440,9 +440,9 @@ + m->mcache->local_nlookup++; + if (sizeof(void*) == 4 && m->mcache->local_nlookup >= (1<<30)) { + // purge cache stats to prevent overflow +- runtime_lock(&runtime_mheap); ++ runtime_lock(&runtime_mheap.lock); + runtime_purgecachedstats(m->mcache); +- runtime_unlock(&runtime_mheap); ++ runtime_unlock(&runtime_mheap.lock); + } + + s = runtime_MHeap_LookupMaybe(&runtime_mheap, v); +@@ -743,7 +743,7 @@ + + static struct + { +- Lock; ++ Lock lock; + byte* pos; + byte* end; + } persistent; +@@ -772,19 +772,19 @@ + align = 8; + if(size >= PersistentAllocMaxBlock) + return runtime_SysAlloc(size, stat); +- runtime_lock(&persistent); ++ runtime_lock(&persistent.lock); + persistent.pos = (byte*)ROUND((uintptr)persistent.pos, align); + if(persistent.pos + size > persistent.end) { + persistent.pos = runtime_SysAlloc(PersistentAllocChunk, &mstats.other_sys); + if(persistent.pos == nil) { +- runtime_unlock(&persistent); ++ runtime_unlock(&persistent.lock); + runtime_throw("runtime: cannot allocate memory"); + } + persistent.end = persistent.pos + PersistentAllocChunk; + } + p = persistent.pos; + persistent.pos += size; +- runtime_unlock(&persistent); ++ runtime_unlock(&persistent.lock); + if(stat != &mstats.other_sys) { + // reaccount the allocation against provided stat + runtime_xadd64(stat, size); +diff -r 225a208260a6 libgo/runtime/malloc.h +--- a/libgo/runtime/malloc.h Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/malloc.h Tue Sep 23 15:59:57 2014 -0700 +@@ -390,7 +390,7 @@ + typedef struct SpecialFinalizer SpecialFinalizer; + struct SpecialFinalizer + { +- Special; ++ Special special; + FuncVal* fn; + const FuncType* ft; + const PtrType* ot; +@@ -401,7 +401,7 @@ + typedef struct SpecialProfile SpecialProfile; + struct SpecialProfile + { +- Special; ++ Special special; + Bucket* b; + }; + +@@ -458,7 +458,7 @@ + // Central list of free objects of a given size. + struct MCentral + { +- Lock; ++ Lock lock; + int32 sizeclass; + MSpan nonempty; // list of spans with a free object + MSpan empty; // list of spans with no free objects (or cached in an MCache) +@@ -476,7 +476,7 @@ + // but all the other global data is here too. + struct MHeap + { +- Lock; ++ Lock lock; + MSpan free[MaxMHeapList]; // free lists of given length + MSpan freelarge; // free lists length >= MaxMHeapList + MSpan busy[MaxMHeapList]; // busy lists of large objects of given length +@@ -505,7 +505,7 @@ + // spaced CacheLineSize bytes apart, so that each MCentral.Lock + // gets its own cache line. + struct { +- MCentral; ++ MCentral mcentral; + byte pad[64]; + } central[NumSizeClasses]; + +diff -r 225a208260a6 libgo/runtime/mcache.c +--- a/libgo/runtime/mcache.c Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/mcache.c Tue Sep 23 15:59:57 2014 -0700 +@@ -23,9 +23,9 @@ + MCache *c; + int32 i; + +- runtime_lock(&runtime_mheap); ++ runtime_lock(&runtime_mheap.lock); + c = runtime_FixAlloc_Alloc(&runtime_mheap.cachealloc); +- runtime_unlock(&runtime_mheap); ++ runtime_unlock(&runtime_mheap.lock); + runtime_memclr((byte*)c, sizeof(*c)); + for(i = 0; i < NumSizeClasses; i++) + c->alloc[i] = &emptymspan; +@@ -44,10 +44,10 @@ + runtime_freemcache(MCache *c) + { + runtime_MCache_ReleaseAll(c); +- runtime_lock(&runtime_mheap); ++ runtime_lock(&runtime_mheap.lock); + runtime_purgecachedstats(c); + runtime_FixAlloc_Free(&runtime_mheap.cachealloc, c); +- runtime_unlock(&runtime_mheap); ++ runtime_unlock(&runtime_mheap.lock); + } + + // Gets a span that has a free object in it and assigns it +@@ -64,19 +64,19 @@ + if(s->freelist != nil) + runtime_throw("refill on a nonempty span"); + if(s != &emptymspan) +- runtime_MCentral_UncacheSpan(&runtime_mheap.central[sizeclass], s); ++ runtime_MCentral_UncacheSpan(&runtime_mheap.central[sizeclass].mcentral, s); + + // Push any explicitly freed objects to the central lists. + // Not required, but it seems like a good time to do it. + l = &c->free[sizeclass]; + if(l->nlist > 0) { +- runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass], l->list); ++ runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass].mcentral, l->list); + l->list = nil; + l->nlist = 0; + } + + // Get a new cached span from the central lists. +- s = runtime_MCentral_CacheSpan(&runtime_mheap.central[sizeclass]); ++ s = runtime_MCentral_CacheSpan(&runtime_mheap.central[sizeclass].mcentral); + if(s == nil) + runtime_throw("out of memory"); + if(s->freelist == nil) { +@@ -102,7 +102,7 @@ + // We transfer a span at a time from MCentral to MCache, + // so we'll do the same in the other direction. + if(l->nlist >= (runtime_class_to_allocnpages[sizeclass]<list); ++ runtime_MCentral_FreeList(&runtime_mheap.central[sizeclass].mcentral, l->list); + l->list = nil; + l->nlist = 0; + } +@@ -118,12 +118,12 @@ + for(i=0; ialloc[i]; + if(s != &emptymspan) { +- runtime_MCentral_UncacheSpan(&runtime_mheap.central[i], s); ++ runtime_MCentral_UncacheSpan(&runtime_mheap.central[i].mcentral, s); + c->alloc[i] = &emptymspan; + } + l = &c->free[i]; + if(l->nlist > 0) { +- runtime_MCentral_FreeList(&runtime_mheap.central[i], l->list); ++ runtime_MCentral_FreeList(&runtime_mheap.central[i].mcentral, l->list); + l->list = nil; + l->nlist = 0; + } +diff -r 225a208260a6 libgo/runtime/mcentral.c +--- a/libgo/runtime/mcentral.c Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/mcentral.c Tue Sep 23 15:59:57 2014 -0700 +@@ -39,14 +39,14 @@ + int32 cap, n; + uint32 sg; + +- runtime_lock(c); ++ runtime_lock(&c->lock); + sg = runtime_mheap.sweepgen; + retry: + for(s = c->nonempty.next; s != &c->nonempty; s = s->next) { + if(s->sweepgen == sg-2 && runtime_cas(&s->sweepgen, sg-2, sg-1)) { +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + runtime_MSpan_Sweep(s); +- runtime_lock(c); ++ runtime_lock(&c->lock); + // the span could have been moved to heap, retry + goto retry; + } +@@ -65,9 +65,9 @@ + runtime_MSpanList_Remove(s); + // swept spans are at the end of the list + runtime_MSpanList_InsertBack(&c->empty, s); +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + runtime_MSpan_Sweep(s); +- runtime_lock(c); ++ runtime_lock(&c->lock); + // the span could be moved to nonempty or heap, retry + goto retry; + } +@@ -82,7 +82,7 @@ + + // Replenish central list if empty. + if(!MCentral_Grow(c)) { +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + return nil; + } + goto retry; +@@ -98,7 +98,7 @@ + runtime_MSpanList_Remove(s); + runtime_MSpanList_InsertBack(&c->empty, s); + s->incache = true; +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + return s; + } + +@@ -109,7 +109,7 @@ + MLink *v; + int32 cap, n; + +- runtime_lock(c); ++ runtime_lock(&c->lock); + + s->incache = false; + +@@ -135,7 +135,7 @@ + runtime_MSpanList_Remove(s); + runtime_MSpanList_Insert(&c->nonempty, s); + } +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + } + + // Free the list of objects back into the central free list c. +@@ -145,12 +145,12 @@ + { + MLink *next; + +- runtime_lock(c); ++ runtime_lock(&c->lock); + for(; start != nil; start = next) { + next = start->next; + MCentral_Free(c, start); + } +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + } + + // Helper: free one object back into the central free list. +@@ -193,7 +193,7 @@ + // If s is completely freed, return it to the heap. + if(s->ref == 0) { + MCentral_ReturnToHeap(c, s); // unlocks c +- runtime_lock(c); ++ runtime_lock(&c->lock); + } + } + +@@ -206,7 +206,7 @@ + { + if(s->incache) + runtime_throw("freespan into cached span"); +- runtime_lock(c); ++ runtime_lock(&c->lock); + + // Move to nonempty if necessary. + if(s->freelist == nil) { +@@ -227,7 +227,7 @@ + runtime_atomicstore(&s->sweepgen, runtime_mheap.sweepgen); + + if(s->ref != 0) { +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + return false; + } + +@@ -260,12 +260,12 @@ + byte *p; + MSpan *s; + +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + runtime_MGetSizeClassInfo(c->sizeclass, &size, &npages, &n); + s = runtime_MHeap_Alloc(&runtime_mheap, npages, c->sizeclass, 0, 1); + if(s == nil) { + // TODO(rsc): Log out of memory +- runtime_lock(c); ++ runtime_lock(&c->lock); + return false; + } + +@@ -282,7 +282,7 @@ + *tailp = nil; + runtime_markspan((byte*)(s->start<npages<lock); + c->nfree += n; + runtime_MSpanList_Insert(&c->nonempty, s); + return true; +@@ -301,7 +301,7 @@ + if(s->ref != 0) + runtime_throw("ref wrong"); + c->nfree -= (s->npages << PageShift) / size; +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + runtime_unmarkspan((byte*)(s->start<npages<start << PageShift) + spf->offset/s->elemsize*s->elemsize); ++ p = (void*)((s->start << PageShift) + spf->special.offset/s->elemsize*s->elemsize); + enqueue1(&wbuf, (Obj){p, s->elemsize, 0}); + enqueue1(&wbuf, (Obj){(void*)&spf->fn, PtrSize, 0}); + enqueue1(&wbuf, (Obj){(void*)&spf->ft, PtrSize, 0}); +@@ -1378,7 +1378,7 @@ + b = (Workbuf*)runtime_lfstackpop(&work.empty); + if(b == nil) { + // Need to allocate. +- runtime_lock(&work); ++ runtime_lock(&work.lock); + if(work.nchunk < sizeof *b) { + work.nchunk = 1<<20; + work.chunk = runtime_SysAlloc(work.nchunk, &mstats.gc_sys); +@@ -1388,7 +1388,7 @@ + b = (Workbuf*)work.chunk; + work.chunk += sizeof *b; + work.nchunk -= sizeof *b; +- runtime_unlock(&work); ++ runtime_unlock(&work.lock); + } + b->nobj = 0; + return b; +@@ -1802,7 +1802,7 @@ + c->local_nsmallfree[cl] += nfree; + c->local_cachealloc -= nfree * size; + runtime_xadd64(&mstats.next_gc, -(uint64)(nfree * size * (gcpercent + 100)/100)); +- res = runtime_MCentral_FreeSpan(&runtime_mheap.central[cl], s, nfree, head.next, end); ++ res = runtime_MCentral_FreeSpan(&runtime_mheap.central[cl].mcentral, s, nfree, head.next, end); + //MCentral_FreeSpan updates sweepgen + } + return res; +@@ -2147,10 +2147,10 @@ + return; + + if(gcpercent == GcpercentUnknown) { // first time through +- runtime_lock(&runtime_mheap); ++ runtime_lock(&runtime_mheap.lock); + if(gcpercent == GcpercentUnknown) + gcpercent = readgogc(); +- runtime_unlock(&runtime_mheap); ++ runtime_unlock(&runtime_mheap.lock); + } + if(gcpercent < 0) + return; +@@ -2421,7 +2421,7 @@ + + // Pass back: pauses, last gc (absolute time), number of gc, total pause ns. + p = (uint64*)pauses->array; +- runtime_lock(&runtime_mheap); ++ runtime_lock(&runtime_mheap.lock); + n = mstats.numgc; + if(n > nelem(mstats.pause_ns)) + n = nelem(mstats.pause_ns); +@@ -2436,7 +2436,7 @@ + p[n] = mstats.last_gc; + p[n+1] = mstats.numgc; + p[n+2] = mstats.pause_total_ns; +- runtime_unlock(&runtime_mheap); ++ runtime_unlock(&runtime_mheap.lock); + pauses->__count = n+3; + } + +@@ -2444,14 +2444,14 @@ + runtime_setgcpercent(int32 in) { + int32 out; + +- runtime_lock(&runtime_mheap); ++ runtime_lock(&runtime_mheap.lock); + if(gcpercent == GcpercentUnknown) + gcpercent = readgogc(); + out = gcpercent; + if(in < 0) + in = -1; + gcpercent = in; +- runtime_unlock(&runtime_mheap); ++ runtime_unlock(&runtime_mheap.lock); + return out; + } + +diff -r 225a208260a6 libgo/runtime/mheap.c +--- a/libgo/runtime/mheap.c Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/mheap.c Tue Sep 23 15:59:57 2014 -0700 +@@ -70,7 +70,7 @@ + runtime_MSpanList_Init(&h->freelarge); + runtime_MSpanList_Init(&h->busylarge); + for(i=0; icentral); i++) +- runtime_MCentral_Init(&h->central[i], i); ++ runtime_MCentral_Init(&h->central[i].mcentral, i); + } + + void +@@ -109,9 +109,9 @@ + runtime_MSpanList_Remove(s); + // swept spans are at the end of the list + runtime_MSpanList_InsertBack(list, s); +- runtime_unlock(h); ++ runtime_unlock(&h->lock); + n += runtime_MSpan_Sweep(s); +- runtime_lock(h); ++ runtime_lock(&h->lock); + if(n >= npages) + return n; + // the span could have been moved elsewhere +@@ -156,7 +156,7 @@ + } + + // Now sweep everything that is not yet swept. +- runtime_unlock(h); ++ runtime_unlock(&h->lock); + for(;;) { + n = runtime_sweepone(); + if(n == (uintptr)-1) // all spans are swept +@@ -165,7 +165,7 @@ + if(reclaimed >= npage) + break; + } +- runtime_lock(h); ++ runtime_lock(&h->lock); + } + + // Allocate a new span of npage pages from the heap +@@ -175,7 +175,7 @@ + { + MSpan *s; + +- runtime_lock(h); ++ runtime_lock(&h->lock); + mstats.heap_alloc += runtime_m()->mcache->local_cachealloc; + runtime_m()->mcache->local_cachealloc = 0; + s = MHeap_AllocLocked(h, npage, sizeclass); +@@ -191,7 +191,7 @@ + runtime_MSpanList_InsertBack(&h->busylarge, s); + } + } +- runtime_unlock(h); ++ runtime_unlock(&h->lock); + if(s != nil) { + if(needzero && s->needzero) + runtime_memclr((byte*)(s->start<npages<lock); + mstats.heap_alloc += runtime_m()->mcache->local_cachealloc; + runtime_m()->mcache->local_cachealloc = 0; + mstats.heap_inuse -= s->npages<lock); + } + + static void +@@ -548,10 +548,10 @@ + runtime_noteclear(¬e); + runtime_notetsleepg(¬e, tick); + +- runtime_lock(h); ++ runtime_lock(&h->lock); + unixnow = runtime_unixnanotime(); + if(unixnow - mstats.last_gc > forcegc) { +- runtime_unlock(h); ++ runtime_unlock(&h->lock); + // The scavenger can not block other goroutines, + // otherwise deadlock detector can fire spuriously. + // GC blocks other goroutines via the runtime_worldsema. +@@ -561,11 +561,11 @@ + runtime_notetsleepg(¬e, -1); + if(runtime_debug.gctrace > 0) + runtime_printf("scvg%d: GC forced\n", k); +- runtime_lock(h); ++ runtime_lock(&h->lock); + } + now = runtime_nanotime(); + scavenge(k, now, limit); +- runtime_unlock(h); ++ runtime_unlock(&h->lock); + } + } + +@@ -575,9 +575,9 @@ + runtime_debug_freeOSMemory(void) + { + runtime_gc(2); // force GC and do eager sweep +- runtime_lock(&runtime_mheap); ++ runtime_lock(&runtime_mheap.lock); + scavenge(-1, ~(uintptr)0, 0); +- runtime_unlock(&runtime_mheap); ++ runtime_unlock(&runtime_mheap.lock); + } + + // Initialize a new span with the given start and npages. +@@ -752,11 +752,11 @@ + runtime_lock(&runtime_mheap.speciallock); + s = runtime_FixAlloc_Alloc(&runtime_mheap.specialfinalizeralloc); + runtime_unlock(&runtime_mheap.speciallock); +- s->kind = KindSpecialFinalizer; ++ s->special.kind = KindSpecialFinalizer; + s->fn = f; + s->ft = ft; + s->ot = ot; +- if(addspecial(p, s)) ++ if(addspecial(p, &s->special)) + return true; + + // There was an old finalizer +@@ -789,9 +789,9 @@ + runtime_lock(&runtime_mheap.speciallock); + s = runtime_FixAlloc_Alloc(&runtime_mheap.specialprofilealloc); + runtime_unlock(&runtime_mheap.speciallock); +- s->kind = KindSpecialProfile; ++ s->special.kind = KindSpecialProfile; + s->b = b; +- if(!addspecial(p, s)) ++ if(!addspecial(p, &s->special)) + runtime_throw("setprofilebucket: profile already set"); + } + +@@ -879,14 +879,14 @@ + // remove the span from whatever list it is in now + if(s->sizeclass > 0) { + // must be in h->central[x].empty +- c = &h->central[s->sizeclass]; +- runtime_lock(c); ++ c = &h->central[s->sizeclass].mcentral; ++ runtime_lock(&c->lock); + runtime_MSpanList_Remove(s); +- runtime_unlock(c); +- runtime_lock(h); ++ runtime_unlock(&c->lock); ++ runtime_lock(&h->lock); + } else { + // must be in h->busy/busylarge +- runtime_lock(h); ++ runtime_lock(&h->lock); + runtime_MSpanList_Remove(s); + } + // heap is locked now +@@ -933,18 +933,18 @@ + + // place the span into a new list + if(s->sizeclass > 0) { +- runtime_unlock(h); +- c = &h->central[s->sizeclass]; +- runtime_lock(c); ++ runtime_unlock(&h->lock); ++ c = &h->central[s->sizeclass].mcentral; ++ runtime_lock(&c->lock); + // swept spans are at the end of the list + runtime_MSpanList_InsertBack(&c->empty, s); +- runtime_unlock(c); ++ runtime_unlock(&c->lock); + } else { + // Swept spans are at the end of lists. + if(s->npages < nelem(h->free)) + runtime_MSpanList_InsertBack(&h->busy[s->npages], s); + else + runtime_MSpanList_InsertBack(&h->busylarge, s); +- runtime_unlock(h); ++ runtime_unlock(&h->lock); + } + } +diff -r 225a208260a6 libgo/runtime/netpoll.goc +--- a/libgo/runtime/netpoll.goc Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/netpoll.goc Tue Sep 23 15:59:57 2014 -0700 +@@ -53,7 +53,7 @@ + // pollReset, pollWait, pollWaitCanceled and runtime_netpollready (IO rediness notification) + // proceed w/o taking the lock. So closing, rg, rd, wg and wd are manipulated + // in a lock-free way by all operations. +- Lock; // protectes the following fields ++ Lock lock; // protectes the following fields + uintptr fd; + bool closing; + uintptr seq; // protects from stale timers and ready notifications +@@ -68,7 +68,7 @@ + + static struct + { +- Lock; ++ Lock lock; + PollDesc* first; + // PollDesc objects must be type-stable, + // because we can get ready notification from epoll/kqueue +@@ -100,7 +100,7 @@ + + func runtime_pollOpen(fd uintptr) (pd *PollDesc, errno int) { + pd = allocPollDesc(); +- runtime_lock(pd); ++ runtime_lock(&pd->lock); + if(pd->wg != nil && pd->wg != READY) + runtime_throw("runtime_pollOpen: blocked write on free descriptor"); + if(pd->rg != nil && pd->rg != READY) +@@ -112,7 +112,7 @@ + pd->rd = 0; + pd->wg = nil; + pd->wd = 0; +- runtime_unlock(pd); ++ runtime_unlock(&pd->lock); + + errno = runtime_netpollopen(fd, pd); + } +@@ -125,10 +125,10 @@ + if(pd->rg != nil && pd->rg != READY) + runtime_throw("runtime_pollClose: blocked read on closing descriptor"); + runtime_netpollclose(pd->fd); +- runtime_lock(&pollcache); ++ runtime_lock(&pollcache.lock); + pd->link = pollcache.first; + pollcache.first = pd; +- runtime_unlock(&pollcache); ++ runtime_unlock(&pollcache.lock); + } + + func runtime_pollReset(pd *PollDesc, mode int) (err int) { +@@ -169,9 +169,9 @@ + func runtime_pollSetDeadline(pd *PollDesc, d int64, mode int) { + G *rg, *wg; + +- runtime_lock(pd); ++ runtime_lock(&pd->lock); + if(pd->closing) { +- runtime_unlock(pd); ++ runtime_unlock(&pd->lock); + return; + } + pd->seq++; // invalidate current timers +@@ -223,7 +223,7 @@ + rg = netpollunblock(pd, 'r', false); + if(pd->wd < 0) + wg = netpollunblock(pd, 'w', false); +- runtime_unlock(pd); ++ runtime_unlock(&pd->lock); + if(rg) + runtime_ready(rg); + if(wg) +@@ -233,7 +233,7 @@ + func runtime_pollUnblock(pd *PollDesc) { + G *rg, *wg; + +- runtime_lock(pd); ++ runtime_lock(&pd->lock); + if(pd->closing) + runtime_throw("runtime_pollUnblock: already closing"); + pd->closing = true; +@@ -249,7 +249,7 @@ + runtime_deltimer(&pd->wt); + pd->wt.fv = nil; + } +- runtime_unlock(pd); ++ runtime_unlock(&pd->lock); + if(rg) + runtime_ready(rg); + if(wg) +@@ -277,13 +277,13 @@ + void + runtime_netpolllock(PollDesc *pd) + { +- runtime_lock(pd); ++ runtime_lock(&pd->lock); + } + + void + runtime_netpollunlock(PollDesc *pd) + { +- runtime_unlock(pd); ++ runtime_unlock(&pd->lock); + } + + // make pd ready, newly runnable goroutines (if any) are enqueued info gpp list +@@ -401,10 +401,10 @@ + // If it's stale, ignore the timer event. + seq = (uintptr)arg.type; + rg = wg = nil; +- runtime_lock(pd); ++ runtime_lock(&pd->lock); + if(seq != pd->seq) { + // The descriptor was reused or timers were reset. +- runtime_unlock(pd); ++ runtime_unlock(&pd->lock); + return; + } + if(read) { +@@ -421,7 +421,7 @@ + runtime_atomicstorep(&pd->wt.fv, nil); // full memory barrier between store to wd and load of wg in netpollunblock + wg = netpollunblock(pd, 'w', false); + } +- runtime_unlock(pd); ++ runtime_unlock(&pd->lock); + if(rg) + runtime_ready(rg); + if(wg) +@@ -452,7 +452,7 @@ + PollDesc *pd; + uint32 i, n; + +- runtime_lock(&pollcache); ++ runtime_lock(&pollcache.lock); + if(pollcache.first == nil) { + n = PollBlockSize/sizeof(*pd); + if(n == 0) +@@ -467,6 +467,6 @@ + } + pd = pollcache.first; + pollcache.first = pd->link; +- runtime_unlock(&pollcache); ++ runtime_unlock(&pollcache.lock); + return pd; + } +diff -r 225a208260a6 libgo/runtime/proc.c +--- a/libgo/runtime/proc.c Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/proc.c Tue Sep 23 15:59:57 2014 -0700 +@@ -357,7 +357,7 @@ + + typedef struct Sched Sched; + struct Sched { +- Lock; ++ Lock lock; + + uint64 goidgen; + M* midle; // idle m's waiting for work +@@ -770,7 +770,7 @@ + + mp->fastrand = 0x49f6428aUL + mp->id + runtime_cputicks(); + +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + mp->id = runtime_sched.mcount++; + checkmcount(); + runtime_mpreinit(mp); +@@ -781,7 +781,7 @@ + // runtime_NumCgoCall() iterates over allm w/o schedlock, + // so we need to publish it safely. + runtime_atomicstorep(&runtime_allm, mp); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + } + + // Mark gp ready to run. +@@ -808,7 +808,7 @@ + + // Figure out how many CPUs to use during GC. + // Limited by gomaxprocs, number of actual CPUs, and MaxGcproc. +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + n = runtime_gomaxprocs; + if(n > runtime_ncpu) + n = runtime_ncpu > 0 ? runtime_ncpu : 1; +@@ -816,7 +816,7 @@ + n = MaxGcproc; + if(n > runtime_sched.nmidle+1) // one M is currently running + n = runtime_sched.nmidle+1; +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + return n; + } + +@@ -825,14 +825,14 @@ + { + int32 n; + +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + n = runtime_gomaxprocs; + if(n > runtime_ncpu) + n = runtime_ncpu; + if(n > MaxGcproc) + n = MaxGcproc; + n -= runtime_sched.nmidle+1; // one M is currently running +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + return n > 0; + } + +@@ -842,7 +842,7 @@ + M *mp; + int32 n, pos; + +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + pos = 0; + for(n = 1; n < nproc; n++) { // one M is currently running + if(runtime_allp[pos]->mcache == m->mcache) +@@ -855,7 +855,7 @@ + pos++; + runtime_notewakeup(&mp->park); + } +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + } + + // Similar to stoptheworld but best-effort and can be called several times. +@@ -894,7 +894,7 @@ + P *p; + bool wait; + +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + runtime_sched.stopwait = runtime_gomaxprocs; + runtime_atomicstore((uint32*)&runtime_sched.gcwaiting, 1); + preemptall(); +@@ -914,7 +914,7 @@ + runtime_sched.stopwait--; + } + wait = runtime_sched.stopwait > 0; +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + + // wait for remaining P's to stop voluntarily + if(wait) { +@@ -948,7 +948,7 @@ + gp = runtime_netpoll(false); // non-blocking + injectglist(gp); + add = needaddgcproc(); +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + if(newprocs) { + procresize(newprocs); + newprocs = 0; +@@ -972,7 +972,7 @@ + runtime_sched.sysmonwait = false; + runtime_notewakeup(&runtime_sched.sysmonnote); + } +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + + while(p1) { + p = p1; +@@ -1404,9 +1404,9 @@ + } + + retry: +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + mput(m); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + runtime_notesleep(&m->park); + runtime_noteclear(&m->park); + if(m->helpgc) { +@@ -1433,18 +1433,18 @@ + M *mp; + void (*fn)(void); + +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + if(p == nil) { + p = pidleget(); + if(p == nil) { +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(spinning) + runtime_xadd(&runtime_sched.nmspinning, -1); + return; + } + } + mp = mget(); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(mp == nil) { + fn = nil; + if(spinning) +@@ -1477,28 +1477,28 @@ + startm(p, true); + return; + } +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + if(runtime_sched.gcwaiting) { + p->status = Pgcstop; + if(--runtime_sched.stopwait == 0) + runtime_notewakeup(&runtime_sched.stopnote); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + return; + } + if(runtime_sched.runqsize) { +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + startm(p, false); + return; + } + // If this is the last running P and nobody is polling network, + // need to wakeup another M to poll network. + if(runtime_sched.npidle == (uint32)runtime_gomaxprocs-1 && runtime_atomicload64(&runtime_sched.lastpoll) != 0) { +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + startm(p, false); + return; + } + pidleput(p); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + } + + // Tries to add one more P to execute G's. +@@ -1570,11 +1570,11 @@ + runtime_xadd(&runtime_sched.nmspinning, -1); + } + p = releasep(); +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + p->status = Pgcstop; + if(--runtime_sched.stopwait == 0) + runtime_notewakeup(&runtime_sched.stopnote); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + stopm(); + } + +@@ -1625,9 +1625,9 @@ + return gp; + // global runq + if(runtime_sched.runqsize) { +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + gp = globrunqget(m->p, 0); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(gp) + return gp; + } +@@ -1661,19 +1661,19 @@ + } + stop: + // return P and block +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + if(runtime_sched.gcwaiting) { +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + goto top; + } + if(runtime_sched.runqsize) { + gp = globrunqget(m->p, 0); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + return gp; + } + p = releasep(); + pidleput(p); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(m->spinning) { + m->spinning = false; + runtime_xadd(&runtime_sched.nmspinning, -1); +@@ -1682,9 +1682,9 @@ + for(i = 0; i < runtime_gomaxprocs; i++) { + p = runtime_allp[i]; + if(p && p->runqhead != p->runqtail) { +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + p = pidleget(); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(p) { + acquirep(p); + goto top; +@@ -1701,9 +1701,9 @@ + gp = runtime_netpoll(true); // block until new work is available + runtime_atomicstore64(&runtime_sched.lastpoll, runtime_nanotime()); + if(gp) { +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + p = pidleget(); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(p) { + acquirep(p); + injectglist(gp->schedlink); +@@ -1746,14 +1746,14 @@ + + if(glist == nil) + return; +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + for(n = 0; glist; n++) { + gp = glist; + glist = gp->schedlink; + gp->status = Grunnable; + globrunqput(gp); + } +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + + for(; n && runtime_sched.npidle; n--) + startm(nil, false); +@@ -1784,9 +1784,9 @@ + // This is a fancy way to say tick%61==0, + // it uses 2 MUL instructions instead of a single DIV and so is faster on modern processors. + if(tick - (((uint64)tick*0x4325c53fu)>>36)*61 == 0 && runtime_sched.runqsize > 0) { +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + gp = globrunqget(m->p, 1); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(gp) + resetspinning(); + } +@@ -1880,9 +1880,9 @@ + gp->status = Grunnable; + gp->m = nil; + m->curg = nil; +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + globrunqput(gp); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(m->lockedg) { + stoplockedm(); + execute(gp); // Never returns. +@@ -1985,24 +1985,24 @@ + g->status = Gsyscall; + + if(runtime_atomicload(&runtime_sched.sysmonwait)) { // TODO: fast atomic +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + if(runtime_atomicload(&runtime_sched.sysmonwait)) { + runtime_atomicstore(&runtime_sched.sysmonwait, 0); + runtime_notewakeup(&runtime_sched.sysmonnote); + } +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + } + + m->mcache = nil; + m->p->m = nil; + runtime_atomicstore(&m->p->status, Psyscall); + if(runtime_sched.gcwaiting) { +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + if (runtime_sched.stopwait > 0 && runtime_cas(&m->p->status, Psyscall, Pgcstop)) { + if(--runtime_sched.stopwait == 0) + runtime_notewakeup(&runtime_sched.stopnote); + } +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + } + + m->locks--; +@@ -2113,13 +2113,13 @@ + // Try to get any other idle P. + m->p = nil; + if(runtime_sched.pidle) { +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + p = pidleget(); + if(p && runtime_atomicload(&runtime_sched.sysmonwait)) { + runtime_atomicstore(&runtime_sched.sysmonwait, 0); + runtime_notewakeup(&runtime_sched.sysmonnote); + } +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(p) { + acquirep(p); + return true; +@@ -2138,7 +2138,7 @@ + gp->status = Grunnable; + gp->m = nil; + m->curg = nil; +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + p = pidleget(); + if(p == nil) + globrunqput(gp); +@@ -2146,7 +2146,7 @@ + runtime_atomicstore(&runtime_sched.sysmonwait, 0); + runtime_notewakeup(&runtime_sched.sysmonnote); + } +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + if(p) { + acquirep(p); + execute(gp); // Never returns. +@@ -2425,13 +2425,13 @@ + + if(n > MaxGomaxprocs) + n = MaxGomaxprocs; +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + ret = runtime_gomaxprocs; + if(n <= 0 || n == ret) { +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + return ret; + } +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + + runtime_semacquire(&runtime_worldsema, false); + m->gcing = 1; +@@ -2536,7 +2536,7 @@ + } + + static struct { +- Lock; ++ Lock lock; + void (*fn)(uintptr*, int32); + int32 hz; + uintptr pcbuf[TracebackMaxFrames]; +@@ -2568,9 +2568,9 @@ + if(mp->mcache == nil) + traceback = false; + +- runtime_lock(&prof); ++ runtime_lock(&prof.lock); + if(prof.fn == nil) { +- runtime_unlock(&prof); ++ runtime_unlock(&prof.lock); + mp->mallocing--; + return; + } +@@ -2598,7 +2598,7 @@ + prof.pcbuf[1] = (uintptr)System; + } + prof.fn(prof.pcbuf, n); +- runtime_unlock(&prof); ++ runtime_unlock(&prof.lock); + mp->mallocing--; + } + +@@ -2623,13 +2623,13 @@ + // it would deadlock. + runtime_resetcpuprofiler(0); + +- runtime_lock(&prof); ++ runtime_lock(&prof.lock); + prof.fn = fn; + prof.hz = hz; +- runtime_unlock(&prof); +- runtime_lock(&runtime_sched); ++ runtime_unlock(&prof.lock); ++ runtime_lock(&runtime_sched.lock); + runtime_sched.profilehz = hz; +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + + if(hz != 0) + runtime_resetcpuprofiler(hz); +@@ -2767,11 +2767,11 @@ + static void + incidlelocked(int32 v) + { +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + runtime_sched.nmidlelocked += v; + if(v > 0) + checkdead(); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + } + + // Check for deadlock situation. +@@ -2840,16 +2840,16 @@ + runtime_usleep(delay); + if(runtime_debug.schedtrace <= 0 && + (runtime_sched.gcwaiting || runtime_atomicload(&runtime_sched.npidle) == (uint32)runtime_gomaxprocs)) { // TODO: fast atomic +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + if(runtime_atomicload(&runtime_sched.gcwaiting) || runtime_atomicload(&runtime_sched.npidle) == (uint32)runtime_gomaxprocs) { + runtime_atomicstore(&runtime_sched.sysmonwait, 1); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + runtime_notesleep(&runtime_sched.sysmonnote); + runtime_noteclear(&runtime_sched.sysmonnote); + idle = 0; + delay = 20; + } else +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + } + // poll network if not polled for more than 10ms + lastpoll = runtime_atomicload64(&runtime_sched.lastpoll); +@@ -2978,7 +2978,7 @@ + if(starttime == 0) + starttime = now; + +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + runtime_printf("SCHED %Dms: gomaxprocs=%d idleprocs=%d threads=%d idlethreads=%d runqueue=%d", + (now-starttime)/1000000, runtime_gomaxprocs, runtime_sched.npidle, runtime_sched.mcount, + runtime_sched.nmidle, runtime_sched.runqsize); +@@ -3014,7 +3014,7 @@ + } + } + if(!detailed) { +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + return; + } + for(mp = runtime_allm; mp; mp = mp->alllink) { +@@ -3046,7 +3046,7 @@ + lockedm ? lockedm->id : -1); + } + runtime_unlock(&allglock); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + } + + // Put mp on midle list. +@@ -3202,9 +3202,9 @@ + for(i=0; ischedlink = batch[i+1]; + // Now put the batch on global queue. +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + globrunqputbatch(batch[0], batch[n], n+1); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + return true; + } + +@@ -3356,11 +3356,11 @@ + { + int32 out; + +- runtime_lock(&runtime_sched); ++ runtime_lock(&runtime_sched.lock); + out = runtime_sched.maxmcount; + runtime_sched.maxmcount = in; + checkmcount(); +- runtime_unlock(&runtime_sched); ++ runtime_unlock(&runtime_sched.lock); + return out; + } + +diff -r 225a208260a6 libgo/runtime/runtime.h +--- a/libgo/runtime/runtime.h Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/runtime.h Tue Sep 23 15:59:57 2014 -0700 +@@ -286,7 +286,7 @@ + + struct P + { +- Lock; ++ Lock lock; + + int32 id; + uint32 status; // one of Pidle/Prunning/... +@@ -384,7 +384,7 @@ + + struct Timers + { +- Lock; ++ Lock lock; + G *timerproc; + bool sleeping; + bool rescheduling; +diff -r 225a208260a6 libgo/runtime/sema.goc +--- a/libgo/runtime/sema.goc Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/sema.goc Tue Sep 23 15:59:57 2014 -0700 +@@ -35,7 +35,7 @@ + typedef struct SemaRoot SemaRoot; + struct SemaRoot + { +- Lock; ++ Lock lock; + SemaWaiter* head; + SemaWaiter* tail; + // Number of waiters. Read w/o the lock. +@@ -47,7 +47,7 @@ + + struct semtable + { +- SemaRoot; ++ SemaRoot root; + uint8 pad[CacheLineSize-sizeof(SemaRoot)]; + }; + static struct semtable semtable[SEMTABLESZ]; +@@ -55,7 +55,7 @@ + static SemaRoot* + semroot(uint32 volatile *addr) + { +- return &semtable[((uintptr)addr >> 3) % SEMTABLESZ]; ++ return &semtable[((uintptr)addr >> 3) % SEMTABLESZ].root; + } + + static void +@@ -124,19 +124,19 @@ + } + for(;;) { + +- runtime_lock(root); ++ runtime_lock(&root->lock); + // Add ourselves to nwait to disable "easy case" in semrelease. + runtime_xadd(&root->nwait, 1); + // Check cansemacquire to avoid missed wakeup. + if(cansemacquire(addr)) { + runtime_xadd(&root->nwait, -1); +- runtime_unlock(root); ++ runtime_unlock(&root->lock); + return; + } + // Any semrelease after the cansemacquire knows we're waiting + // (we set nwait above), so go to sleep. + semqueue(root, addr, &s); +- runtime_parkunlock(root, "semacquire"); ++ runtime_parkunlock(&root->lock, "semacquire"); + if(cansemacquire(addr)) { + if(t0) + runtime_blockevent(s.releasetime - t0, 3); +@@ -161,11 +161,11 @@ + return; + + // Harder case: search for a waiter and wake it. +- runtime_lock(root); ++ runtime_lock(&root->lock); + if(runtime_atomicload(&root->nwait) == 0) { + // The count is already consumed by another goroutine, + // so no need to wake up another goroutine. +- runtime_unlock(root); ++ runtime_unlock(&root->lock); + return; + } + for(s = root->head; s; s = s->next) { +@@ -175,7 +175,7 @@ + break; + } + } +- runtime_unlock(root); ++ runtime_unlock(&root->lock); + if(s) { + if(s->releasetime) + s->releasetime = runtime_cputicks(); +@@ -211,7 +211,7 @@ + typedef struct SyncSema SyncSema; + struct SyncSema + { +- Lock; ++ Lock lock; + SemaWaiter* head; + SemaWaiter* tail; + }; +@@ -238,7 +238,7 @@ + w.releasetime = -1; + } + +- runtime_lock(s); ++ runtime_lock(&s->lock); + if(s->head && s->head->nrelease > 0) { + // have pending release, consume it + wake = nil; +@@ -249,7 +249,7 @@ + if(s->head == nil) + s->tail = nil; + } +- runtime_unlock(s); ++ runtime_unlock(&s->lock); + if(wake) + runtime_ready(wake->g); + } else { +@@ -259,7 +259,7 @@ + else + s->tail->next = &w; + s->tail = &w; +- runtime_parkunlock(s, "semacquire"); ++ runtime_parkunlock(&s->lock, "semacquire"); + if(t0) + runtime_blockevent(w.releasetime - t0, 2); + } +@@ -274,7 +274,7 @@ + w.next = nil; + w.releasetime = 0; + +- runtime_lock(s); ++ runtime_lock(&s->lock); + while(w.nrelease > 0 && s->head && s->head->nrelease < 0) { + // have pending acquire, satisfy it + wake = s->head; +@@ -293,7 +293,7 @@ + else + s->tail->next = &w; + s->tail = &w; +- runtime_parkunlock(s, "semarelease"); ++ runtime_parkunlock(&s->lock, "semarelease"); + } else +- runtime_unlock(s); ++ runtime_unlock(&s->lock); + } +diff -r 225a208260a6 libgo/runtime/sigqueue.goc +--- a/libgo/runtime/sigqueue.goc Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/sigqueue.goc Tue Sep 23 15:59:57 2014 -0700 +@@ -32,7 +32,7 @@ + #include "defs.h" + + static struct { +- Note; ++ Note note; + uint32 mask[(NSIG+31)/32]; + uint32 wanted[(NSIG+31)/32]; + uint32 state; +@@ -70,7 +70,7 @@ + new = HASSIGNAL; + if(runtime_cas(&sig.state, old, new)) { + if (old == HASWAITER) +- runtime_notewakeup(&sig); ++ runtime_notewakeup(&sig.note); + break; + } + } +@@ -107,8 +107,8 @@ + new = HASWAITER; + if(runtime_cas(&sig.state, old, new)) { + if (new == HASWAITER) { +- runtime_notetsleepg(&sig, -1); +- runtime_noteclear(&sig); ++ runtime_notetsleepg(&sig.note, -1); ++ runtime_noteclear(&sig.note); + } + break; + } +@@ -138,7 +138,7 @@ + // to use for initialization. It does not pass + // signal information in m. + sig.inuse = true; // enable reception of signals; cannot disable +- runtime_noteclear(&sig); ++ runtime_noteclear(&sig.note); + return; + } + +diff -r 225a208260a6 libgo/runtime/time.goc +--- a/libgo/runtime/time.goc Mon Sep 22 14:14:24 2014 -0700 ++++ b/libgo/runtime/time.goc Tue Sep 23 15:59:57 2014 -0700 +@@ -94,17 +94,17 @@ + t.period = 0; + t.fv = &readyv; + t.arg.__object = g; +- runtime_lock(&timers); ++ runtime_lock(&timers.lock); + addtimer(&t); +- runtime_parkunlock(&timers, reason); ++ runtime_parkunlock(&timers.lock, reason); + } + + void + runtime_addtimer(Timer *t) + { +- runtime_lock(&timers); ++ runtime_lock(&timers.lock); + addtimer(t); +- runtime_unlock(&timers); ++ runtime_unlock(&timers.lock); + } + + // Add a timer to the heap and start or kick the timer proc +@@ -169,14 +169,14 @@ + i = t->i; + gi = i; + +- runtime_lock(&timers); ++ runtime_lock(&timers.lock); + + // t may not be registered anymore and may have + // a bogus i (typically 0, if generated by Go). + // Verify it before proceeding. + i = t->i; + if(i < 0 || i >= timers.len || timers.t[i] != t) { +- runtime_unlock(&timers); ++ runtime_unlock(&timers.lock); + return false; + } + +@@ -192,7 +192,7 @@ + } + if(debug) + dumptimers("deltimer"); +- runtime_unlock(&timers); ++ runtime_unlock(&timers.lock); + return true; + } + +@@ -210,7 +210,7 @@ + Eface arg; + + for(;;) { +- runtime_lock(&timers); ++ runtime_lock(&timers.lock); + timers.sleeping = false; + now = runtime_nanotime(); + for(;;) { +@@ -236,7 +236,7 @@ + fv = t->fv; + f = (void*)t->fv->fn; + arg = t->arg; +- runtime_unlock(&timers); ++ runtime_unlock(&timers.lock); + if(raceenabled) + runtime_raceacquire(t); + __go_set_closure(fv); +@@ -249,20 +249,20 @@ + arg.__object = nil; + USED(&arg); + +- runtime_lock(&timers); ++ runtime_lock(&timers.lock); + } + if(delta < 0) { + // No timers left - put goroutine to sleep. + timers.rescheduling = true; + runtime_g()->isbackground = true; +- runtime_parkunlock(&timers, "timer goroutine (idle)"); ++ runtime_parkunlock(&timers.lock, "timer goroutine (idle)"); + runtime_g()->isbackground = false; + continue; + } + // At least one timer pending. Sleep until then. + timers.sleeping = true; + runtime_noteclear(&timers.waitnote); +- runtime_unlock(&timers); ++ runtime_unlock(&timers.lock); + runtime_notetsleepg(&timers.waitnote, delta); + } + } Index: tools/llgo/llgo-go.sh =================================================================== --- /dev/null +++ tools/llgo/llgo-go.sh @@ -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 Index: tools/llgo/ssaopt/esc.go =================================================================== --- /dev/null +++ tools/llgo/ssaopt/esc.go @@ -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) + } + } + } +} Index: tools/llgo/test/CMakeLists.txt =================================================================== --- /dev/null +++ tools/llgo/test/CMakeLists.txt @@ -0,0 +1,9 @@ +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 llgo libgo) +set_target_properties(check-llgo PROPERTIES FOLDER "Tests") Index: tools/llgo/test/debuginfo/emptyname.go =================================================================== --- /dev/null +++ tools/llgo/test/debuginfo/emptyname.go @@ -0,0 +1,7 @@ +// RUN: llgo -c -o /dev/null -g %s + +package main + +//line :1 +func main() { +} Index: tools/llgo/test/driver/parse-arguments.go =================================================================== --- /dev/null +++ tools/llgo/test/driver/parse-arguments.go @@ -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' Index: tools/llgo/test/execution/Inputs/init2.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/Inputs/init2.go @@ -0,0 +1,5 @@ +package main + +func init() { + println("do some other stuff before main") +} Index: tools/llgo/test/execution/arrays/compare.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/arrays/compare.go @@ -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) +} Index: tools/llgo/test/execution/arrays/index.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/arrays/index.go @@ -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() +} Index: tools/llgo/test/execution/arrays/range.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/arrays/range.go @@ -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) + } +} Index: tools/llgo/test/execution/arrays/slice.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/arrays/slice.go @@ -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)) +} Index: tools/llgo/test/execution/assignment/arrays.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/assignment/arrays.go @@ -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]) +} Index: tools/llgo/test/execution/assignment/binop.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/assignment/binop.go @@ -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) +} Index: tools/llgo/test/execution/assignment/dereferencing.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/assignment/dereferencing.go @@ -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) +} Index: tools/llgo/test/execution/assignment/multi.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/assignment/multi.go @@ -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) +} Index: tools/llgo/test/execution/assignment/namedresult.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/assignment/namedresult.go @@ -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) +} Index: tools/llgo/test/execution/branching/goto.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/branching/goto.go @@ -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 +} Index: tools/llgo/test/execution/branching/labeled.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/branching/labeled.go @@ -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() +} Index: tools/llgo/test/execution/chan/buffered.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/chan/buffered.go @@ -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) +} Index: tools/llgo/test/execution/chan/range.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/chan/range.go @@ -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) + } +} Index: tools/llgo/test/execution/chan/select.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/chan/select.go @@ -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() +} Index: tools/llgo/test/execution/chan/self.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/chan/self.go @@ -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) +} Index: tools/llgo/test/execution/circulartype.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/circulartype.go @@ -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 +} Index: tools/llgo/test/execution/closures/basic.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/closures/basic.go @@ -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")) +} Index: tools/llgo/test/execution/closures/issue176.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/closures/issue176.go @@ -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) +} Index: tools/llgo/test/execution/complex.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/complex.go @@ -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) +} Index: tools/llgo/test/execution/const.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/const.go @@ -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() +} Index: tools/llgo/test/execution/conversions/complex.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/conversions/complex.go @@ -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) +} Index: tools/llgo/test/execution/conversions/float.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/conversions/float.go @@ -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)) +} Index: tools/llgo/test/execution/conversions/int.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/conversions/int.go @@ -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) +} Index: tools/llgo/test/execution/conversions/sameunderlying.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/conversions/sameunderlying.go @@ -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 +} Index: tools/llgo/test/execution/defer.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/defer.go @@ -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() +} Index: tools/llgo/test/execution/errors/recover.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/errors/recover.go @@ -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) +} Index: tools/llgo/test/execution/for/branch.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/for/branch.go @@ -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("!") + } + } +} Index: tools/llgo/test/execution/fun.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/fun.go @@ -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) +} Index: tools/llgo/test/execution/functions/compare.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/functions/compare.go @@ -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) +} Index: tools/llgo/test/execution/functions/multivalue.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/functions/multivalue.go @@ -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)) +} Index: tools/llgo/test/execution/functions/unreachable.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/functions/unreachable.go @@ -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()) +} Index: tools/llgo/test/execution/go.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/go.go @@ -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") +} Index: tools/llgo/test/execution/if/lazy.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/if/lazy.go @@ -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()) +} Index: tools/llgo/test/execution/init.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/init.go @@ -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") +} Index: tools/llgo/test/execution/interfaces/assert.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/assert.go @@ -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("!") + } +} Index: tools/llgo/test/execution/interfaces/basic.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/basic.go @@ -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)) +} Index: tools/llgo/test/execution/interfaces/comparei2i.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/comparei2i.go @@ -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) +} Index: tools/llgo/test/execution/interfaces/comparei2v.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/comparei2v.go @@ -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) +} Index: tools/llgo/test/execution/interfaces/e2i_conversion.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/e2i_conversion.go @@ -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) +} Index: tools/llgo/test/execution/interfaces/embedded.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/embedded.go @@ -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() +} Index: tools/llgo/test/execution/interfaces/error.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/error.go @@ -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()) +} Index: tools/llgo/test/execution/interfaces/i2i_conversion.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/i2i_conversion.go @@ -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()) +} Index: tools/llgo/test/execution/interfaces/import.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/import.go @@ -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) +} Index: tools/llgo/test/execution/interfaces/methods.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/methods.go @@ -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()) +} Index: tools/llgo/test/execution/interfaces/static_conversion.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/static_conversion.go @@ -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()) +} Index: tools/llgo/test/execution/interfaces/wordsize.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/interfaces/wordsize.go @@ -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() +} Index: tools/llgo/test/execution/literals/array.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/literals/array.go @@ -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) + } +} Index: tools/llgo/test/execution/literals/func.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/literals/func.go @@ -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) +} Index: tools/llgo/test/execution/literals/map.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/literals/map.go @@ -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]) +} Index: tools/llgo/test/execution/literals/slice.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/literals/slice.go @@ -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) +} Index: tools/llgo/test/execution/literals/struct.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/literals/struct.go @@ -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) + } +} Index: tools/llgo/test/execution/maps/delete.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/maps/delete.go @@ -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]) +} Index: tools/llgo/test/execution/maps/insert.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/maps/insert.go @@ -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]) + } +} Index: tools/llgo/test/execution/maps/lookup.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/maps/lookup.go @@ -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) +} Index: tools/llgo/test/execution/maps/range.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/maps/range.go @@ -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) +} Index: tools/llgo/test/execution/methods/methodvalues.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/methods/methodvalues.go @@ -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() +} Index: tools/llgo/test/execution/methods/nilrecv.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/methods/nilrecv.go @@ -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() +} Index: tools/llgo/test/execution/methods/selectors.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/methods/selectors.go @@ -0,0 +1,45 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: F1 +// CHECK-NEXT: F2 +// CHECK-NEXT: F1 +// CHECK-NEXT: F2 + +package main + +type S1 struct{} +type S2 struct { + S1 +} + +func (s S1) F1() { + println("F1") +} + +func (s *S2) F2() { + println("F2") +} + +func testUnnamedStructMethods() { + // Test method lookup on an unnamed struct type. + var x struct { + S1 + S2 + } + x.F1() + x.F2() +} + +func main() { + var s S2 + + // Derive pointer-receiver function. + f1 := (*S2).F1 + f1(&s) + + f2 := (*S2).F2 + f2(&s) + + testUnnamedStructMethods() +} Index: tools/llgo/test/execution/new.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/new.go @@ -0,0 +1,17 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 0 +// CHECK-NEXT: 2 +// CHECK-NEXT: 4 + +package main + +func main() { + x := new(int) + println(*x) + *x = 2 + println(*x) + *x = *x * *x + println(*x) +} Index: tools/llgo/test/execution/nil.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/nil.go @@ -0,0 +1,32 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 0x0 +// CHECK-NEXT: x is nil +// CHECK-NEXT: y is nil +// CHECK-NEXT: z is nil + +package main + +func main() { + var x *int = nil + println(x) + + if x == nil { + println("x is nil") + } + + var y interface{} + var z interface{} = y + if y == nil { + println("y is nil") + } else { + println("y is not nil") + } + + if z == nil { + println("z is nil") + } else { + println("z is not nil") + } +} Index: tools/llgo/test/execution/operators/basics.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/operators/basics.go @@ -0,0 +1,133 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 4096 +// CHECK-NEXT: 256 +// CHECK-NEXT: 0 +// CHECK-NEXT: 0 +// CHECK-NEXT: 4096 +// CHECK-NEXT: 256 +// CHECK-NEXT: 0 +// CHECK-NEXT: 0 +// CHECK-NEXT: 65280 +// CHECK-NEXT: 0 +// CHECK-NEXT: 0 +// CHECK-NEXT: 0 +// CHECK-NEXT: 61184 +// CHECK-NEXT: 65024 +// CHECK-NEXT: 65296 +// CHECK-NEXT: 65281 +// CHECK-NEXT: 61184 +// CHECK-NEXT: 65024 +// CHECK-NEXT: 65296 +// CHECK-NEXT: 65281 +// CHECK-NEXT: -62 +// CHECK-NEXT: -246 +// CHECK-NEXT: +1.224560e+002 +// CHECK-NEXT: 3 +// CHECK-NEXT: 3 +// CHECK-NEXT: 4 +// CHECK-NEXT: 122 +// CHECK-NEXT: -124 +// CHECK-NEXT: 120 +// CHECK-NEXT: 18446744073709547520 +// CHECK-NEXT: false +// CHECK-NEXT: 2147483648 +// CHECK-NEXT: 9223372036854775808 +// CHECK-NEXT: 2147483648 2147483648 +// CHECK-NEXT: 9223372036854775808 9223372036854775808 + +package main + +import "unsafe" + +var global string + +var hi = 0xFF00 +var lo = 0xFF00 + +// borrowed from math package to avoid dependency on standard library +func float32bits(f float32) uint32 { return *(*uint32)(unsafe.Pointer(&f)) } +func float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) } + +func main() { + println(hi & 0x1000) + println(hi & 0x0100) + println(hi & 0x0010) + println(hi & 0x0001) + println(lo & 0x1000) + println(lo & 0x0100) + println(lo & 0x0010) + println(lo & 0x0001) + println(hi | lo) + println(hi ^ hi) + println(hi ^ lo) + println(lo ^ lo) + println(hi ^ 0x1000) + println(hi ^ 0x0100) + println(hi ^ 0x0010) + println(hi ^ 0x0001) + println(lo ^ 0x1000) + println(lo ^ 0x0100) + println(lo ^ 0x0010) + println(lo ^ 0x0001) + println(-123 >> 1) + println(-123 << 1) + + var f float64 = 123.456 + f-- + println(f) + + // context of '&' op is used to type the untyped lhs + // operand of the shift expression. + shift := uint(2) + println(uint64(0xFFFFFFFF) & (1<> (63 - _uint) + if x == 2<<_uint { + println("!") + } + } + + // There was a bug related to compound expressions involving + // multiple binary logical operators. + var a, b, c int + if a == 0 && (b != 0 || c != 0) { + println("!") + } + + var si int = -123 + var ui int = 123 + println(^si) + println(^ui) + println(ui &^ 3) + + // test case from math/modf.go + var x uint64 = 0xFFFFFFFFFFFFFFFF + var e uint = 40 + x &^= 1<<(64-12-e) - 1 + println(x) + + // compare global to non-global + println(new(string) == &global) + + // negative zero + var f32 float32 + var f64 float64 + var c64 complex64 + var c128 complex128 + f32 = -f32 + f64 = -f64 + c64 = -c64 + c128 = -c128 + println(float32bits(f32)) + println(float64bits(f64)) + println(float32bits(real(c64)), float32bits(imag(c64))) + println(float64bits(real(c128)), float64bits(imag(c128))) +} Index: tools/llgo/test/execution/operators/binary_untyped.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/operators/binary_untyped.go @@ -0,0 +1,17 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: true + +package main + +func f1(b bool) bool { + return b +} + +func main() { + x := false + y := x + x = !y + println(x || y) +} Index: tools/llgo/test/execution/operators/shifts.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/operators/shifts.go @@ -0,0 +1,290 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 4294967295 +// CHECK-NEXT: 4294967295 +// CHECK-NEXT: 2147483647 +// CHECK-NEXT: 4294967294 +// CHECK-NEXT: 1073741823 +// CHECK-NEXT: 4294967292 +// CHECK-NEXT: 536870911 +// CHECK-NEXT: 4294967288 +// CHECK-NEXT: 268435455 +// CHECK-NEXT: 4294967280 +// CHECK-NEXT: 134217727 +// CHECK-NEXT: 4294967264 +// CHECK-NEXT: 67108863 +// CHECK-NEXT: 4294967232 +// CHECK-NEXT: 33554431 +// CHECK-NEXT: 4294967168 +// CHECK-NEXT: 16777215 +// CHECK-NEXT: 4294967040 +// CHECK-NEXT: 8388607 +// CHECK-NEXT: 4294966784 +// CHECK-NEXT: 4194303 +// CHECK-NEXT: 4294966272 +// CHECK-NEXT: 2097151 +// CHECK-NEXT: 4294965248 +// CHECK-NEXT: 1048575 +// CHECK-NEXT: 4294963200 +// CHECK-NEXT: 524287 +// CHECK-NEXT: 4294959104 +// CHECK-NEXT: 262143 +// CHECK-NEXT: 4294950912 +// CHECK-NEXT: 131071 +// CHECK-NEXT: 4294934528 +// CHECK-NEXT: 65535 +// CHECK-NEXT: 4294901760 +// CHECK-NEXT: 32767 +// CHECK-NEXT: 4294836224 +// CHECK-NEXT: 16383 +// CHECK-NEXT: 4294705152 +// CHECK-NEXT: 8191 +// CHECK-NEXT: 4294443008 +// CHECK-NEXT: 4095 +// CHECK-NEXT: 4293918720 +// CHECK-NEXT: 2047 +// CHECK-NEXT: 4292870144 +// CHECK-NEXT: 1023 +// CHECK-NEXT: 4290772992 +// CHECK-NEXT: 511 +// CHECK-NEXT: 4286578688 +// CHECK-NEXT: 255 +// CHECK-NEXT: 4278190080 +// CHECK-NEXT: 127 +// CHECK-NEXT: 4261412864 +// CHECK-NEXT: 63 +// CHECK-NEXT: 4227858432 +// CHECK-NEXT: 31 +// CHECK-NEXT: 4160749568 +// CHECK-NEXT: 15 +// CHECK-NEXT: 4026531840 +// CHECK-NEXT: 7 +// CHECK-NEXT: 3758096384 +// CHECK-NEXT: 3 +// CHECK-NEXT: 3221225472 +// CHECK-NEXT: 1 +// CHECK-NEXT: 2147483648 +// CHECK-NEXT: 0 +// CHECK-NEXT: 0 +// CHECK-NEXT: 4026531839 +// CHECK-NEXT: 4026531839 +// CHECK-NEXT: 2013265919 +// CHECK-NEXT: 3758096382 +// CHECK-NEXT: 1006632959 +// CHECK-NEXT: 3221225468 +// CHECK-NEXT: 503316479 +// CHECK-NEXT: 2147483640 +// CHECK-NEXT: 251658239 +// CHECK-NEXT: 4294967280 +// CHECK-NEXT: 125829119 +// CHECK-NEXT: 4294967264 +// CHECK-NEXT: 62914559 +// CHECK-NEXT: 4294967232 +// CHECK-NEXT: 31457279 +// CHECK-NEXT: 4294967168 +// CHECK-NEXT: 15728639 +// CHECK-NEXT: 4294967040 +// CHECK-NEXT: 7864319 +// CHECK-NEXT: 4294966784 +// CHECK-NEXT: 3932159 +// CHECK-NEXT: 4294966272 +// CHECK-NEXT: 1966079 +// CHECK-NEXT: 4294965248 +// CHECK-NEXT: 983039 +// CHECK-NEXT: 4294963200 +// CHECK-NEXT: 491519 +// CHECK-NEXT: 4294959104 +// CHECK-NEXT: 245759 +// CHECK-NEXT: 4294950912 +// CHECK-NEXT: 122879 +// CHECK-NEXT: 4294934528 +// CHECK-NEXT: 61439 +// CHECK-NEXT: 4294901760 +// CHECK-NEXT: 30719 +// CHECK-NEXT: 4294836224 +// CHECK-NEXT: 15359 +// CHECK-NEXT: 4294705152 +// CHECK-NEXT: 7679 +// CHECK-NEXT: 4294443008 +// CHECK-NEXT: 3839 +// CHECK-NEXT: 4293918720 +// CHECK-NEXT: 1919 +// CHECK-NEXT: 4292870144 +// CHECK-NEXT: 959 +// CHECK-NEXT: 4290772992 +// CHECK-NEXT: 479 +// CHECK-NEXT: 4286578688 +// CHECK-NEXT: 239 +// CHECK-NEXT: 4278190080 +// CHECK-NEXT: 119 +// CHECK-NEXT: 4261412864 +// CHECK-NEXT: 59 +// CHECK-NEXT: 4227858432 +// CHECK-NEXT: 29 +// CHECK-NEXT: 4160749568 +// CHECK-NEXT: 14 +// CHECK-NEXT: 4026531840 +// CHECK-NEXT: 7 +// CHECK-NEXT: 3758096384 +// CHECK-NEXT: 3 +// CHECK-NEXT: 3221225472 +// CHECK-NEXT: 1 +// CHECK-NEXT: 2147483648 +// CHECK-NEXT: 0 +// CHECK-NEXT: 0 +// CHECK-NEXT: -1 +// CHECK-NEXT: -1 +// CHECK-NEXT: -1 +// CHECK-NEXT: -2 +// CHECK-NEXT: -1 +// CHECK-NEXT: -4 +// CHECK-NEXT: -1 +// CHECK-NEXT: -8 +// CHECK-NEXT: -1 +// CHECK-NEXT: -16 +// CHECK-NEXT: -1 +// CHECK-NEXT: -32 +// CHECK-NEXT: -1 +// CHECK-NEXT: -64 +// CHECK-NEXT: -1 +// CHECK-NEXT: -128 +// CHECK-NEXT: -1 +// CHECK-NEXT: -256 +// CHECK-NEXT: -1 +// CHECK-NEXT: -512 +// CHECK-NEXT: -1 +// CHECK-NEXT: -1024 +// CHECK-NEXT: -1 +// CHECK-NEXT: -2048 +// CHECK-NEXT: -1 +// CHECK-NEXT: -4096 +// CHECK-NEXT: -1 +// CHECK-NEXT: -8192 +// CHECK-NEXT: -1 +// CHECK-NEXT: -16384 +// CHECK-NEXT: -1 +// CHECK-NEXT: -32768 +// CHECK-NEXT: -1 +// CHECK-NEXT: -65536 +// CHECK-NEXT: -1 +// CHECK-NEXT: -131072 +// CHECK-NEXT: -1 +// CHECK-NEXT: -262144 +// CHECK-NEXT: -1 +// CHECK-NEXT: -524288 +// CHECK-NEXT: -1 +// CHECK-NEXT: -1048576 +// CHECK-NEXT: -1 +// CHECK-NEXT: -2097152 +// CHECK-NEXT: -1 +// CHECK-NEXT: -4194304 +// CHECK-NEXT: -1 +// CHECK-NEXT: -8388608 +// CHECK-NEXT: -1 +// CHECK-NEXT: -16777216 +// CHECK-NEXT: -1 +// CHECK-NEXT: -33554432 +// CHECK-NEXT: -1 +// CHECK-NEXT: -67108864 +// CHECK-NEXT: -1 +// CHECK-NEXT: -134217728 +// CHECK-NEXT: -1 +// CHECK-NEXT: -268435456 +// CHECK-NEXT: -1 +// CHECK-NEXT: -536870912 +// CHECK-NEXT: -1 +// CHECK-NEXT: -1073741824 +// CHECK-NEXT: -1 +// CHECK-NEXT: -2147483648 +// CHECK-NEXT: -1 +// CHECK-NEXT: 0 +// CHECK-NEXT: 1 +// CHECK-NEXT: 1 +// CHECK-NEXT: 0 +// CHECK-NEXT: 2 +// CHECK-NEXT: 0 +// CHECK-NEXT: 4 +// CHECK-NEXT: 0 +// CHECK-NEXT: 8 +// CHECK-NEXT: 0 +// CHECK-NEXT: 16 +// CHECK-NEXT: 0 +// CHECK-NEXT: 32 +// CHECK-NEXT: 0 +// CHECK-NEXT: 64 +// CHECK-NEXT: 0 +// CHECK-NEXT: 128 +// CHECK-NEXT: 0 +// CHECK-NEXT: 256 +// CHECK-NEXT: 0 +// CHECK-NEXT: 512 +// CHECK-NEXT: 0 +// CHECK-NEXT: 1024 +// CHECK-NEXT: 0 +// CHECK-NEXT: 2048 +// CHECK-NEXT: 0 +// CHECK-NEXT: 4096 +// CHECK-NEXT: 0 +// CHECK-NEXT: 8192 +// CHECK-NEXT: 0 +// CHECK-NEXT: 16384 +// CHECK-NEXT: 0 +// CHECK-NEXT: 32768 +// CHECK-NEXT: 0 +// CHECK-NEXT: 65536 +// CHECK-NEXT: 0 +// CHECK-NEXT: 131072 +// CHECK-NEXT: 0 +// CHECK-NEXT: 262144 +// CHECK-NEXT: 0 +// CHECK-NEXT: 524288 +// CHECK-NEXT: 0 +// CHECK-NEXT: 1048576 +// CHECK-NEXT: 0 +// CHECK-NEXT: 2097152 +// CHECK-NEXT: 0 +// CHECK-NEXT: 4194304 +// CHECK-NEXT: 0 +// CHECK-NEXT: 8388608 +// CHECK-NEXT: 0 +// CHECK-NEXT: 16777216 +// CHECK-NEXT: 0 +// CHECK-NEXT: 33554432 +// CHECK-NEXT: 0 +// CHECK-NEXT: 67108864 +// CHECK-NEXT: 0 +// CHECK-NEXT: 134217728 +// CHECK-NEXT: 0 +// CHECK-NEXT: 268435456 +// CHECK-NEXT: 0 +// CHECK-NEXT: 536870912 +// CHECK-NEXT: 0 +// CHECK-NEXT: 1073741824 +// CHECK-NEXT: 0 +// CHECK-NEXT: -2147483648 +// CHECK-NEXT: 0 +// CHECK-NEXT: 0 + +package main + +func testShrUint32(v uint32) { + for i := uint(0); i <= 32; i++ { + println(v >> i) + println(v << i) + } +} + +func testShrInt32(v int32) { + for i := uint(0); i <= 32; i++ { + println(v >> i) + println(v << i) + } +} + +func main() { + testShrUint32(0xFFFFFFFF) + testShrUint32(0xEFFFFFFF) + testShrInt32(-1) + testShrInt32(1) +} Index: tools/llgo/test/execution/slices/append.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/slices/append.go @@ -0,0 +1,254 @@ +// 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: 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: 1 +// CHECK-NEXT: 2 +// CHECK-NEXT: 3 +// CHECK-NEXT: abcdef +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: false false +// CHECK-NEXT: true true +// CHECK-NEXT: false false + +package main + +func stringtobytes() { + var b []byte + b = append(b, "abc"...) + b = append(b, "def"...) + println(string(b)) +} + +func appendnothing() { + var x []string + println(append(x) == nil) + x = append(x, "!") + println(len(append(x)) == 1) +} + +func appendmulti() { + a := append([]bool{}, []bool{false, true, false}...) + b := append([]bool{}, false, true, false) + for i := range a { + println(a[i], b[i]) + } +} + +func main() { + x := []int{} + for i := 0; i < 100; i++ { + x = append(x, i) + } + for i := 0; i < len(x); i++ { + println(x[i]) + } + y := []int{1, 2, 3} + x = append(x, y...) + for i := 0; i < len(x); i++ { + println(x[i]) + } + stringtobytes() + appendnothing() + appendmulti() +} Index: tools/llgo/test/execution/slices/cap.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/slices/cap.go @@ -0,0 +1,50 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 0 0 +// CHECK-NEXT: 0 0 +// CHECK-NEXT: 0 0 +// CHECK-NEXT: 1 1 +// CHECK-NEXT: 1 1 +// CHECK-NEXT: 1 2 +// CHECK-NEXT: 2 9 +// CHECK-NEXT: 3 9 +// CHECK-NEXT: 999 +// CHECK-NEXT: 999 +// CHECK-NEXT: 1 2 + +package main + +func test(l, c int) { + var s []int + if l != -1 { + if c == -1 { + s = make([]int, l) + } else { + s = make([]int, l, c) + } + } + println(len(s), cap(s)) +} + +func main() { + test(-1, -1) + test(0, -1) + test(0, 0) + test(1, -1) + test(1, 1) + test(1, 2) + + // make sure capacity is transferred to slice + s := make([]int, 5, 10) + s1 := s[1:3] + println(len(s1), cap(s1)) + + s2 := append(s1, 999) + println(len(s2), cap(s2)) + println(s2[2]) + println(s[3]) + + s3 := s1[0:1:2] + println(len(s3), cap(s3)) +} Index: tools/llgo/test/execution/slices/compare.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/slices/compare.go @@ -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 s []int + println(s == nil) + println(s != nil) + println(nil == s) + println(nil != s) + s = make([]int, 0) + println(s == nil) + println(s != nil) + println(nil == s) + println(nil != s) +} Index: tools/llgo/test/execution/slices/copy.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/slices/copy.go @@ -0,0 +1,17 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 5 +// CHECK-NEXT: 0 + +package main + +func main() { + a := make([]int, 10) + b := make([]int, 10) + for i, _ := range b { + b[i] = 1 + } + println(copy(a[:5], b)) // expect 5 + println(a[5]) // expect 0 +} Index: tools/llgo/test/execution/slices/index.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/slices/index.go @@ -0,0 +1,14 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 0 + +package main + +func blah() []int { + return make([]int, 1) +} + +func main() { + println(blah()[0]) +} Index: tools/llgo/test/execution/slices/literal.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/slices/literal.go @@ -0,0 +1,15 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 1 +// CHECK-NEXT: 2 +// CHECK-NEXT: 3 + +package main + +func main() { + x := []int{1, 2, 3} + for i := 0; i < len(x); i++ { + println(x[i]) + } +} Index: tools/llgo/test/execution/slices/make.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/slices/make.go @@ -0,0 +1,23 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 0 0 +// CHECK-NEXT: 1 0 +// CHECK-NEXT: 2 0 +// CHECK-NEXT: 3 0 +// CHECK-NEXT: 4 0 +// CHECK-NEXT: 5 666 +// CHECK-NEXT: 6 0 +// CHECK-NEXT: 7 0 +// CHECK-NEXT: 8 0 +// CHECK-NEXT: 9 0 + +package main + +func main() { + x := make([]int, 10) + x[5] = 666 + for i, val := range x { + println(i, val) + } +} Index: tools/llgo/test/execution/slices/sliceexpr.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/slices/sliceexpr.go @@ -0,0 +1,39 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: a +// CHECK-NEXT: 0 2 +// CHECK-NEXT: 1 3 +// CHECK-NEXT: b +// CHECK-NEXT: 0 3 +// CHECK-NEXT: 1 4 +// CHECK-NEXT: c +// CHECK-NEXT: 0 1 +// CHECK-NEXT: 1 2 +// CHECK-NEXT: d +// CHECK-NEXT: 0 1 +// CHECK-NEXT: 1 2 +// CHECK-NEXT: 2 3 +// CHECK-NEXT: 3 4 + +package main + +func main() { + x := []int{1, 2, 3, 4} + println("a") + for i, val := range x[1:3] { + println(i, val) + } + println("b") + for i, val := range x[2:] { + println(i, val) + } + println("c") + for i, val := range x[:2] { + println(i, val) + } + println("d") + for i, val := range x[:] { + println(i, val) + } +} Index: tools/llgo/test/execution/strings/add.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/strings/add.go @@ -0,0 +1,15 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 3 3 6 +// CHECK-NEXT: abc123 + +package main + +func main() { + a := "abc" + b := "123" + c := a + b + println(len(a), len(b), len(c)) + println(c) +} Index: tools/llgo/test/execution/strings/bytes.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/strings/bytes.go @@ -0,0 +1,41 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: testBytesConversion: true +// CHECK-NEXT: 97 +// CHECK-NEXT: 98 +// CHECK-NEXT: 99 +// CHECK-NEXT: abc +// CHECK-NEXT: !bc +// CHECK-NEXT: testBytesCopy: true + +package main + +type namedByte byte + +func testBytesConversion() { + s := "abc" + b := []byte(s) + println("testBytesConversion:", s == string(b)) + nb := []namedByte(s) + for _, v := range nb { + println(v) + } + b[0] = '!' + println(s) + s = string(b) + b[0] = 'a' + println(s) +} + +func testBytesCopy() { + s := "abc" + b := make([]byte, len(s)) + copy(b, s) + println("testBytesCopy:", string(b) == s) +} + +func main() { + testBytesConversion() + testBytesCopy() +} Index: tools/llgo/test/execution/strings/compare.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/strings/compare.go @@ -0,0 +1,54 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: true +// CHECK-NEXT: false +// CHECK-NEXT: false +// CHECK-NEXT: true +// CHECK-NEXT: false +// CHECK-NEXT: true +// CHECK-NEXT: false +// CHECK-NEXT: false +// CHECK-NEXT: false +// CHECK-NEXT: true +// CHECK-NEXT: false +// CHECK-NEXT: false +// CHECK-NEXT: true +// CHECK-NEXT: false +// CHECK-NEXT: false +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true + +package main + +func main() { + x := "abc" + y := "def" + z := "abcd" + + println(x == x) // true + println(x == y) // false + println(x != x) // false + println(x != y) // true + println(x < x) // false + println(x < y) // true + println(y < x) // false + println(x > x) // false + println(x > y) // false + println(y > x) // true + + println(x == z) // false + println(z == x) // false + println(x < z) // true + println(x > z) // false + println(z < x) // false + println(z > x) // true + + println(x <= x) // true + println(x <= y) // true + println(x >= x) // true + println(y >= x) // true +} Index: tools/llgo/test/execution/strings/index.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/strings/index.go @@ -0,0 +1,11 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 97 98 99 + +package main + +func main() { + s := "abc" + println(s[0], s[1], s[2]) +} Index: tools/llgo/test/execution/strings/range.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/strings/range.go @@ -0,0 +1,86 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 0 46 1 +// CHECK-NEXT: 0 46 +// CHECK-NEXT: 0 169 1 +// CHECK-NEXT: 0 169 +// CHECK-NEXT: 0 8364 1 +// CHECK-NEXT: 0 8364 +// CHECK-NEXT: 0 66560 1 +// CHECK-NEXT: 0 66560 +// CHECK-NEXT: 0 83 1 +// CHECK-NEXT: 1 97 2 +// CHECK-NEXT: 2 108 3 +// CHECK-NEXT: 3 101 4 +// CHECK-NEXT: 4 32 5 +// CHECK-NEXT: 5 112 6 +// CHECK-NEXT: 6 114 7 +// CHECK-NEXT: 7 105 8 +// CHECK-NEXT: 8 99 9 +// CHECK-NEXT: 9 101 10 +// CHECK-NEXT: 10 58 11 +// CHECK-NEXT: 11 32 12 +// CHECK-NEXT: 12 8364 13 +// CHECK-NEXT: 15 48 14 +// CHECK-NEXT: 16 46 15 +// CHECK-NEXT: 17 57 16 +// CHECK-NEXT: 18 57 17 +// CHECK-NEXT: 0 83 +// CHECK-NEXT: 1 97 +// CHECK-NEXT: 2 108 +// CHECK-NEXT: 3 101 +// CHECK-NEXT: 4 32 +// CHECK-NEXT: 5 112 +// CHECK-NEXT: 6 114 +// CHECK-NEXT: 7 105 +// CHECK-NEXT: 8 99 +// CHECK-NEXT: 9 101 +// CHECK-NEXT: 10 58 +// CHECK-NEXT: 11 32 +// CHECK-NEXT: 12 8364 +// CHECK-NEXT: 15 48 +// CHECK-NEXT: 16 46 +// CHECK-NEXT: 17 57 +// CHECK-NEXT: 18 57 + +package main + +func printchars(s string) { + var x int + for i, c := range s { + // test loop-carried dependence (x++), introducing a Phi node + x++ + println(i, c, x) + } + + // now test with plain old assignment + var i int + var c rune + for i, c = range s { + println(i, c) + if i == len(s)-1 { + // test multiple branches to loop header + continue + } + } +} + +func main() { + // 1 bytes + printchars(".") + + // 2 bytes + printchars("©") + + // 3 bytes + printchars("€") + + // 4 bytes + printchars("𐐀") + + // mixed + printchars("Sale price: €0.99") + + // TODO add test cases for invalid sequences +} Index: tools/llgo/test/execution/strings/runetostring.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/strings/runetostring.go @@ -0,0 +1,74 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: test( 46 ) +// CHECK-NEXT: . +// CHECK-NEXT: 46 +// CHECK-NEXT: 0 46 +// CHECK-NEXT: test( 169 ) +// CHECK-NEXT: © +// CHECK-NEXT: 194 +// CHECK-NEXT: 169 +// CHECK-NEXT: 0 169 +// CHECK-NEXT: test( 8364 ) +// CHECK-NEXT: € +// CHECK-NEXT: 226 +// CHECK-NEXT: 130 +// CHECK-NEXT: 172 +// CHECK-NEXT: 0 8364 +// CHECK-NEXT: test( 66560 ) +// CHECK-NEXT: 𐐀 +// CHECK-NEXT: 240 +// CHECK-NEXT: 144 +// CHECK-NEXT: 144 +// CHECK-NEXT: 128 +// CHECK-NEXT: 0 66560 +// CHECK-NEXT: .©€𐐀 +// CHECK-NEXT: 4 4 4 +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true +// CHECK-NEXT: true + +package main + +func test(r rune) { + println("test(", r, ")") + s := string(r) + println(s) + for i := 0; i < len(s); i++ { + println(s[i]) + } + for i, r := range s { + println(i, r) + } +} + +type namedRune rune + +func testslice(r1 []rune) { + s := string(r1) + println(s) + r2 := []rune(s) + r3 := []namedRune(s) + println(len(r1), len(r2), len(r3)) + if len(r2) == len(r1) && len(r3) == len(r1) { + for i := range r2 { + println(r1[i] == r2[i]) + println(r1[i] == rune(r3[i])) + } + } +} + +func main() { + var runes = []rune{'.', '©', '€', '𐐀'} + test(runes[0]) + test(runes[1]) + test(runes[2]) + test(runes[3]) + testslice(runes) +} Index: tools/llgo/test/execution/strings/slice.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/strings/slice.go @@ -0,0 +1,17 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: abcdef +// CHECK-NEXT: bcdef +// CHECK-NEXT: abc +// CHECK-NEXT: bcd + +package main + +func main() { + s := "abcdef" + println(s[:]) + println(s[1:]) + println(s[:3]) + println(s[1:4]) +} Index: tools/llgo/test/execution/structs/compare.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/structs/compare.go @@ -0,0 +1,52 @@ +// 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: true +// CHECK-NEXT: false +// CHECK-NEXT: false +// CHECK-NEXT: true + +package main + +type S0 struct{} + +type S1 struct { + a int +} + +type S2 struct { + a, b int +} + +func testS0() { + println(S0{} == S0{}) + println(S0{} != S0{}) +} + +func testS1() { + println(S1{1} == S1{1}) + println(S1{1} != S1{1}) + println(S1{1} == S1{2}) + println(S1{1} != S1{2}) +} + +func testS2() { + s1 := S2{1, 2} + s2 := S2{1, 3} + println(s1 == s1) + println(s1 == s2) + println(s1 != s1) + println(s1 != s2) +} + +func main() { + testS0() + testS1() + testS2() +} Index: tools/llgo/test/execution/structs/embed.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/structs/embed.go @@ -0,0 +1,58 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: A.test 1 +// CHECK-NEXT: A.testA +// CHECK-NEXT: A.testA2 +// CHECK-NEXT: B.test 2 +// CHECK-NEXT: A.testA +// CHECK-NEXT: A.testA2 +// CHECK-NEXT: A.testA + +package main + +type A struct{ aval int } + +func (a *A) test() { + println("A.test", a.aval) +} + +func (a *A) testA() { + println("A.testA") +} + +func (a A) testA2() { + println("A.testA2") +} + +type B struct { + A + bval int +} + +func (b B) test() { + println("B.test", b.bval) +} + +type C struct { + *B + cval int +} + +func main() { + var b B + b.aval = 1 + b.bval = 2 + b.A.test() + b.A.testA() + b.A.testA2() + b.test() + b.testA() + b.testA2() + + var c C + c.B = &b + c.cval = 3 + c.testA() + //c.testA2() +} Index: tools/llgo/test/execution/switch/branch.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/switch/branch.go @@ -0,0 +1,23 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: true +// CHECK-NEXT: false + +package main + +func main() { + switch true { + default: + break + println("default") + } + + switch true { + case true: + println("true") + fallthrough + case false: + println("false") + } +} Index: tools/llgo/test/execution/switch/default.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/switch/default.go @@ -0,0 +1,21 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: default +// CHECK-NEXT: true + +package main + +func main() { + switch true { + default: + println("default") + } + + switch { + default: + println("default") + case true: + println("true") + } +} Index: tools/llgo/test/execution/switch/empty.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/switch/empty.go @@ -0,0 +1,16 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: f was called + +package main + +func f() int { + println("f was called") + return 123 +} + +func main() { + switch f() { + } +} Index: tools/llgo/test/execution/switch/scope.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/switch/scope.go @@ -0,0 +1,20 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 1 +// CHECK-NEXT: 2 + +package main + +func main() { + // case clauses have their own scope. + switch { + case true, false: + x := 1 + println(x) + fallthrough + default: + x := 2 + println(x) + } +} Index: tools/llgo/test/execution/switch/strings.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/switch/strings.go @@ -0,0 +1,21 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: abc +// CHECK-NEXT: def, abc + +package main + +func main() { + switch "abc" { + case "def": + println("def") + case "abc": + println("abc") + } + + switch "abc" { + case "def", "abc": + println("def, abc") + } +} Index: tools/llgo/test/execution/switch/type.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/switch/type.go @@ -0,0 +1,72 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: int64 123 +// CHECK-NEXT: default +// CHECK-NEXT: uint8 or int8 +// CHECK-NEXT: uint8 or int8 +// CHECK-NEXT: N + +package main + +func test(i interface{}) { + switch x := i.(type) { + case int64: + println("int64", x) + // FIXME + //case string: + // println("string", x) + default: + println("default") + } +} + +type stringer interface { + String() string +} + +func printany(i interface{}) { + switch v := i.(type) { + case nil: + print("nil", v) + case stringer: + print(v.String()) + case error: + print(v.Error()) + case int: + print(v) + case string: + print(v) + } +} + +func multi(i interface{}) { + switch i.(type) { + case uint8, int8: + println("uint8 or int8") + default: + println("something else") + } +} + +type N int + +func (n N) String() string { return "N" } + +func named() { + var x interface{} = N(123) + switch x := x.(type) { + case N: + // Test for bug: previously, type switch was + // assigning underlying type of N (int). + println(x.String()) + } +} + +func main() { + test(int64(123)) + test("abc") + multi(uint8(123)) + multi(int8(123)) + named() +} Index: tools/llgo/test/execution/types/named.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/types/named.go @@ -0,0 +1,37 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 24 +// CHECK-NEXT: 16 +// CHECK-NEXT: 0 + +package main + +import "unsafe" + +func f1() { + type T struct { + a, b, c int + } + var t T + println(unsafe.Sizeof(t)) +} + +func f2() { + type T interface{} + var t T + t = 1 + println(unsafe.Sizeof(t)) +} + +func f3() { + type T struct{} + var t T + println(unsafe.Sizeof(t)) +} + +func main() { + f1() + f2() + f3() +} Index: tools/llgo/test/execution/types/recursive.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/types/recursive.go @@ -0,0 +1,29 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 3 +// CHECK-NEXT: 4 + +package main + +type T1 *T1 + +func count(t T1) int { + if t == nil { + return 1 + } + return 1 + count(*t) +} + +func testSelfPointer() { + var a T1 + var b T1 + var c T1 = &b + *c = &a + println(count(c)) + println(count(&c)) +} + +func main() { + testSelfPointer() +} Index: tools/llgo/test/execution/unsafe/const_sizeof.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/unsafe/const_sizeof.go @@ -0,0 +1,18 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 8 +// CHECK-NEXT: 8 + +package main + +import "unsafe" + +const ptrSize = unsafe.Sizeof((*byte)(nil)) + +var x [ptrSize]int + +func main() { + println(ptrSize) + println(len(x)) +} Index: tools/llgo/test/execution/unsafe/offsetof.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/unsafe/offsetof.go @@ -0,0 +1,26 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 0 +// CHECK-NEXT: 4 +// CHECK-NEXT: 8 +// CHECK-NEXT: 16 + +package main + +import "unsafe" + +type S struct { + a int16 + b int32 + c int8 + d int64 +} + +func main() { + var s S + println(unsafe.Offsetof(s.a)) + println(unsafe.Offsetof(s.b)) + println(unsafe.Offsetof(s.c)) + println(unsafe.Offsetof(s.d)) +} Index: tools/llgo/test/execution/unsafe/pointer.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/unsafe/pointer.go @@ -0,0 +1,21 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 123 +// CHECK-NEXT: 456 + +package main + +import "unsafe" + +func main() { + var i [2]int + i[0] = 123 + i[1] = 456 + ptr := &i[0] + println(*ptr) + ptr_i := unsafe.Pointer(ptr) + ptr_i = unsafe.Pointer(uintptr(ptr_i) + unsafe.Sizeof(i[0])) + ptr = (*int)(ptr_i) + println(*ptr) +} Index: tools/llgo/test/execution/unsafe/sizeof_array.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/unsafe/sizeof_array.go @@ -0,0 +1,18 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 12 + +package main + +import "unsafe" + +type uint24 struct { + a uint16 + b uint8 +} + +func main() { + var a [3]uint24 + println(unsafe.Sizeof(a)) +} Index: tools/llgo/test/execution/unsafe/sizeof_basic.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/unsafe/sizeof_basic.go @@ -0,0 +1,102 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 1 +// CHECK-NEXT: 8 +// CHECK-NEXT: 1 +// CHECK-NEXT: 2 +// CHECK-NEXT: 4 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 +// CHECK-NEXT: 1 +// CHECK-NEXT: 2 +// CHECK-NEXT: 4 +// CHECK-NEXT: 8 +// CHECK-NEXT: 4 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 +// CHECK-NEXT: 16 +// CHECK-NEXT: 16 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 +// CHECK-NEXT: 1 +// CHECK-NEXT: 8 +// CHECK-NEXT: 1 +// CHECK-NEXT: 2 +// CHECK-NEXT: 4 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 +// CHECK-NEXT: 1 +// CHECK-NEXT: 2 +// CHECK-NEXT: 4 +// CHECK-NEXT: 8 +// CHECK-NEXT: 4 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 +// CHECK-NEXT: 8 + +package main + +import "unsafe" + +func main() { + var b bool + var i int + var i8 int8 + var i16 int16 + var i32 int32 + var i64 int64 + var u uint + var u8 uint8 + var u16 uint16 + var u32 uint32 + var u64 uint64 + var f32 float32 + var f64 float64 + var c64 complex64 + var c128 complex128 + var s string + var p unsafe.Pointer + var up uintptr + + println(unsafe.Sizeof(b)) + println(unsafe.Sizeof(i)) + println(unsafe.Sizeof(i8)) + println(unsafe.Sizeof(i16)) + println(unsafe.Sizeof(i32)) + println(unsafe.Sizeof(i64)) + println(unsafe.Sizeof(u)) + println(unsafe.Sizeof(u8)) + println(unsafe.Sizeof(u16)) + println(unsafe.Sizeof(u32)) + println(unsafe.Sizeof(u64)) + println(unsafe.Sizeof(f32)) + println(unsafe.Sizeof(f64)) + println(unsafe.Sizeof(c64)) + println(unsafe.Sizeof(c128)) + println(unsafe.Sizeof(s)) + println(unsafe.Sizeof(p)) + println(unsafe.Sizeof(up)) + + println(unsafe.Alignof(b)) + println(unsafe.Alignof(i)) + println(unsafe.Alignof(i8)) + println(unsafe.Alignof(i16)) + println(unsafe.Alignof(i32)) + println(unsafe.Alignof(i64)) + println(unsafe.Alignof(u)) + println(unsafe.Alignof(u8)) + println(unsafe.Alignof(u16)) + println(unsafe.Alignof(u32)) + println(unsafe.Alignof(u64)) + println(unsafe.Alignof(f32)) + println(unsafe.Alignof(f64)) + println(unsafe.Alignof(c64)) + println(unsafe.Alignof(c128)) + println(unsafe.Alignof(s)) + println(unsafe.Alignof(p)) + println(unsafe.Alignof(up)) +} Index: tools/llgo/test/execution/unsafe/sizeof_struct.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/unsafe/sizeof_struct.go @@ -0,0 +1,19 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 24 + +package main + +import "unsafe" + +type a struct { + a int16 + b int32 + c int8 + d int64 +} + +func main() { + println(unsafe.Sizeof(a{})) +} Index: tools/llgo/test/execution/var.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/var.go @@ -0,0 +1,37 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: woobie +// CHECK-NEXT: 579 456 +// CHECK-NEXT: 12 +3.450000e+000 +// CHECK-NEXT: -1 + +package main + +func Blah() int { + println("woobie") + return 123 +} + +func F1() (int, float64) { + return 12, 3.45 +} + +var X = Y + Blah() // == 579 +var Y = 123 + Z // == 456 + +var X1, Y1 = F1() + +const ( + _ = 333 * iota + Z +) + +var I interface{} = -1 +var I1 = I.(int) + +func main() { + println(X, Y) + println(X1, Y1) + println(I1) +} Index: tools/llgo/test/execution/varargs.go =================================================================== --- /dev/null +++ tools/llgo/test/execution/varargs.go @@ -0,0 +1,31 @@ +// RUN: llgo -o %t %s +// RUN: %t 2>&1 | FileCheck %s + +// CHECK: 3 +// CHECK-NEXT: 123 +// CHECK-NEXT: 456 +// CHECK-NEXT: 789 +// CHECK-NEXT: 4 +// CHECK-NEXT: 123 +// CHECK-NEXT: 456 +// CHECK-NEXT: 789 +// CHECK-NEXT: 101112 +// CHECK-NEXT: 3 +// CHECK-NEXT: 1 +// CHECK-NEXT: 2 +// CHECK-NEXT: 3 + +package main + +func p(i ...int) { + println(len(i)) + for j := 0; j < len(i); j++ { + println(i[j]) + } +} + +func main() { + p(123, 456, 789) + p(123, 456, 789, 101112) + p([]int{1, 2, 3}...) +} Index: tools/llgo/test/gllgo/dead.go =================================================================== --- /dev/null +++ tools/llgo/test/gllgo/dead.go @@ -0,0 +1,6 @@ +// RUN: llgo -O0 -S -o - %s | FileCheck %s + +package gotest + +// CHECK-NOT: deadfunc +func deadfunc() Index: tools/llgo/test/irgen/mangling.go =================================================================== --- /dev/null +++ tools/llgo/test/irgen/mangling.go @@ -0,0 +1,7 @@ +// RUN: llgo -fgo-pkgpath=llvm.org/llvm -S -emit-llvm -o - %s | FileCheck %s + +package llvm + +// CHECK: @llvm_org_llvm.F +func F() { +} Index: tools/llgo/test/lit.cfg =================================================================== --- /dev/null +++ tools/llgo/test/lit.cfg @@ -0,0 +1,15 @@ +import lit.formats +import os +import sys + +config.name = 'llgo' +config.suffixes = ['.go'] +config.test_format = lit.formats.ShTest() +config.test_source_root = config.llvm_src_root + '/tools/llgo/test' +config.test_exec_root = config.llvm_obj_root + '/tools/llgo/test' +config.excludes = ['Inputs'] + +config.substitutions.append((r"\bllgo\b", config.llvm_obj_root + '/bin/llgo -static-libgo')) +config.substitutions.append((r"\bFileCheck\b", config.llvm_obj_root + '/bin/FileCheck')) +config.substitutions.append((r"\bcount\b", config.llvm_obj_root + '/bin/count')) +config.substitutions.append((r"\bnot\b", config.llvm_obj_root + '/bin/not')) Index: tools/llgo/test/lit.site.cfg.in =================================================================== --- /dev/null +++ tools/llgo/test/lit.site.cfg.in @@ -0,0 +1,4 @@ +config.llvm_src_root = "@LLVM_SOURCE_DIR@" +config.llvm_obj_root = "@LLVM_BINARY_DIR@" + +lit_config.load_config(config, "@CMAKE_CURRENT_SOURCE_DIR@/lit.cfg") Index: tools/llgo/update_third_party.sh =================================================================== --- /dev/null +++ tools/llgo/update_third_party.sh @@ -0,0 +1,78 @@ +#!/bin/sh -e + +gofrontendrepo=https://code.google.com/p/gofrontend +gofrontendrev=82f97044669e + +gccrepo=svn://gcc.gnu.org/svn/gcc/trunk +gccrev=216268 + +gotoolsrepo=https://code.google.com/p/go.tools +gotoolsrev=40c49f5c2b64 + +tempdir=$(mktemp -d /tmp/update_third_party.XXXXXX) +gofrontenddir=$tempdir/gofrontend +gotoolsdir=$tempdir/go.tools + +rm -rf third_party +mkdir -p third_party/gofrontend third_party/go.tools + +# --------------------- gofrontend --------------------- + +hg clone -r $gofrontendrev $gofrontendrepo $gofrontenddir + +cp -r $gofrontenddir/LICENSE $gofrontenddir/libgo third_party/gofrontend + +# Apply a diff that eliminates use of the unnamed struct extension beyond what +# -fms-extensions supports. +(cd third_party/gofrontend && patch -p1) < libgo-noext.diff + +# Remove GPL licensed files. +rm \ + third_party/gofrontend/libgo/testsuite/libgo.testmain/testmain.exp \ + third_party/gofrontend/libgo/testsuite/lib/libgo.exp \ + third_party/gofrontend/libgo/testsuite/config/default.exp + +# --------------------- gcc --------------------- + +# Some dependencies are stored in the gcc repository. +# TODO(pcc): Ask iant about mirroring these dependencies into gofrontend. + +mkdir -p third_party/gofrontend/include third_party/gofrontend/libgcc +for f in config.guess config-ml.in config.sub depcomp \ + install-sh ltmain.sh missing move-if-change \ + include/dwarf2.def include/dwarf2.h libgcc/unwind-pe.h ; do + svn cat -r $gccrev $gccrepo/$f > third_party/gofrontend/$f +done + +# Avoid pulling in a bunch of unneeded gcc headers. +echo "#define IS_ABSOLUTE_PATH(path) ((path)[0] == '/')" \ + > third_party/gofrontend/include/filenames.h + +for d in libbacktrace libffi ; do + svn export -r $gccrev $gccrepo/$d third_party/gofrontend/$d +done + +# Remove GPL licensed files. +rm \ + third_party/gofrontend/libffi/msvcc.sh \ + third_party/gofrontend/libffi/testsuite/lib/libffi.exp \ + third_party/gofrontend/libffi/testsuite/libffi.call/call.exp \ + third_party/gofrontend/libffi/testsuite/libffi.special/special.exp \ + third_party/gofrontend/libffi/testsuite/config/default.exp + +# --------------------- go.tools --------------------- + +hg clone -r $gotoolsrev $gotoolsrepo $gotoolsdir + +cp -r $gotoolsdir/* third_party/go.tools + +# Vendor the go.tools repository. +find third_party/go.tools -name '*.go' | xargs sed -i -e \ + 's,"golang.org/x/tools/,"llvm.org/llgo/third_party/go.tools/,g' + +# Until the version skew between the "go" tool and the compiler is resolved, +# we patch out Go 1.4 specific code in go.tools. +sed -i -e '/go1\.4/ d' third_party/go.tools/go/exact/go13.go +rm third_party/go.tools/go/exact/go14.go + +rm -rf $tempdir Index: tools/llgo/utils/benchcomp/README =================================================================== --- /dev/null +++ tools/llgo/utils/benchcomp/README @@ -0,0 +1,20 @@ +These are some quick and dirty tools for measuring the performance impact +of a change to llgo by sampling the results of running the libgo benchmark +suite. They can be used to calculate the geo-mean and 95% confidence interval +using the Student's t-test. The benchcomp program massages the output of the +Go benchmark tools into a form that can be read by the R program analyze.R +which runs the statistics. + +To use, clpatch this into gofrontend: +https://codereview.appspot.com/103550047/ + +then run: + +make +make -C workdir/gofrontend_build/libgo-stage1 bench 2>&1 | tee before.out +# make changes +make +make -C workdir/gofrontend_build/libgo-stage1 bench 2>&1 | tee after.out +utils/benchcomp/benchcomp benchns before.out after.out | R -f utils/benchcomp/analyze.R + +The results should be displayed on stdout. Index: tools/llgo/utils/benchcomp/analyze.R =================================================================== --- /dev/null +++ tools/llgo/utils/benchcomp/analyze.R @@ -0,0 +1,15 @@ +sc <- read.table(file('stdin')) +scratio <- sc$V2 / sc$V3 +scratio <- scratio[scratio > 0] + +# Take the log of the ratio. Our null hypothesis is a normal distribution +# around zero. +tt <- t.test(log(scratio)) +tt + +# This gives us the geo-mean as we are taking the exponent of the linear mean +# of logarithms. +1 - 1/exp(tt$estimate) + +# Likewise for the confidence interval. +1 - 1/exp(tt$conf.int) Index: tools/llgo/utils/benchcomp/main.go =================================================================== --- /dev/null +++ tools/llgo/utils/benchcomp/main.go @@ -0,0 +1,92 @@ +package main + +import ( + "bufio" + "debug/elf" + "fmt" + "os" + "strconv" + "strings" +) + +func symsizes(path string) map[string]float64 { + m := make(map[string]float64) + f, err := elf.Open(path) + if err != nil { + panic(err.Error()) + } + syms, err := f.Symbols() + if err != nil { + panic(err.Error()) + } + for _, sym := range syms { + if sym.Section < elf.SectionIndex(len(f.Sections)) && f.Sections[sym.Section].Name == ".text" { + m[sym.Name] = float64(sym.Size) + } + } + return m +} + +func benchnums(path, stat string) map[string]float64 { + m := make(map[string]float64) + + fh, err := os.Open(path) + if err != nil { + panic(err.Error()) + } + + scanner := bufio.NewScanner(fh) + for scanner.Scan() { + elems := strings.Split(scanner.Text(), "\t") + if !strings.HasPrefix(elems[0], "Benchmark") || len(elems) < 3 { + continue + } + var s string + for _, elem := range elems[2:] { + selems := strings.Split(strings.TrimSpace(elem), " ") + if selems[1] == stat { + s = selems[0] + } + } + if s != "" { + ns, err := strconv.ParseFloat(s, 64) + if err != nil { + panic(scanner.Text() + " ---- " + err.Error()) + } + m[elems[0]] = ns + } + } + + if err := scanner.Err(); err != nil { + panic(err) + } + + return m +} + +func main() { + var cmp func(string) map[string]float64 + switch os.Args[1] { + case "symsizes": + cmp = symsizes + + case "benchns": + cmp = func(path string) map[string]float64 { + return benchnums(path, "ns/op") + } + + case "benchallocs": + cmp = func(path string) map[string]float64 { + return benchnums(path, "allocs/op") + } + } + + syms1 := cmp(os.Args[2]) + syms2 := cmp(os.Args[3]) + + for n, z1 := range syms1 { + if z2, ok := syms2[n]; ok && z2 != 0 { + fmt.Printf("%s %f %f %f\n", n, z1, z2, z1/z2) + } + } +} Index: tools/llvm-go/llvm-go.go =================================================================== --- tools/llvm-go/llvm-go.go +++ tools/llvm-go/llvm-go.go @@ -30,6 +30,7 @@ var packages = []pkg{ {"bindings/go/llvm", "llvm.org/llvm/bindings/go/llvm"}, + {"tools/llgo", "llvm.org/llgo"}, } type compilerFlags struct { @@ -136,7 +137,7 @@ `, flags.cpp, flags.cxx, flags.ld) } -func runGoWithLLVMEnv(args []string, cc, cxx, cppflags, cxxflags, ldflags string) { +func runGoWithLLVMEnv(args []string, cc, cxx, llgo, cppflags, cxxflags, ldflags string) { args = addTag(args, "byollvm") srcdir := llvmConfig("--src-root") @@ -159,6 +160,28 @@ } } + newpath := os.Getenv("PATH") + + if llgo != "" { + bindir := filepath.Join(tmpgopath, "bin") + + err = os.MkdirAll(bindir, os.ModePerm) + if err != nil { + panic(err.Error()) + } + + err = os.Symlink(llgo, filepath.Join(bindir, "gccgo")) + if err != nil { + panic(err.Error()) + } + + newpathlist := []string{bindir} + newpathlist = append(newpathlist, filepath.SplitList(newpath)...) + newpath = strings.Join(newpathlist, string(filepath.ListSeparator)) + + args = append([]string{args[0], "-compiler", "gccgo"}, args[1:]...) + } + newgopathlist := []string{tmpgopath} newgopathlist = append(newgopathlist, filepath.SplitList(os.Getenv("GOPATH"))...) newgopath := strings.Join(newgopathlist, string(filepath.ListSeparator)) @@ -172,6 +195,7 @@ "CGO_CXXFLAGS=" + flags.cxx + " " + cxxflags, "CGO_LDFLAGS=" + flags.ld + " " + ldflags, "GOPATH=" + newgopath, + "PATH=" + newpath, } for _, v := range os.Environ() { if !strings.HasPrefix(v, "CC=") && @@ -179,7 +203,8 @@ !strings.HasPrefix(v, "CGO_CPPFLAGS=") && !strings.HasPrefix(v, "CGO_CXXFLAGS=") && !strings.HasPrefix(v, "CGO_LDFLAGS=") && - !strings.HasPrefix(v, "GOPATH=") { + !strings.HasPrefix(v, "GOPATH=") && + !strings.HasPrefix(v, "PATH=") { newenv = append(newenv, v) } } @@ -222,6 +247,7 @@ cppflags := os.Getenv("CGO_CPPFLAGS") cxxflags := os.Getenv("CGO_CXXFLAGS") ldflags := os.Getenv("CGO_LDFLAGS") + llgo := "" args := os.Args[1:] DONE: for { @@ -234,6 +260,9 @@ case strings.HasPrefix(args[0], "cxx="): cxx = args[0][4:] args = args[1:] + case strings.HasPrefix(args[0], "llgo="): + llgo = args[0][5:] + args = args[1:] case strings.HasPrefix(args[0], "cppflags="): cppflags = args[0][9:] args = args[1:] @@ -250,7 +279,7 @@ switch args[0] { case "build", "get", "install", "run", "test": - runGoWithLLVMEnv(args, cc, cxx, cppflags, cxxflags, ldflags) + runGoWithLLVMEnv(args, cc, cxx, llgo, cppflags, cxxflags, ldflags) case "print-components": printComponents() case "print-config":