gollvm: support target CPU/features (-march=XXX, etc)

This patch adds real support for the "-march=" command line option, to
allow users to have the back end target a specific CPU (ex: 'nehalem')
within a given architecture (ex: x86-64). Each specific cpu/arch
selection gets translated into a set of "target feature" attributes
that are then attached to each LLVM function; specifying the correct
attributes is key to insuring that vectorization works properly.

Notes:

- creates a new Go helper program, "capture-fcn-attributes.go", that
  developers can build and run to generate a header containing the
  correct set of fcn attributes to use for specific CPUs/archs. The
  expectation is that building and running this Go program is
  something done by gollvm developers offline (as opposed to having it
  be part of the cmake/ninja build process each time gollvm is built).
  The helper program works by capturing the behavior of clang:
  running clang on an input file for specific -march=... values
  and then looking at the generated IR.

- unlike clang, gollvm won't support (at least initially) flags for
  turning on/off individual features/instructions, since this would
  add a great deal of complexity.

Change-Id: I99abf703608e0d1f5d00f6becc3f43cbdb60b394
Reviewed-on: https://go-review.googlesource.com/118322
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
6 files changed
tree: 4fbf5dc82e6450eceb2ee62111e6897210090d2d
  1. bridge/
  2. cmake/
  3. driver/
  4. driver-main/
  5. gotools/
  6. libgo/
  7. tools/
  8. unittests/
  9. .gitignore
  10. AUTHORS
  11. CMakeLists.txt
  12. CONTRIBUTORS
  13. LICENSE
  14. PATENTS
  15. README.md
README.md

gollvm

Gollvm is an LLVM-based Go compiler. It incorporates “gofrontend” (a Go language front end written in C++ and shared with GCCGO), a bridge component (which translates from gofrontend IR to LLVM IR), and a driver that sends the resulting IR through the LLVM back end.

Gollvm (name still not finalized) is set up to be a subproject within the LLVM tools directory, similar to how things work for “clang” or “compiler-rt”: you check out a copy of the LLVM source tree, then within the LLVM tree you check out additional git repos.

You'll need to have an up-to-date copy of cmake on your system (3.6 vintage).

Setting up a gollvm work area:

// Here 'workarea' will contain a copy of the LLVM source tree and one or more build areas
% mkdir workarea
% cd workarea

// Sources
% git clone http://llvm.org/git/llvm.git
...
% cd llvm/tools
% git clone https://go.googlesource.com/gollvm
...
% cd gollvm
% git clone https://go.googlesource.com/gofrontend
...
% cd libgo
% git clone https://github.com/libffi/libffi.git
...
% git clone https://github.com/ianlancetaylor/libbacktrace.git
...
%

// Create a build directory, and run cmake
% mkdir build.opt
% cd build.opt
% cmake -DCMAKE_BUILD_TYPE=Debug -DLLVM_USE_LINKER=gold -G Ninja ../llvm

// Build
ninja <gollvm target(s)>

Installing gollvm

A gollvm installation will contain ‘llvm-goc’, the Gollvm compiler, the libgo standard Go libraries, and the standard Go tools (“go”, “vet”, “cgo”, etc).

The installation directory for gollvm needs to be specified when invoking cmake prior to the build:

% mkdir build.rel
% cd build.rel
% cmake -DCMAKE_INSTALL_PREFIX=/my/install/dir -DCMAKE_BUILD_TYPE=Release -DLLVM_USE_LINKER=gold -G Ninja ../llvm

// Build all of gollvm
% ninja gollvm
...

// Install gollvm
% ninja install-gollvm

Using an installed copy of gollvm

Programs build with the Gollvm Go compiler default to shared linkage, meaning that they need to pick up the Go runtime library via LD_LIBRARY_PATH:


// Root of Gollvm install is /tmp/gollvm-install % export LD_LIBRARY_PATH=/tmp/gollvm-install/lib64 % export PATH=/tmp/gollvm-install/bin:$PATH % go run himom.go hi mom! %

Source code structure

Within /llvm/tools/gollvm, the following directories are of interest:

.../llvm/tools/gollvm:

  • contains rules to build third party libraries needed for gollvm, along with common definitions for subdirs.

.../llvm/tools/gollvm/driver, .../llvm/tools/gollvm/driver-main:

  • contains build rules and source code for llvm-goc

.../llvm/tools/gollvm/gofrontend:

  • source code for gofrontend and libgo (note: no cmake files here)

.../llvm/tools/gollvm/bridge:

  • contains build rules for the libLLVMCppGoFrontEnd.a, a library that contains both the gofrontend code and the LLVM-specific middle layer (for example, the definition of the class Llvm_backend, which inherits from Backend).

.../llvm/tools/gollvm/unittests:

  • source code for the unit tests

Building and running llvm-goc

The executable llvm-goc is the main compiler driver for gollvm; it functions as a compiler (consuming source for a Go package and producing an object file), an assembler, and/or a linker. While it is possible to build and run llvm-goc directly from the command line, in practice there is little point in doing this (better to build using “go build”, which will invoke llvm-goc on your behalf.

// From within <workarea>/build.opt:

% ninja llvm-goc
...
% cat micro.go
package foo
func Bar() int {
	return 1
}
% ./bin/llvm-goc -fgo-pkgpath=foo -O3 -o micro.s micro.go
%

Building and running the unit tests

Here are instructions on building and running the unit tests for the middle layer:

// From within <workarea>/build.opt:

// Build unit test
% ninja GoBackendCoreTests

// Run unit test
% ./tools/gollvm/unittests/BackendCore/GoBackendCoreTests
[==========] Running 10 tests from 2 test cases.
[----------] Global test environment set-up.
[----------] 9 tests from BackendCoreTests
[ RUN      ] BackendCoreTests.MakeBackend
[       OK ] BackendCoreTests.MakeBackend (1 ms)
[ RUN      ] BackendCoreTests.ScalarTypes
[       OK ] BackendCoreTests.ScalarTypes (0 ms)
[ RUN      ] BackendCoreTests.StructTypes
[       OK ] BackendCoreTests.StructTypes (1 ms)
[ RUN      ] BackendCoreTests.ComplexTypes
[       OK ] BackendCoreTests.ComplexTypes (0 ms)
[ RUN      ] BackendCoreTests.FunctionTypes
[       OK ] BackendCoreTests.FunctionTypes (0 ms)
[ RUN      ] BackendCoreTests.PlaceholderTypes
[       OK ] BackendCoreTests.PlaceholderTypes (0 ms)
[ RUN      ] BackendCoreTests.ArrayTypes
[       OK ] BackendCoreTests.ArrayTypes (0 ms)
[ RUN      ] BackendCoreTests.NamedTypes
[       OK ] BackendCoreTests.NamedTypes (0 ms)
[ RUN      ] BackendCoreTests.TypeUtils

...

[  PASSED  ] 10 tests.

The unit tests currently work by instantiating an LLVM Backend instance and making backend method calls (to mimic what the frontend would do), then inspects the results to make sure they are as expected. Here is an example:

TEST(BackendCoreTests, ComplexTypes) {
  LLVMContext C;

  Type *ft = Type::getFloatTy(C);
  Type *dt = Type::getDoubleTy(C);

  std::unique_ptr<Backend> be(go_get_backend(C));
  Btype *c32 = be->complex_type(64);
  ASSERT_TRUE(c32 != NULL);
  ASSERT_EQ(c32->type(), mkTwoFieldLLvmStruct(C, ft, ft));
  Btype *c64 = be->complex_type(128);
  ASSERT_TRUE(c64 != NULL);
  ASSERT_EQ(c64->type(), mkTwoFieldLLvmStruct(C, dt, dt));
}

The test above makes sure that the LLVM type we get as a result of calling Backend::complex_type() is kosher and matches up to expectations.

Building libgo (Go runtime and standard libraries)

To build the Go runtime and standard libraries, use the following:

// From within <workarea>/build.opt:

// Build Go runtime and standard libraries
% ninja libgo_all

This will compile static (.a) and dynamic (.so) versions of the library.