diff --git a/configure b/configure index 2a79e1c2a9753..fa66a0e60675e 100755 --- a/configure +++ b/configure @@ -566,6 +566,7 @@ opt rpath 0 "build rpaths into rustc itself" opt dist-host-only 0 "only install bins for the host architecture" opt inject-std-version 1 "inject the current compiler version of libstd into programs" opt llvm-version-check 1 "check if the LLVM version is supported, build anyway" +valopt nacl-cross-path "" "NaCl SDK path (must be Pepper 43 or higher). Must be absolute!" # Optimization and debugging options. These may be overridden by the release channel, etc. opt_nosave optimize 1 "build optimized rust code" @@ -1078,6 +1079,18 @@ do fi ;; + *-unknown-nacl) + if [ -z "$CFG_NACL_CROSS_PATH" ] + then + err "I need the NaCl SDK path! (use --nacl-cross-path)" + fi + if [ ! -z "$CFG_LLVM_ROOT" ] + then + CFG_LLVM_ROOT="${CFG_NACL_CROSS_PATH}/toolchain/`${CFG_NACL_CROSS_PATH}/tools/getos.py`_pnacl" + putvar CFG_LLVM_ROOT + fi + ;; + arm-apple-darwin) if [ $CFG_OSTYPE != apple-darwin ] then @@ -1308,12 +1321,143 @@ for t in $CFG_HOST do do_reconfigure=1 is_msvc=0 + + # LLVM's configure doesn't recognize the new Windows triples yet + gnu_t=$(to_gnu_triple $t) + if [ "$t" != "$CFG_BUILD" ] + then + LLVM_PREFIX="$gnu_t-" + else + LLVM_PREFIX="" + fi + + LLVM_AR="ar" + LLVM_RANLIB="ranlib" + + case "$CFG_CC" in + ("ccache clang") + LLVM_CXX_32="ccache ${LLVM_PREFIX}clang++ -Qunused-arguments" + LLVM_CC_32="ccache ${LLVM_PREFIX}clang -Qunused-arguments" + + LLVM_CXX_64="ccache ${LLVM_PREFIX}clang++ -Qunused-arguments" + LLVM_CC_64="ccache ${LLVM_PREFIX}clang -Qunused-arguments" + ;; + ("clang") + LLVM_CXX_32="${LLVM_PREFIX}clang++ -Qunused-arguments" + LLVM_CC_32="${LLVM_PREFIX}clang -Qunused-arguments" + + LLVM_CXX_64="${LLVM_PREFIX}clang++ -Qunused-arguments" + LLVM_CC_64="${LLVM_PREFIX}clang -Qunused-arguments" + ;; + ("ccache gcc") + LLVM_CXX_32="ccache ${LLVM_PREFIX}g++" + LLVM_CC_32="ccache ${LLVM_PREFIX}gcc" + + LLVM_CXX_64="ccache ${LLVM_PREFIX}g++" + LLVM_CC_64="ccache ${LLVM_PREFIX}gcc" + ;; + ("gcc") + LLVM_CXX_32="${LLVM_PREFIX}g++" + LLVM_CC_32="${LLVM_PREFIX}gcc" + + LLVM_CXX_64="${LLVM_PREFIX}g++" + LLVM_CC_64="${LLVM_PREFIX}gcc" + ;; + + (*) + msg "inferring CXX/CC = $CXX/$CC" + LLVM_CXX_32="$CXX" + LLVM_CC_32="$CC" + + LLVM_CXX_64="$CXX" + LLVM_CC_64="$CC" + ;; + esac + + case "$CFG_CPUTYPE" in + (x86*) + LLVM_CFLAGS_32="-m32" + LLVM_CXXFLAGS_32="-m32" + LLVM_LDFLAGS_32="-m32" + + LLVM_CFLAGS_64="" + LLVM_CXXFLAGS_64="" + LLVM_LDFLAGS_64="" + ;; + + (*) + LLVM_CFLAGS_32="" + LLVM_CXXFLAGS_32="" + LLVM_LDFLAGS_32="" + + LLVM_CFLAGS_64="" + LLVM_CXXFLAGS_64="" + LLVM_LDFLAGS_64="" + ;; + esac + + if echo $t | grep -q x86_64 + then + LLVM_CXX=$LLVM_CXX_64 + LLVM_CC=$LLVM_CC_64 + LLVM_CFLAGS=$LLVM_CFLAGS_64 + LLVM_CXXFLAGS=$LLVM_CXXFLAGS_64 + LLVM_LDFLAGS=$LLVM_LDFLAGS_64 + else + LLVM_CXX=$LLVM_CXX_32 + LLVM_CC=$LLVM_CC_32 + LLVM_CFLAGS=$LLVM_CFLAGS_32 + LLVM_CXXFLAGS=$LLVM_CXXFLAGS_32 + LLVM_LDFLAGS=$LLVM_LDFLAGS_32 + fi + + # Toolchain overrides for specific hosts case "$t" in (*-msvc) - is_msvc=1 + is_msvc=1 + ;; + (le32-unknown-nacl) + BUILD_CXX=clang++ + BUILD_CC=clang + BUILD_CXXFLAGS=$LLVM_CXXFLAGS + BUILD_CFLAGS=$LLVM_CFLAGS + + export BUILD_CXX + export BUILD_CC + export BUILD_CXXFLAGS + export BUILD_CFLAGS + + LLVM_CXX="`$CFG_NACL_CROSS_PATH/tools/nacl_config.py -t pnacl --tool=c++`" + LLVM_CC="`$CFG_NACL_CROSS_PATH/tools/nacl_config.py -t pnacl --tool=cc`" + LLVM_RANLIB="`$CFG_NACL_CROSS_PATH/tools/nacl_config.py -t pnacl --tool=ranlib`" + LLVM_CFLAGS="-I$NACL_SDK_ROOT/toolchain/$($NACL_SDK_ROOT/tools/getos.py)_pnacl/le32-nacl/usr/include/glibc-compat" + LLVM_CXXFLAGS=$LLVM_CFLAGS + LLVM_AR="`$CFG_NACL_CROSS_PATH/tools/nacl_config.py -t pnacl --tool=ar`" + ;; + (*) + unset BUILD_CXX + unset BUILD_CC + unset BUILD_CXXFLAGS + unset BUILD_CFLAGS ;; esac + CXX=$LLVM_CXX + CC=$LLVM_CC + AR=$LLVM_AR + RANLIB=$LLVM_RANLIB + CFLAGS=$LLVM_CFLAGS + CXXFLAGS=$LLVM_CXXFLAGS + LDFLAGS=$LLVM_LDFLAGS + + export CXX + export CC + export AR + export RANLIB + export CFLAGS + export CXXFLAGS + export LDFLAGS + if [ -z $CFG_LLVM_ROOT ] then LLVM_BUILD_DIR=${CFG_BUILD_DIR}$t/llvm @@ -1322,10 +1466,10 @@ do LLVM_DBG_OPTS="--enable-debug-symbols --disable-optimized" # Just use LLVM straight from its build directory to # avoid 'make install' time - LLVM_INST_DIR=$LLVM_BUILD_DIR/Debug + LLVM_BUILD_SUBDIR=Debug else LLVM_DBG_OPTS="--enable-optimized" - LLVM_INST_DIR=$LLVM_BUILD_DIR/Release + LLVM_BUILD_SUBDIR=Release fi if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ] then @@ -1337,9 +1481,13 @@ do # LLVM's CMake build system ignore this and outputs in `Release` # anyway. if [ ${is_msvc} -eq 0 ]; then - LLVM_INST_DIR=${LLVM_INST_DIR}+Asserts + LLVM_BUILD_SUBDIR=${LLVM_BUILD_SUBDIR}+Asserts + else + LLVM_BUILD_SUBDIR=${LLVM_BUILD_SUBDIR} fi fi + + LLVM_INST_DIR=$LLVM_BUILD_DIR/$LLVM_BUILD_SUBDIR else msg "not reconfiguring LLVM, external LLVM root" # The user is using their own LLVM @@ -1395,18 +1543,27 @@ do if [ ${do_reconfigure} -ne 0 ] && [ ${is_msvc} -eq 0 ] then - # LLVM's configure doesn't recognize the new Windows triples yet - gnu_t=$(to_gnu_triple $t) - msg "configuring LLVM for $gnu_t" - LLVM_TARGETS="--enable-targets=x86,x86_64,arm,aarch64,mips,powerpc" - LLVM_BUILD="--build=$gnu_t" + LLVM_BUILD="--build=$(to_gnu_triple $CFG_BUILD)" LLVM_HOST="--host=$gnu_t" LLVM_TARGET="--target=$gnu_t" # Disable unused LLVM features LLVM_OPTS="$LLVM_DBG_OPTS $LLVM_ASSERTION_OPTS --disable-docs --enable-bindings=none" + + case "$t" in + (le32-unknown-nacl) + # Enable only the arches that NaCl can sandbox. + LLVM_TARGETS="--enable-targets=x86,arm,mips" + # Use libc++ to match Rust. + LLVM_OPTS="$LLVM_OPTS --enable-libcpp" + ;; + (*) + LLVM_TARGETS="--enable-targets=x86,x86_64,arm,aarch64,mips,powerpc" + ;; + esac + # Disable term-info, linkage of which comes in multiple forms, # making our snapshots incompatible (#9334) LLVM_OPTS="$LLVM_OPTS --disable-terminfo" @@ -1422,95 +1579,6 @@ do ;; esac - case "$CFG_CC" in - ("ccache clang") - LLVM_CXX_32="ccache clang++ -Qunused-arguments" - LLVM_CC_32="ccache clang -Qunused-arguments" - - LLVM_CXX_64="ccache clang++ -Qunused-arguments" - LLVM_CC_64="ccache clang -Qunused-arguments" - ;; - ("clang") - LLVM_CXX_32="clang++ -Qunused-arguments" - LLVM_CC_32="clang -Qunused-arguments" - - LLVM_CXX_64="clang++ -Qunused-arguments" - LLVM_CC_64="clang -Qunused-arguments" - ;; - ("ccache gcc") - LLVM_CXX_32="ccache g++" - LLVM_CC_32="ccache gcc" - - LLVM_CXX_64="ccache g++" - LLVM_CC_64="ccache gcc" - ;; - ("gcc") - LLVM_CXX_32="g++" - LLVM_CC_32="gcc" - - LLVM_CXX_64="g++" - LLVM_CC_64="gcc" - ;; - - (*) - msg "inferring LLVM_CXX/CC from CXX/CC = $CXX/$CC" - LLVM_CXX_32="$CXX" - LLVM_CC_32="$CC" - - LLVM_CXX_64="$CXX" - LLVM_CC_64="$CC" - ;; - esac - - case "$CFG_CPUTYPE" in - (x86*) - LLVM_CXX_32="$LLVM_CXX_32 -m32" - LLVM_CC_32="$LLVM_CC_32 -m32" - - LLVM_CFLAGS_32="-m32" - LLVM_CXXFLAGS_32="-m32" - LLVM_LDFLAGS_32="-m32" - - LLVM_CFLAGS_64="" - LLVM_CXXFLAGS_64="" - LLVM_LDFLAGS_64="" - - LLVM_CXX_32="$LLVM_CXX_32 -m32" - LLVM_CC_32="$LLVM_CC_32 -m32" - ;; - - (*) - LLVM_CFLAGS_32="" - LLVM_CXXFLAGS_32="" - LLVM_LDFLAGS_32="" - - LLVM_CFLAGS_64="" - LLVM_CXXFLAGS_64="" - LLVM_LDFLAGS_64="" - ;; - esac - - if echo $t | grep -q x86_64 - then - LLVM_CXX=$LLVM_CXX_64 - LLVM_CC=$LLVM_CC_64 - LLVM_CFLAGS=$LLVM_CFLAGS_64 - LLVM_CXXFLAGS=$LLVM_CXXFLAGS_64 - LLVM_LDFLAGS=$LLVM_LDFLAGS_64 - else - LLVM_CXX=$LLVM_CXX_32 - LLVM_CC=$LLVM_CC_32 - LLVM_CFLAGS=$LLVM_CFLAGS_32 - LLVM_CXXFLAGS=$LLVM_CXXFLAGS_32 - LLVM_LDFLAGS=$LLVM_LDFLAGS_32 - fi - - CXX=$LLVM_CXX - CC=$LLVM_CC - CFLAGS=$LLVM_CFLAGS - CXXFLAGS=$LLVM_CXXFLAGS - LDFLAGS=$LLVM_LDFLAGS - if [ -z "$CFG_DISABLE_LIBCPP" ] && [ -n "$CFG_USING_CLANG" ]; then LLVM_OPTS="$LLVM_OPTS --enable-libcpp" fi @@ -1521,12 +1589,6 @@ do msg "configuring LLVM with:" msg "$LLVM_FLAGS" - export CXX - export CC - export CFLAGS - export CXXFLAGS - export LDFLAGS - cd $LLVM_BUILD_DIR case $CFG_SRC_DIR in /* | [a-z]:* | [A-Z]:*) @@ -1549,8 +1611,10 @@ do # variables can't contain hyphens. The makefile will then have to # convert back. CFG_LLVM_BUILD_DIR=$(echo CFG_LLVM_BUILD_DIR_${t} | tr - _) + CFG_LLVM_BUILD_SUBDIR=$(echo CFG_LLVM_BUILD_SUBDIR_${t} | tr - _) CFG_LLVM_INST_DIR=$(echo CFG_LLVM_INST_DIR_${t} | tr - _) eval ${CFG_LLVM_BUILD_DIR}="'$LLVM_BUILD_DIR'" + eval ${CFG_LLVM_BUILD_SUBDIR}="'$LLVM_BUILD_SUBDIR'" eval ${CFG_LLVM_INST_DIR}="'$LLVM_INST_DIR'" done @@ -1568,6 +1632,7 @@ putvar CFG_TARGET putvar CFG_LIBDIR_RELATIVE putvar CFG_DISABLE_MANAGE_SUBMODULES putvar CFG_ANDROID_CROSS_PATH +putvar CFG_NACL_CROSS_PATH putvar CFG_MANDIR # Avoid spurious warnings from clang by feeding it original source on @@ -1596,8 +1661,10 @@ putvar CFG_LLVM_SRC_DIR for t in $CFG_HOST do CFG_LLVM_BUILD_DIR=$(echo CFG_LLVM_BUILD_DIR_${t} | tr - _) + CFG_LLVM_BUILD_SUBDIR=$(echo CFG_LLVM_BUILD_SUBDIR_${t} | tr - _) CFG_LLVM_INST_DIR=$(echo CFG_LLVM_INST_DIR_${t} | tr - _) putvar $CFG_LLVM_BUILD_DIR + putvar $CFG_LLVM_BUILD_SUBDIR putvar $CFG_LLVM_INST_DIR done diff --git a/mk/cfg/le32-unknown-nacl.mk b/mk/cfg/le32-unknown-nacl.mk new file mode 100644 index 0000000000000..738281edeadb0 --- /dev/null +++ b/mk/cfg/le32-unknown-nacl.mk @@ -0,0 +1,45 @@ +# le32-unknown-nacl (portable, PNaCl) +CROSS_PREFIX_le32-unknown-nacl:=$(CFG_NACL_CROSS_PATH)/toolchain/$(NACL_TOOLCHAIN_OS_PATH)_pnacl/bin/pnacl- +CC_le32-unknown-nacl=clang +CXX_le32-unknown-nacl=clang++ +CPP_le32-unknown-nacl=$(CXX_le32-unknown-nacl) -E +AR_le32-unknown-nacl=ar + +# Note: pso's aren't supported by PNaCl or Rust yet. +CFG_LIB_NAME_le32-unknown-nacl=lib$(1).pso +CFG_STATIC_LIB_NAME_le32-unknown-nacl=lib$(1).a +CFG_LIB_GLOB_le32-unknown-nacl=lib$(1)-*.pso +CFG_LIB_DSYM_GLOB_le32-unknown-nacl=lib$(1)-*.dylib.dSYM +CFG_CFLAGS_le32-unknown-nacl := -Wall -Wno-unused-variable -Wno-unused-value -I$(CFG_NACL_CROSS_PATH)/include -I$(CFG_NACL_CROSS_PATH)/include/pnacl -D_YUGA_LITTLE_ENDIAN=1 -D_YUGA_BIG_ENDIAN=0 -Os +CFG_CXXFLAGS_le32-unknown-nacl := -stdlib=libc++ $(CFG_CFLAGS_le32-unknown-nacl) +CFG_GCCISH_CFLAGS_le32-unknown-nacl := $(CFG_CFLAGS_le32-unknown-nacl) +CFG_GCCISH_CXXFLAGS_le32-unknown-nacl := $(CFG_CXXFLAGS_le32-unknown-nacl) +CFG_GCCISH_LINK_FLAGS_le32-unknown-nacl := -static -pthread -lm +CFG_GCCISH_DEF_FLAG_le32-unknown-nacl := -Wl,--export-dynamic,--dynamic-list= +CFG_GCCISH_PRE_LIB_FLAGS_le32-unknown-nacl := -Wl,-no-whole-archive +CFG_GCCISH_POST_LIB_FLAGS_le32-unknown-nacl := +CFG_DEF_SUFFIX_le32-unknown-nacl := .le32.nacl.def +CFG_INSTALL_NAME_le32-unknown-nacl = +CFG_LIBUV_LINK_FLAGS_le32-unknown-nacl = +CFG_DISABLE_LIBUV_le32-unknown-nacl := 1 +CFG_EXE_SUFFIX_le32-unknown-nacl = .pexe +CFG_WINDOWSY_le32-unknown-nacl := +CFG_UNIXY_le32-unknown-nacl := 1 +CFG_NACLY_le32-unknown-nacl := 1 +CFG_PATH_MUNGE_le32-unknown-nacl := true +CFG_LDPATH_le32-unknown-nacl := +CFG_RUN_le32-unknown-nacl=$(2) +CFG_RUN_TARG_le32-unknown-nacl=$(call CFG_RUN_le32-unknown-nacl,,$(2)) +SHARED_LIBS_DISABLED_le32-unknown-nacl := 1 +RUNTIME_CFLAGS_le32-unknown-nacl:= -I$(CFG_NACL_CROSS_PATH)/include/pnacl +RUNTIME_DISABLE_ASM_le32-unknown-nacl := 1 +RUSTC_FLAGS_le32-unknown-nacl:= +RUSTC_CROSS_FLAGS_le32-unknown-nacl=-C cross-path=$(CFG_NACL_CROSS_PATH) --cfg "target_libc=\"newlib\"" -L $(CFG_NACL_CROSS_PATH)/lib/pnacl/Release -L $(CFG_NACL_CROSS_PATH)/toolchain/$(NACL_TOOLCHAIN_OS_PATH)_pnacl/lib/clang/3.6.0/lib/le32-nacl -L $(CFG_NACL_CROSS_PATH)/toolchain/$(NACL_TOOLCHAIN_OS_PATH)_pnacl/le32-nacl/usr/lib +CFG_GNU_TRIPLE_le32-unknown-nacl := le32-unknown-nacl + +LLVM_EXTRA_COMPONENTS_le32-unknown-nacl := NaClTransforms NaClAnalysis NaClBitWriter NaClBitReader irreader +LLVM_DISABLED_TARGETS_le32-unknown-nacl := aarch64 mips powerpc +# strdup isn't defined unless -std=gnu++11 is used +LLVM_FILTER_CXXFLAGS_le32-unknown-nacl := -std=c++11 +LLVM_EXTRA_CXXFLAGS_le32-unknown-nacl := -std=gnu++11 +LLVM_HAS_TARGETMACHINE_le32-unknown-nacl := 0 diff --git a/mk/llvm.mk b/mk/llvm.mk index d5b608e88daf8..027c7d7de6d6d 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -82,7 +82,7 @@ endif # LLVM linkage: LLVM_LINKAGE_PATH_$(1):=$$(abspath $$(RT_OUTPUT_DIR_$(1))/llvmdeps.rs) $$(LLVM_LINKAGE_PATH_$(1)): $(S)src/etc/mklldeps.py $$(LLVM_CONFIG_$(1)) - $(Q)$(CFG_PYTHON) "$$<" "$$@" "$$(LLVM_COMPONENTS)" "$$(CFG_ENABLE_LLVM_STATIC_STDCPP)" \ + $(Q)$(CFG_PYTHON) "$$<" "$$@" "$$(filter-out $$(LLVM_DISABLED_TARGETS_$(1)),$$(LLVM_COMPONENTS))" "$$(CFG_ENABLE_LLVM_STATIC_STDCPP)" \ $$(LLVM_CONFIG_$(1)) endef diff --git a/mk/main.mk b/mk/main.mk index 32d1fcf3968fc..6c1ddc7a7c988 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -273,6 +273,16 @@ endif LLVM_COMPONENTS=x86 arm aarch64 mips powerpc ipo bitreader bitwriter linker asmparser mcjit \ interpreter instrumentation +define DEF_ADD_LLVM_TARGET_COMPONENTS +LLVM_COMPONENTS += $$(LLVM_EXTRA_COMPONENTS_$(1)) +endef + +$(foreach target,$(CFG_TARGET), \ + $(eval $(call DEF_ADD_LLVM_TARGET_COMPONENTS,$(target)))) + +# deduplicate components: +LLVM_COMPONENTS:=$(shell echo $(LLVM_COMPONENTS) | tr ' ' '\n' | awk '!a[$$0]++' | tr '\n' ' ') + # Only build these LLVM tools LLVM_TOOLS=bugpoint llc llvm-ar llvm-as llvm-dis llvm-mc opt llvm-extract @@ -282,12 +292,22 @@ define DEF_LLVM_VARS ifeq ($$(CFG_LLVM_ROOT),) CFG_LLVM_BUILD_DIR_$(1):=$$(CFG_LLVM_BUILD_DIR_$(subst -,_,$(1))) CFG_LLVM_INST_DIR_$(1):=$$(CFG_LLVM_INST_DIR_$(subst -,_,$(1))) + +# We need to use a copy of llvm-config that can run on the build machine: +ifneq ($$(CFG_BUILD),$(1)) +LLVM_CONFIG_$(1):=$$(CFG_LLVM_BUILD_DIR_$(subst -,_,$(1)))/BuildTools/$$(CFG_LLVM_BUILD_SUBDIR_$(subst -,_,$(1)))/bin/llvm-config$$(X_$$(CFG_BUILD)) else -CFG_LLVM_INST_DIR_$(1):=$$(CFG_LLVM_ROOT) +# Any rules that depend on LLVM should depend on LLVM_CONFIG +LLVM_CONFIG_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-config$$(X_$(1)) endif +else +CFG_LLVM_INST_DIR_$(1):=$$(CFG_LLVM_ROOT) # Any rules that depend on LLVM should depend on LLVM_CONFIG LLVM_CONFIG_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-config$$(X_$(1)) +endif + + LLVM_MC_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-mc$$(X_$(1)) LLVM_AR_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-ar$$(X_$(1)) LLVM_VERSION_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --version) @@ -295,19 +315,26 @@ LLVM_BINDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --bindir) LLVM_INCDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --includedir) LLVM_LIBDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libdir) LLVM_LIBDIR_RUSTFLAGS_$(1)=-L "$$(LLVM_LIBDIR_$(1))" -LLVM_LIBS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libs $$(LLVM_COMPONENTS)) + +LLVM_LIBS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libs $$(filter-out $$(LLVM_DISABLED_TARGETS_$(1)), \ + $$(LLVM_COMPONENTS)) LLVM_LDFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --ldflags) + ifeq ($$(findstring freebsd,$(1)),freebsd) # On FreeBSD, it may search wrong headers (that are for pre-installed LLVM), # so we replace -I with -iquote to ensure that it searches bundled LLVM first. LLVM_CXXFLAGS_$(1)=$$(subst -I, -iquote , $$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags)) else -LLVM_CXXFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags) +LLVM_CXXFLAGS_$(1)=$$(filter-out $$(LLVM_FILTER_CXXFLAGS_$(1)), \ + $$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags)) \ + $$(LLVM_EXTRA_CXXFLAGS_$(1)) endif + LLVM_HOST_TRIPLE_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --host-target) LLVM_AS_$(1)=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-as$$(X_$(1)) LLC_$(1)=$$(CFG_LLVM_INST_DIR_$(1))/bin/llc$$(X_$(1)) +OPT_$(1)=$$(CFG_LLVM_INST_DIR_$(1))/bin/opt$$(X_$(1)) endef diff --git a/mk/platform.mk b/mk/platform.mk index 8a5e58c46f676..96f9d1417c14a 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -139,7 +139,9 @@ define FILTER_FLAGS endif endef -$(foreach target,$(CFG_TARGET), \ +# pnacl-clang doesn't accept `-Qunused-arguments` despite using clang under the +# hood. +$(foreach target,$(filter-out le32-unknown-nacl,$(CFG_TARGET)), \ $(eval $(call FILTER_FLAGS,$(target)))) # Configure various macros to pass gcc or cl.exe style arguments diff --git a/mk/prepare.mk b/mk/prepare.mk index 573b7ac79fd1a..4f164651283c7 100644 --- a/mk/prepare.mk +++ b/mk/prepare.mk @@ -140,7 +140,8 @@ prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(call PREPARE_DIR,$$(PREPARE_WORKING_DEST_LIB_DIR)) \ $$(call PREPARE_DIR,$$(PREPARE_DEST_BIN_DIR)) \ $$(foreach crate,$$(TARGET_CRATES), \ - $$(if $$(or $$(findstring 1, $$(ONLY_RLIB_$$(crate))),$$(findstring 1,$$(CFG_INSTALL_ONLY_RLIB_$(2)))),, \ + $$(if $$(SHARED_LIBS_DISABLED_$(2)),,\ + $$(if $$(or $$(findstring 1, $$(ONLY_RLIB_$$(crate))),$$(findstring 1,$$(CFG_INSTALL_ONLY_RLIB_$(2)))),, \ $$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate)))) \ $$(call PREPARE_LIB,$$(call CFG_RLIB_GLOB,$$(crate)))) \ $$(if $$(findstring $(2),$$(CFG_HOST)), \ diff --git a/mk/rt.mk b/mk/rt.mk index 777a2a0fd3b4b..a8e6711f91841 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -54,11 +54,19 @@ NATIVE_DEPS_miniz_$(1) = miniz.c NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \ rust_android_dummy.c NATIVE_DEPS_rustrt_native_$(1) := \ - rust_try.ll \ - arch/$$(HOST_$(1))/record_sp.S + rust_try.ll + +ifeq ($(1),le32-unknown-nacl) +NATIVE_DEPS_rustrt_native_$(1) += crtbegin.bc crti.bc +endif + NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c + +ifneq ($$(RUNTIME_DISABLE_ASM_$(1)), 1) +NATIVE_DEPS_rustrt_native_$(1) += arch/$$(HOST_$(1))/record_sp.S NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S +endif ################################################################################ # You shouldn't find it that necessary to edit anything below this line. @@ -69,6 +77,7 @@ NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S RT_OUTPUT_DIR_$(1) := $(1)/rt +ifneq ($(1), le32-unknown-nacl) $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.ll $$(MKFILE_DEPS) \ $$(LLVM_CONFIG_$$(CFG_BUILD)) @mkdir -p $$(@D) @@ -76,6 +85,25 @@ $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.ll $$(MKFILE_DEPS) \ $$(Q)$$(LLC_$$(CFG_BUILD)) $$(CFG_LLC_FLAGS_$(1)) \ -filetype=obj -mtriple=$$(CFG_LLVM_TARGET_$(1)) \ -relocation-model=pic -o $$@ $$< +else +# le32-unknown-nacl doesn't have a target machine, so llc chokes. +# Fortunately, PNaCl object files are just bitcode. +$$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.ll \ + $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD)) + @mkdir -p $$(@D) + @$$(call E, compile: $$@) + $$(OPT_$$(CFG_BUILD)) -Oz -o $$@ $$< +$$(RT_OUTPUT_DIR_$(1))/%.o: $(CFG_NACL_CROSS_PATH)/toolchain/$(NACL_TOOLCHAIN_OS_PATH)_pnacl/lib/clang/3.7.0/lib/le32-nacl/%.bc \ + $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD)) + @mkdir -p $$(@D) + @$$(call E, compile: $$@) + $$(OPT_$$(CFG_BUILD)) -Oz -o $$@ $$< +$$(RT_OUTPUT_DIR_$(1))/%.o: $(CFG_NACL_CROSS_PATH)/toolchain/$(NACL_TOOLCHAIN_OS_PATH)_pnacl/le32-nacl/lib/%.bc \ + $$(MKFILE_DEPS) $$(LLVM_CONFIG_$$(CFG_BUILD)) + @mkdir -p $$(@D) + @$$(call E, compile: $$@) + $$(OPT_$$(CFG_BUILD)) -Oz -o $$@ $$< +endif $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.c $$(MKFILE_DEPS) @mkdir -p $$(@D) @@ -106,6 +134,7 @@ OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.c=.o) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.cpp=.o) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.ll=.o) OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.S=.o) +OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.bc=.o) NATIVE_$(2)_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$(2)) $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1)) @$$(call E, link: $$@) @@ -285,6 +314,12 @@ $$(BACKTRACE_LIB_$(1)): touch $$@ else +ifeq ($$(CFG_NACLY_$(1)),1) +# See comment above +$$(BACKTRACE_LIB_$(1)): + touch $$@ +else + ifdef CFG_ENABLE_FAST_MAKE BACKTRACE_DEPS := $(S)/.gitmodules else @@ -323,6 +358,7 @@ $$(BACKTRACE_LIB_$(1)): $$(BACKTRACE_BUILD_DIR_$(1))/Makefile $$(MKFILE_DEPS) INCDIR=$(S)src/libbacktrace $$(Q)cp $$(BACKTRACE_BUILD_DIR_$(1))/.libs/libbacktrace.a $$@ +endif # endif for nacly endif # endif for windowsy endif # endif for ios endif # endif for darwin diff --git a/mk/rustllvm.mk b/mk/rustllvm.mk index 50d993701421e..4b170aeeb96ba 100644 --- a/mk/rustllvm.mk +++ b/mk/rustllvm.mk @@ -35,6 +35,14 @@ RUSTLLVM_OBJS_OBJS_$(1) := $$(RUSTLLVM_OBJS_CS_$(1):rustllvm/%.cpp=$(1)/rustllvm # handling flag with the `EHsc` argument here as well. ifeq ($$(findstring msvc,$(1)),msvc) EXTRA_RUSTLLVM_CXXFLAGS_$(1) := //EHsc +else + +ifneq ($(filter-out le32-unknown-nacl,$(CFG_TARGET)),$(CFG_TARGET)) +EXTRA_RUSTLLVM_CXXFLAGS_$(1) := -DENABLE_PNACL=1 +else +EXTRA_RUSTLLVM_CXXFLAGS_$(1) := -DENABLE_PNACL=0 +endif + endif $$(RT_OUTPUT_DIR_$(1))/$$(call CFG_STATIC_LIB_NAME_$(1),rustllvm): \ diff --git a/mk/tests.mk b/mk/tests.mk index 185cc9b2f4c58..1769117254d89 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -436,6 +436,18 @@ $$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)): \ exit 101; \ fi endef +define DEF_TEST_CRATE_RULES_le32-unknown-nacl +check-stage$(1)-T-$(2)-H-$(3)-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)) +$$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)): \ + $(3)/stage$(1)/test/$(4)test-$(2)$$(X_$(2)) + @$$(call E, run: rust_pnacl_trans $$<) + $$(Q)$$(RPATH_VAR$(1)_T_$(2)_H_$(3)) \ + $$(HBIN$(1)_H_$(3))/rust_pnacl_trans$$(X_$(3)) \ + --cross-path=$$(CFG_NACL_CROSS_PATH) $$< \ + -o $$<.nexe + @$$(call E, run: $$<.nexe) + $$(Q)$$(CFG_NACL_CROSS_PATH)/tools/sel_ldr.py -- $$<.nexe +endef define DEF_TEST_CRATE_RULES_null check-stage$(1)-T-$(2)-H-$(3)-$(4)-exec: $$(call TEST_OK_FILE,$(1),$(2),$(3),$(4)) @@ -457,8 +469,10 @@ $(foreach host,$(CFG_HOST), \ $(eval $(call DEF_TEST_CRATE_RULES_android,$(stage),$(target),$(host),$(crate))), \ $(eval $(call DEF_TEST_CRATE_RULES_null,$(stage),$(target),$(host),$(crate))) \ ), \ + $(if $(findstring $(target),"le32-unknown-nacl"), \ + $(eval $(call DEF_TEST_CRATE_RULES_le32-unknown-nacl,$(stage),$(target),$(host),$(crate))), \ $(eval $(call DEF_TEST_CRATE_RULES,$(stage),$(target),$(host),$(crate))) \ - )))))) + ))))))) ###################################################################### # Rules for the compiletest tests (rpass, rfail, etc.) @@ -643,6 +657,7 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \ --rustc-path $$(HBIN$(1)_H_$(3))/rustc$$(X_$(3)) \ --rustdoc-path $$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \ --llvm-bin-path $(CFG_LLVM_INST_DIR_$(CFG_BUILD))/bin \ + --nacl-cross-path=$(CFG_NACL_CROSS_PATH) \ --aux-base $$(S)src/test/auxiliary/ \ --stage-id stage$(1)-$(2) \ --target $(2) \ @@ -656,7 +671,7 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \ --host-rustcflags "$(RUSTC_FLAGS_$(3)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(3))" \ --lldb-python-dir=$(CFG_LLDB_PYTHON_DIR) \ --target-rustcflags "$(RUSTC_FLAGS_$(2)) $$(CTEST_RUSTC_FLAGS) -L $$(RT_OUTPUT_DIR_$(2))" \ - $$(CTEST_TESTARGS) + $$(CTEST_TESTARGS) $$(CTEST_TARGETARGS_$(2)) ifdef CFG_VALGRIND_RPASS ifdef GOOD_VALGRIND_$(2) diff --git a/mk/util.mk b/mk/util.mk index 3664236eabdd8..f0b4c0f8579ff 100644 --- a/mk/util.mk +++ b/mk/util.mk @@ -20,3 +20,11 @@ print-%: @echo $*=$($*) S := $(CFG_SRC_DIR) + +ifeq ($(CFG_OSTYPE),pc-windows-gnu) + NACL_TOOLCHAIN_OS_PATH:=win +else ifeq ($(CFG_OSTYPE),apple-darwin) + NACL_TOOLCHAIN_OS_PATH:=mac +else + NACL_TOOLCHAIN_OS_PATH:=linux +endif \ No newline at end of file diff --git a/src/compiletest/common.rs b/src/compiletest/common.rs index 3f34c89d18fb2..07557f4c912d7 100644 --- a/src/compiletest/common.rs +++ b/src/compiletest/common.rs @@ -154,6 +154,15 @@ pub struct Config { // the path containing LLDB's Python module pub lldb_python_dir: Option, + // NaCl Pepper SDK path + pub nacl_cross_path: Option, + // Explain what's going on pub verbose: bool } + +impl Config { + pub fn targeting_pnacl(&self) -> bool { + "le32-unknown-nacl" == &self.target[..] + } +} diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index 9d575675cc8c9..d0f573468b140 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -88,6 +88,7 @@ pub fn parse_config(args: Vec ) -> Config { optopt("", "adb-path", "path to the android debugger", "PATH"), optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"), optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"), + optopt("", "nacl-cross-path", "path to NaCl Pepper sdk", "PATH"), optflag("h", "help", "show this message")); assert!(!args.is_empty()); @@ -152,6 +153,7 @@ pub fn parse_config(args: Vec ) -> Config { gdb_version: extract_gdb_version(matches.opt_str("gdb-version")), lldb_version: extract_lldb_version(matches.opt_str("lldb-version")), android_cross_path: opt_path(matches, "android-cross-path"), + nacl_cross_path: matches.opt_str("nacl-cross-path").map(|s| Path::new(&s).to_path_buf() ), adb_path: opt_str2(matches.opt_str("adb-path")), adb_test_dir: format!("{}/{}", opt_str2(matches.opt_str("adb-test-dir")), @@ -229,6 +231,11 @@ pub fn run_tests(config: &Config) { env::set_var("RUST_TEST_THREADS","1"); } + if config.targeting_pnacl() && config.mode == DebugInfoGdb { + // Like android, PNaCl/NaCl also uses a remote debugger. + env::set_var("RUST_TEST_TASKS","1"); + } + match config.mode { DebugInfoLldb => { // Some older versions of LLDB seem to have problems with multiple diff --git a/src/compiletest/header.rs b/src/compiletest/header.rs index a648e51497e79..e772ea653de24 100644 --- a/src/compiletest/header.rs +++ b/src/compiletest/header.rs @@ -235,6 +235,7 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool { !parse_name_directive(ln, &ignore_architecture(config)) && !parse_name_directive(ln, &ignore_stage(config)) && !parse_name_directive(ln, &ignore_env(config)) && + !(config.targeting_pnacl() && parse_name_directive(ln, "ignore-pnacl")) && !(config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) && !(config.target != config.host && parse_name_directive(ln, "ignore-cross-compile")) && !ignore_gdb(config, ln) && diff --git a/src/compiletest/runtest.rs b/src/compiletest/runtest.rs index 185a969cbc627..713634b713ddd 100644 --- a/src/compiletest/runtest.rs +++ b/src/compiletest/runtest.rs @@ -26,7 +26,7 @@ use std::io::BufReader; use std::io::prelude::*; use std::net::TcpStream; use std::path::{Path, PathBuf}; -use std::process::{Command, Output, ExitStatus}; +use std::process::{Command, Output, ExitStatus, Child}; pub fn run(config: Config, testfile: &Path) { match &*config.target { @@ -494,6 +494,147 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) { } } + _ if config.targeting_pnacl() => { + use std::env::consts::ARCH; + fn make_absolute(p: &Path) -> PathBuf { + use std::env; + env::current_dir() + .map(|cwd| cwd.join(p) ) + .unwrap() + } + + let arch_prefix = match ARCH { + "x86" => "i686", + _ => ARCH, + }; + #[cfg(windows)] + fn gdb_suffix() -> &'static str { ".exe" } + #[cfg(unix)] + fn gdb_suffix() -> &'static str { "" } + + let gdb = format!("{}-nacl-gdb{}", arch_prefix, gdb_suffix()); + + let _sel_ldr_process = match pnacl_exec_compiled_test(config, + props, + testfile, + props.exec_env.clone(), + true) { + ProcResOrProcessResult::ProcResResult(_) => unreachable!(), + ProcResOrProcessResult::ProcessResult(p) => p, + }; + + cmds = cmds.replace("run", "continue").to_string(); + + let pexe_path = make_absolute(&output_base_name(config, testfile)); + let nexe_path = + // add an extension, don't replace it: + format!("{:?}.nexe", pexe_path); + + // write debugger script + let rust_src_root = find_rust_src_root(config) + .expect("Could not find Rust source root"); + let rust_pp_module_rel_path = Path::new("./src/etc"); + let rust_pp_module_abs_path = rust_src_root + .join(rust_pp_module_rel_path); + let rust_pp_module_abs_path = format!("{:?}", rust_pp_module_abs_path); + + // write debugger script + let mut script_str = String::with_capacity(2048); + + script_str.push_str("set charset UTF-8\n"); + script_str.push_str("target remote :4014\n"); + + + // Add the directory containing the pretty printers to + // GDB's script auto loading safe path + script_str.push_str( + &format!("add-auto-load-safe-path {}\n", + rust_pp_module_abs_path.replace("\\", "\\\\"))[..]); + + // The following line actually doesn't have to do anything with + // pretty printing, it just tells GDB to print values on one line: + script_str.push_str("set print pretty off\n"); + + // Add the pretty printer directory to GDB's source-file search path + script_str.push_str(&format!("directory {}\n", rust_pp_module_abs_path)[..]); + + // Load the target executable + script_str.push_str(&format!("file {}\n", + nexe_path.replace("\\", "\\\\"))[..]); + + // Add line breakpoints + for line in breakpoint_lines.iter() { + script_str.push_str(&format!("break '{:?}':{}\n", + testfile.file_name().unwrap(), + *line)[..]); + } + + script_str.push_str(&cmds[..]); + script_str.push_str("quit\n"); + + debug!("script_str = {}", script_str); + dump_output_file(config, + testfile, + &script_str[..], + "debugger.script"); + + let cross_path = config.nacl_cross_path + .clone() + .expect("need the NaCl SDK path!"); + let mut gdb_path = cross_path.clone(); + gdb_path.push("toolchain"); + gdb_path.push(&({ + let mut s = pnacl_toolchain_prefix(); + s.push_str("_x86_newlib"); + s + })); + gdb_path.push("bin"); + gdb_path.push(&gdb); + let gdb_path = gdb_path; + + loop { + // wait for a quarter second for sel_ldr to start + ::std::thread::sleep_ms(250); + if TcpStream::connect("127.0.0.1:4014").is_ok() { + break; + } + } + + let debugger_script = make_out_name(config, testfile, "debugger.script"); + // FIXME (#9639): This needs to handle non-utf8 paths + let debugger_opts = + vec!("-quiet".to_string(), + "-batch".to_string(), + "-nx".to_string(), + format!("-command={}", debugger_script.display())); + + let procsrv::Result { + out, + err, + status + } = procsrv::run("", + &format!("{}", gdb_path.display())[..], + None, + &debugger_opts[..], + vec!(("".to_string(), "".to_string())), + None) + .expect(&format!("failed to exec `{:?}`", gdb_path)[..]); + let cmdline = { + let cmdline = make_cmdline("", + &gdb[..], + &debugger_opts[..]); + logv(config, format!("executing {}", cmdline)); + cmdline + }; + + debugger_run_result = ProcRes { + status: Status::Normal(status), + stdout: out, + stderr: err, + cmdline: cmdline + }; + } + _=> { let rust_src_root = find_rust_src_root(config) .expect("Could not find Rust source root"); @@ -1190,6 +1331,14 @@ fn exec_compiled_test(config: &Config, props: &TestProps, _arm_exec_compiled_test(config, props, testfile, env) } + "le32-unknown-nacl" => { + match pnacl_exec_compiled_test(config, props, + testfile, env, false) { + ProcResOrProcessResult::ProcResResult(p) => p, + ProcResOrProcessResult::ProcessResult(_) => unreachable!(), + } + } + _=> { let aux_dir = aux_output_dir_name(config, testfile); compose_and_run(config, @@ -1220,6 +1369,8 @@ fn compose_and_run_compiler(config: &Config, props: &TestProps, let aux_props = header::load_props(&abs_ab); let mut crate_type = if aux_props.no_prefer_dynamic { Vec::new() + } else if config.targeting_pnacl() { + vec!("--crate-type=rlib".to_string()) } else { // We primarily compile all auxiliary libraries as dynamic libraries // to avoid code size bloat and large binaries as much as possible @@ -1318,7 +1469,7 @@ fn make_compile_args(config: &Config, config.build_base.to_str().unwrap().to_string(), format!("--target={}", target)); args.push_all(&extras); - if !props.no_prefer_dynamic { + if !props.no_prefer_dynamic && !config.targeting_pnacl() { args.push("-C".to_string()); args.push("prefer-dynamic".to_string()); } @@ -1475,9 +1626,12 @@ fn output_testname(testfile: &Path) -> PathBuf { } fn output_base_name(config: &Config, testfile: &Path) -> PathBuf { - config.build_base + let p = config.build_base .join(&output_testname(testfile)) - .with_extension(&config.stage_id) + .with_extension(&config.stage_id); + if config.targeting_pnacl() && config.mode == DebugInfoGdb { + p.with_extension("debug.pexe") + } else { p } } fn maybe_dump_to_stdout(config: &Config, out: &str, err: &str) { @@ -1673,6 +1827,173 @@ fn _arm_push_aux_shared_library(config: &Config, testfile: &Path) { } } +enum ProcResOrProcessResult { + ProcResResult(ProcRes), + ProcessResult(Child), +} +fn pnacl_exec_compiled_test(config: &Config, props: &TestProps, + testfile: &Path, env: Vec<(String, String)>, + run_background: bool) -> ProcResOrProcessResult { + fn make_absolute(p: &Path) -> PathBuf { + use std::env; + env::current_dir() + .map(|cwd| cwd.join(p) ) + .unwrap() + } + + let cross_path = config.nacl_cross_path + .clone() + .expect("need the NaCl SDK path!"); + + let pexe_path = make_absolute(&output_base_name(config, testfile)); + let nexe_path = + // add an extension, don't replace it: + format!("{}.nexe", pexe_path.display()); + let nexe_path = Path::new(&nexe_path); + + #[cfg(target_arch = "x86")] + fn get_nacl_arch() -> &'static str { + "x86" + } + #[cfg(target_arch = "x86_64")] + fn get_nacl_arch() -> &'static str { + "x86-64" + } + #[cfg(target_arch = "arm")] + fn get_nacl_arch() -> &'static str { + "armv7" + } + #[cfg(target_arch = "mips")] + fn get_nacl_arch() -> &'static str { + "mipsel" + } + #[cfg(not(any(target_arch = "x86", + target_arch = "x86_64", + target_arch = "arm", + target_arch = "mips")))] + fn get_nacl_arch() -> &'static str { + unreachable!("unknown host arch for NaCl"); + } + + let pnacl_trans_args = vec!(format!("-o{}", nexe_path.display()), + format!("{}", pexe_path.display()), + "-arch".to_string(), get_nacl_arch().to_string(), + "-O0".to_string(), // FIXME: not good for benchmarks. + "-threads=seq".to_string(), + "--allow-llvm-bitcode-input".to_string(), + ); + let pnacl_trans = { + let mut pnacl_trans = cross_path.clone(); + pnacl_trans.push("toolchain"); + pnacl_trans.push(&({ + let mut s = pnacl_toolchain_prefix(); + s.push_str("_pnacl"); + s + })); + pnacl_trans.push("bin"); + pnacl_trans.push("pnacl-translate"); + if let Some(str) = config.rustc_path.extension() { + pnacl_trans.with_extension(str) + } else { + pnacl_trans + } + }; + match program_output(config, + testfile, + &config.compile_lib_path[..], + pnacl_trans.display().to_string(), + None, + pnacl_trans_args, + env.clone(), + None) { + ProcRes { ref status, .. } if status.success() => { } + res => { + return ProcResOrProcessResult::ProcResResult(res); + } + } + + let _ = fs::remove_file(&pexe_path); + + let tools = cross_path.join("tools"); + let nacl_helper_bootstrap = tools.join("nacl_helper_bootstrap_x86_64"); + let sel_ldr_bin = tools.join("sel_ldr_x86_64"); + let irt_core = tools.join("irt_core_x86_64.nexe"); + + let mut sel_ldr_args = vec!(sel_ldr_bin.display().to_string(), + "--r_debug=0xXXXXXXXXXXXXXXXX".to_string(), + "--reserved_at_zero=0xXXXXXXXXXXXXXXXX".to_string(), + "-a".to_string(), + "-B".to_string(), + irt_core.display().to_string()); + if run_background && config.mode == DebugInfoGdb { + sel_ldr_args.push("-g".to_string()); + } + sel_ldr_args.push(nexe_path.display().to_string()); + + let ProcArgs { + args: run_args, + .. + } = make_run_args(config, props, testfile); + sel_ldr_args.extend(run_args.into_iter()); + + let sel_ldr_dsp = nacl_helper_bootstrap.display().to_string(); + + let mut process = procsrv::run_background("", + &sel_ldr_dsp[..], + None, + &sel_ldr_args[..], + env, + None) + .expect(&format!("failed to exec `{}`", sel_ldr_dsp)[..]); + + if !run_background { + let status = process.wait() + .unwrap(); + let mut stdout = String::new(); + process.stdout + .as_mut() + .unwrap() + .read_to_string(&mut stdout) + .unwrap(); + + let mut stderr = String::new(); + process.stderr + .as_mut() + .unwrap() + .read_to_string(&mut stderr) + .unwrap(); + return ProcResOrProcessResult::ProcResResult(ProcRes { + status: Status::Normal(status), + stdout: stdout, + stderr: stderr, + cmdline: make_cmdline("", + &sel_ldr_dsp[..], + &sel_ldr_args[..]), + }); + } else { + return ProcResOrProcessResult::ProcessResult(process); + } +} + +#[cfg(target_os = "linux")] +fn pnacl_toolchain_prefix() -> String { + "linux".to_string() +} +#[cfg(target_os = "macos")] +fn pnacl_toolchain_prefix() -> String { + "mac".to_string() +} +#[cfg(windows)] +fn pnacl_toolchain_prefix() -> String { + "win".to_string() +} +#[cfg(all(not(windows), + not(target_os = "linux"), + not(target_os = "macos")))] +fn pnacl_toolchain_prefix() -> String { + unimplemented!(); +} + // codegen tests (using FileCheck) fn compile_test_and_save_ir(config: &Config, props: &TestProps, diff --git a/src/compiletest/util.rs b/src/compiletest/util.rs index 184d62db45114..9d0959bca8fb0 100644 --- a/src/compiletest/util.rs +++ b/src/compiletest/util.rs @@ -21,6 +21,7 @@ const OS_TABLE: &'static [(&'static str, &'static str)] = &[ ("ios", "ios"), ("linux", "linux"), ("mingw32", "windows"), + ("nacl", "nacl"), ("openbsd", "openbsd"), ("win32", "windows"), ("windows", "windows"), @@ -34,6 +35,7 @@ const ARCH_TABLE: &'static [(&'static str, &'static str)] = &[ ("hexagon", "hexagon"), ("i386", "x86"), ("i686", "x86"), + ("le32", "le32"), ("mips", "mips"), ("msp430", "msp430"), ("powerpc", "powerpc"), diff --git a/src/driver/driver.rs b/src/driver/driver.rs index c5c58bb49ac36..159df90643036 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs @@ -17,4 +17,14 @@ extern crate rustdoc as this; #[cfg(rustc)] extern crate rustc_driver as this; +#[cfg_attr(target_os = "nacl", main_link_name = "nacl_main")] fn main() { this::main() } + +#[cfg(target_os = "nacl")] +#[link(name = "ppapi_cpp", kind = "static")] +#[link(name = "ppapi_simple_cpp", kind = "static")] +#[link(name = "ppapi_stub", kind = "static")] +#[link(name = "cli_main", kind = "static")] +#[link(name = "tar", kind = "static")] +#[link(name = "nacl_spawn", kind = "static")] +extern { } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index e155dc86f3251..afe3541c131a6 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -140,7 +140,8 @@ const MIN_ALIGN: usize = 8; not(feature = "external_crate"), any(target_arch = "x86", target_arch = "x86_64", - target_arch = "aarch64")))] + target_arch = "aarch64", + target_arch = "le32")))] const MIN_ALIGN: usize = 16; #[cfg(feature = "external_funcs")] diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 16094f2e6cc3c..32b91f1ee0c87 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -41,6 +41,10 @@ #![unstable(feature = "core")] #![allow(missing_docs)] +#![allow(unused_imports)] + +use f32; +use f64; use marker::Sized; @@ -395,11 +399,6 @@ extern "rust-intrinsic" { /// Returns the square root of an `f64` pub fn sqrtf64(x: f64) -> f64; - /// Raises an `f32` to an integer power. - pub fn powif32(a: f32, x: i32) -> f32; - /// Raises an `f64` to an integer power. - pub fn powif64(a: f64, x: i32) -> f64; - /// Returns the sine of an `f32`. pub fn sinf32(x: f32) -> f32; /// Returns the sine of an `f64`. @@ -487,28 +486,16 @@ extern "rust-intrinsic" { /// Returns the nearest integer to an `f64`. Rounds half-way cases away from zero. pub fn roundf64(x: f64) -> f64; - /// Returns the number of bits set in a `u8`. - pub fn ctpop8(x: u8) -> u8; - /// Returns the number of bits set in a `u16`. - pub fn ctpop16(x: u16) -> u16; /// Returns the number of bits set in a `u32`. pub fn ctpop32(x: u32) -> u32; /// Returns the number of bits set in a `u64`. pub fn ctpop64(x: u64) -> u64; - /// Returns the number of leading bits unset in a `u8`. - pub fn ctlz8(x: u8) -> u8; - /// Returns the number of leading bits unset in a `u16`. - pub fn ctlz16(x: u16) -> u16; /// Returns the number of leading bits unset in a `u32`. pub fn ctlz32(x: u32) -> u32; /// Returns the number of leading bits unset in a `u64`. pub fn ctlz64(x: u64) -> u64; - /// Returns the number of trailing bits unset in a `u8`. - pub fn cttz8(x: u8) -> u8; - /// Returns the number of trailing bits unset in a `u16`. - pub fn cttz16(x: u16) -> u16; /// Returns the number of trailing bits unset in a `u32`. pub fn cttz32(x: u32) -> u32; /// Returns the number of trailing bits unset in a `u64`. @@ -520,6 +507,35 @@ extern "rust-intrinsic" { pub fn bswap32(x: u32) -> u32; /// Reverses the bytes in a `u64`. pub fn bswap64(x: u64) -> u64; +} +/// The following intrinsics are disallowed on PNaCl: +/// We sneakily redirect them to libm or to a larger sized intrinsic. +#[cfg(not(target_os = "nacl"))] +extern "rust-intrinsic" { + + /// Raises an `f32` to an `f32` power. + pub fn powif32(a: f32, x: i32) -> f32; + /// Raises an `f64` to an `f64` power. + pub fn powif64(a: f64, x: i32) -> f64; + + /// Returns the number of bits set in a `u8`. + pub fn ctpop8(x: u8) -> u8; + /// Returns the number of bits set in a `u16`. + pub fn ctpop16(x: u16) -> u16; + + /// Returns the number of leading bits unset in a `u8`. + pub fn ctlz8(x: u8) -> u8; + /// Returns the number of leading bits unset in a `u16`. + pub fn ctlz16(x: u16) -> u16; + + /// Returns the number of trailing bits unset in a `u8`. + pub fn cttz8(x: u8) -> u8; + /// Returns the number of trailing bits unset in a `u16`. + pub fn cttz16(x: u16) -> u16; + +} + +extern "rust-intrinsic" { /// Performs checked `i8` addition. pub fn i8_add_with_overflow(x: i8, y: i8) -> (i8, bool); @@ -600,3 +616,23 @@ extern "rust-intrinsic" { /// cast to a `u64`; if `T` has no discriminant, returns 0. pub fn discriminant_value(v: &T) -> u64; } + +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn powif32(a: f32, x: i32) -> f32 { powf64(a as f64, x as f64) as f32 } +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn powif64(a: f64, x: i32) -> f64 { powf64(a, x as f64) } + +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn ctpop8(x: u8) -> u8 { ctpop32(x as u32) as u8 } +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn ctpop16(x: u16) -> u16 { ctpop32(x as u32) as u16 } + +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn ctlz8(x: u8) -> u8 { ctlz32((x as u32) << 24) as u8 } +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn ctlz16(x: u16) -> u16 { ctlz32((x as u32) << 16) as u16 } + +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn cttz8(x: u8) -> u8 { cttz32(x as u32) as u8 } +#[cfg(target_os = "nacl")] +#[inline] pub unsafe fn cttz16(x: u16) -> u16 { cttz32(x as u32) as u16 } diff --git a/src/libcore/simd.rs b/src/libcore/simd.rs index 7b55ba49a07f7..30876cc314e0b 100644 --- a/src/libcore/simd.rs +++ b/src/libcore/simd.rs @@ -58,6 +58,8 @@ pub struct i16x8(pub i16, pub i16, pub i16, pub i16, #[repr(C)] pub struct i32x4(pub i32, pub i32, pub i32, pub i32); +// The PNaCl ABI doesn't currently allow this type. +#[cfg(not(all(target_os = "nacl", target_arch = "le32")))] #[unstable(feature = "core")] #[simd] #[derive(Copy, Clone, Debug)] @@ -86,6 +88,8 @@ pub struct u16x8(pub u16, pub u16, pub u16, pub u16, #[repr(C)] pub struct u32x4(pub u32, pub u32, pub u32, pub u32); +// The PNaCl ABI doesn't currently allow this type. +#[cfg(not(all(target_os = "nacl", target_arch = "le32")))] #[unstable(feature = "core")] #[simd] #[derive(Copy, Clone, Debug)] @@ -98,6 +102,8 @@ pub struct u64x2(pub u64, pub u64); #[repr(C)] pub struct f32x4(pub f32, pub f32, pub f32, pub f32); +// The PNaCl ABI doesn't currently allow this type. +#[cfg(not(all(target_os = "nacl", target_arch = "le32")))] #[unstable(feature = "core")] #[simd] #[derive(Copy, Clone, Debug)] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 4a715ca621cc2..de49f6924c755 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -83,6 +83,7 @@ pub mod back { pub use rustc_back::abi; pub use rustc_back::archive; pub use rustc_back::arm; + pub use rustc_back::le32; pub use rustc_back::mips; pub use rustc_back::mipsel; pub use rustc_back::rpath; @@ -102,6 +103,7 @@ pub mod middle { pub mod check_loop; pub mod check_match; pub mod check_rvalues; + pub mod check_no_asm; pub mod const_eval; pub mod dataflow; pub mod dead; diff --git a/src/librustc/middle/check_no_asm.rs b/src/librustc/middle/check_no_asm.rs new file mode 100644 index 0000000000000..139702d7ba3b5 --- /dev/null +++ b/src/librustc/middle/check_no_asm.rs @@ -0,0 +1,40 @@ +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +/// Run over the whole crate and check for ExprInlineAsm. +/// Inline asm isn't allowed on PNaCl, so we reject it here. + +use session::Session; + +use syntax::ast; +use syntax::visit::Visitor; +use syntax::visit; + +pub fn check_crate(sess: &Session, krate: &ast::Crate) { + if !sess.target.target.options.no_asm { return; } + + visit::walk_crate(&mut CheckNoAsm { sess: sess, }, krate); +} + +#[derive(Copy, Clone)] +struct CheckNoAsm<'a> { + sess: &'a Session, +} + +impl<'a, 'v> Visitor<'v> for CheckNoAsm<'a> { + fn visit_expr(&mut self, e: &ast::Expr) { + match e.node { + ast::ExprInlineAsm(_) => self.sess.span_err(e.span, + "asm! is unsupported on this target"), + _ => {}, + } + visit::walk_expr(self, e) + } +} diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs index 1aa2ca9115d2f..8f8dd2435a681 100644 --- a/src/librustc/middle/dead.rs +++ b/src/librustc/middle/dead.rs @@ -397,7 +397,7 @@ fn create_and_seed_worklist(tcx: &ty::ctxt, // Seed entry point match *tcx.sess.entry_fn.borrow() { - Some((id, _)) => worklist.push(id), + Some((id, _, _)) => worklist.push(id), None => () } diff --git a/src/librustc/middle/entry.rs b/src/librustc/middle/entry.rs index 0ce9db1c80f3f..0dd678eb0738a 100644 --- a/src/librustc/middle/entry.rs +++ b/src/librustc/middle/entry.rs @@ -27,13 +27,13 @@ struct EntryContext<'a, 'ast: 'a> { main_name: Name, // The top-level function called 'main' - main_fn: Option<(NodeId, Span)>, + main_fn: Option<(NodeId, token::InternedString, Span)>, // The function that has attribute named 'main' - attr_main_fn: Option<(NodeId, Span)>, + attr_main_fn: Option<(NodeId, token::InternedString, Span)>, // The function that has the attribute 'start' on it - start_fn: Option<(NodeId, Span)>, + start_fn: Option<(NodeId, token::InternedString, Span)>, // The functions that one might think are 'main' but aren't, e.g. // main functions not defined at the top level. For diagnostics. @@ -84,7 +84,11 @@ fn find_item(item: &Item, ctxt: &mut EntryContext) { if path.count() == 1 { // This is a top-level function so can be 'main' if ctxt.main_fn.is_none() { - ctxt.main_fn = Some((item.id, item.span)); + let link_name = + attr::first_attr_value_str_by_name(&item.attrs, + "main_link_name") + .unwrap_or_else(|| token::get_name(ctxt.main_name)); + ctxt.main_fn = Some((item.id, link_name, item.span)); } else { span_err!(ctxt.session, item.span, E0136, "multiple 'main' functions"); @@ -98,7 +102,10 @@ fn find_item(item: &Item, ctxt: &mut EntryContext) { if attr::contains_name(&item.attrs, "main") { if ctxt.attr_main_fn.is_none() { - ctxt.attr_main_fn = Some((item.id, item.span)); + let link_name = attr::first_attr_value_str_by_name(&item.attrs, + "main_link_name") + .unwrap_or_else(|| token::get_name(ctxt.main_name)); + ctxt.attr_main_fn = Some((item.id, link_name, item.span)); } else { span_err!(ctxt.session, item.span, E0137, "multiple functions with a #[main] attribute"); @@ -107,7 +114,10 @@ fn find_item(item: &Item, ctxt: &mut EntryContext) { if attr::contains_name(&item.attrs, "start") { if ctxt.start_fn.is_none() { - ctxt.start_fn = Some((item.id, item.span)); + let link_name = attr::first_attr_value_str_by_name(&item.attrs, + "main_link_name") + .unwrap_or_else(|| token::get_name(ctxt.main_name)); + ctxt.start_fn = Some((item.id, link_name, item.span)); } else { span_err!(ctxt.session, item.span, E0138, "multiple 'start' functions"); @@ -122,13 +132,13 @@ fn find_item(item: &Item, ctxt: &mut EntryContext) { fn configure_main(this: &mut EntryContext) { if this.start_fn.is_some() { - *this.session.entry_fn.borrow_mut() = this.start_fn; + *this.session.entry_fn.borrow_mut() = this.start_fn.clone(); this.session.entry_type.set(Some(config::EntryStart)); } else if this.attr_main_fn.is_some() { - *this.session.entry_fn.borrow_mut() = this.attr_main_fn; + *this.session.entry_fn.borrow_mut() = this.attr_main_fn.clone(); this.session.entry_type.set(Some(config::EntryMain)); } else if this.main_fn.is_some() { - *this.session.entry_fn.borrow_mut() = this.main_fn; + *this.session.entry_fn.borrow_mut() = this.main_fn.clone(); this.session.entry_type.set(Some(config::EntryMain)); } else { // No main function diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 48fe574e71f48..9fbc290297c44 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -518,6 +518,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, "metadata to mangle symbol names with"), extra_filename: String = ("".to_string(), parse_string, "extra data to put in each output filename"), + cross_path: Option = (None, parse_opt_string, + "the path to the target specific toolchain"), codegen_units: usize = (1, parse_uint, "divide crate into N units to optimize in parallel"), remark: Passes = (SomePasses(Vec::new()), parse_passes, diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 6b5f58720ab15..8045f982e9fdc 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -43,7 +43,7 @@ pub struct Session { pub cstore: CStore, pub parse_sess: ParseSess, // For a library crate, this is always none - pub entry_fn: RefCell>, + pub entry_fn: RefCell>, pub entry_type: Cell>, pub plugin_registrar_fn: Cell>, pub default_sysroot: Option, diff --git a/src/librustc_back/archive.rs b/src/librustc_back/archive.rs index deac411800f1f..2bc2e8c51f359 100644 --- a/src/librustc_back/archive.rs +++ b/src/librustc_back/archive.rs @@ -30,6 +30,7 @@ pub struct ArchiveConfig<'a> { pub lib_search_paths: Vec, pub slib_prefix: String, pub slib_suffix: String, + pub gold_plugin: Option, pub ar_prog: String } @@ -147,17 +148,37 @@ impl<'a> Archive<'a> { } fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) { + match action { + Action::Remove(_) => { + cmd.arg("d"); + } + Action::AddObjects(_, update_symbols) => { + cmd.arg(if update_symbols {"crs"} else {"crS"}); + } + Action::UpdateSymbols => { + cmd.arg("s"); + } + } + + // The gold plugin argument *must* go after the command flags, but before + // the path arguments. + match self.config.gold_plugin { + Some(ref path) => { + cmd.arg(format!("--plugin={}", path.display())); + } + None => {} + }; + + // Now add the file arguments: match action { Action::Remove(file) => { - cmd.arg("d").arg(dst).arg(file); + cmd.arg(dst).arg(file); } - Action::AddObjects(objs, update_symbols) => { - cmd.arg(if update_symbols {"crs"} else {"crS"}) - .arg(dst) - .args(objs); + Action::AddObjects(objs, _) => { + cmd.arg(dst).args(objs); } Action::UpdateSymbols => { - cmd.arg("s").arg(dst); + cmd.arg(dst); } } } diff --git a/src/librustc_back/arm.rs b/src/librustc_back/arm.rs index 7325e4e7a2ece..9a89765fe88f5 100644 --- a/src/librustc_back/arm.rs +++ b/src/librustc_back/arm.rs @@ -68,6 +68,13 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) -> target_strs -v64:64:64-v128:64:128\ -a:0:64-n32".to_string() } + abi::OsNaCl => { + "e-p:32:32:32\ + -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:64:128\ + -a0:0:64-n32".to_string() + } }, target_triple: target_triple, diff --git a/src/librustc_back/le32.rs b/src/librustc_back/le32.rs new file mode 100644 index 0000000000000..40c9fe7555650 --- /dev/null +++ b/src/librustc_back/le32.rs @@ -0,0 +1,26 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use target_strs; +use syntax::abi; +use std::vec::Vec; + +pub fn get_target_strs(target_triple: String, _target_os: abi::Os) -> target_strs::t { + return target_strs::t { + module_asm: "".to_string(), + + data_layout: "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-\ + i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32".to_string(), + + target_triple: target_triple, + + cc_args: Vec::new(), + }; +} diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 7d46cc84fd685..7716353c98378 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -54,6 +54,7 @@ pub mod abi; pub mod archive; pub mod tempdir; pub mod arm; +pub mod le32; pub mod mips; pub mod mipsel; pub mod rpath; diff --git a/src/librustc_back/mips.rs b/src/librustc_back/mips.rs index b46150f75d084..14444524ae9f6 100644 --- a/src/librustc_back/mips.rs +++ b/src/librustc_back/mips.rs @@ -63,6 +63,13 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) -> target_strs -v64:64:64-v128:64:128\ -a:0:64-n32".to_string() } + abi::OsNaCl => { + "e-p:32:32:32\ + -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:64:128\ + -a0:0:64-n32".to_string() + } }, target_triple: target_triple, diff --git a/src/librustc_back/mipsel.rs b/src/librustc_back/mipsel.rs index c7fa7aa879ac2..862bbc0264138 100644 --- a/src/librustc_back/mipsel.rs +++ b/src/librustc_back/mipsel.rs @@ -63,6 +63,9 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) -> target_strs -v64:64:64-v128:64:128\ -a:0:64-n32".to_string() } + abi::OsNaCl => { + "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string() + } }, target_triple: target_triple, diff --git a/src/librustc_back/target/le32_unknown_nacl.rs b/src/librustc_back/target/le32_unknown_nacl.rs new file mode 100644 index 0000000000000..69bd01350a095 --- /dev/null +++ b/src/librustc_back/target/le32_unknown_nacl.rs @@ -0,0 +1,35 @@ +// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::{Target, TargetOptions}; + +pub fn target() -> Target { + let opts = TargetOptions { + dynamic_linking: false, + executables: true, + morestack: false, + exe_suffix: ".pexe".to_string(), + no_compiler_rt: true, + is_like_pnacl: true, + no_asm: true, + .. Default::default() + }; + Target { + data_layout: "e-i1:8:8-i8:8:8-i16:16:16-i32:32:32-\ + i64:64:64-f32:32:32-f64:64:64-p:32:32:32-v128:32:32".to_string(), + llvm_target: "le32-unknown-nacl".to_string(), + target_endian: "little".to_string(), + target_pointer_width: "32".to_string(), + target_os: "nacl".to_string(), + target_env: "".to_string(), + arch: "le32".to_string(), + options: opts, + } +} diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 402fbcd8d8d8e..227c04ec64218 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -152,6 +152,8 @@ pub struct TargetOptions { /// Whether the target toolchain is like Android's. Only useful for compiling against Android. /// Defaults to false. pub is_like_android: bool, + /// Whether the target is like PNaCl/JS. Defaults to false. + pub is_like_pnacl: bool, /// Whether the linker support GNU-like arguments such as -O. Defaults to false. pub linker_is_gnu: bool, /// Whether the linker support rpaths or not. Defaults to false. @@ -165,6 +167,8 @@ pub struct TargetOptions { /// the functions in the executable are not randomized and can be used /// during an exploit of a vulnerability in any code. pub position_independent_executables: bool, + /// Is asm!() not allowed? Defaults to false + pub no_asm: bool, } impl Default for TargetOptions { @@ -195,12 +199,14 @@ impl Default for TargetOptions { is_like_windows: false, is_like_android: false, is_like_msvc: false, + is_like_pnacl: false, linker_is_gnu: false, has_rpath: false, no_compiler_rt: false, position_independent_executables: false, pre_link_objects: Vec::new(), post_link_objects: Vec::new(), + no_asm: false, } } } @@ -290,11 +296,13 @@ impl Target { key!(function_sections, bool); key!(is_like_osx, bool); key!(is_like_windows, bool); + key!(is_like_pnacl, bool); key!(linker_is_gnu, bool); key!(has_rpath, bool); key!(no_compiler_rt, bool); key!(pre_link_args, list); key!(post_link_args, list); + key!(no_asm, bool); base } @@ -381,7 +389,9 @@ impl Target { x86_64_pc_windows_gnu, i686_pc_windows_gnu, - x86_64_pc_windows_msvc + x86_64_pc_windows_msvc, + + le32_unknown_nacl ); diff --git a/src/librustc_back/x86.rs b/src/librustc_back/x86.rs index 1c6eacc355947..a70054ea19a86 100644 --- a/src/librustc_back/x86.rs +++ b/src/librustc_back/x86.rs @@ -48,6 +48,9 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) abi::OsFreebsd | abi::OsDragonfly | abi::OsBitrig | abi::OsOpenbsd => { "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string() } + abi::OsNaCl => { + "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string() + } }, diff --git a/src/librustc_back/x86_64.rs b/src/librustc_back/x86_64.rs index d016bd12c698f..25388b20f5779 100644 --- a/src/librustc_back/x86_64.rs +++ b/src/librustc_back/x86_64.rs @@ -52,6 +52,11 @@ pub fn get_target_strs(target_triple: String, target_os: abi::Os) -> target_strs f32:32:32-f64:64:64-v64:64:64-v128:128:128-a:0:64-\ s0:64:64-f80:128:128-n8:16:32:64-S128".to_string() } + abi::OsNaCl => { + "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".to_string() + } }, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 13dec30e0a016..15005af2cbdec 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -65,7 +65,7 @@ pub fn compile_input(sess: Session, // We need nested scopes here, because the intermediate results can keep // large chunks of memory alive and we want to free them as soon as // possible to keep the peak memory usage low - let (outputs, trans, sess) = { + let (outputs, mut trans, sess) = { let (outputs, expanded_crate, id) = { let krate = phase_1_parse_input(&sess, cfg, input); @@ -150,16 +150,15 @@ pub fn compile_input(sess: Session, (outputs, trans, tcx.sess) }; - phase_5_run_llvm_passes(&sess, &trans, &outputs); - - controller_entry_point!(after_llvm, + phase_5_run_llvm_passes(&sess, &mut trans, &outputs); + controller_entry_point!(after_llvm, sess, CompileState::state_after_llvm(input, &sess, outdir, &trans)); - phase_6_link_output(&sess, &trans, &outputs); + phase_6_link_output(&sess, &trans, &outputs); } /// The name used for source code that doesn't originate in a file @@ -536,6 +535,9 @@ pub fn phase_2_configure_and_expand(sess: &Session, time(time_passes, "checking that all macro invocations are gone", &krate, |krate| syntax::ext::expand::check_for_macros(&sess.parse_sess, krate)); + time(time_passes, "checking for inline asm in case the target doesn't support it", &krate, + |krate| middle::check_no_asm::check_crate(sess, krate) ); + // One final feature gating of the true AST that gets compiled // later, to make sure we've got everything (e.g. configuration // can insert new attributes via `cfg_attr`) @@ -732,9 +734,9 @@ pub fn phase_4_translate_to_llvm<'tcx>(analysis: ty::CrateAnalysis<'tcx>) /// Run LLVM itself, producing a bitcode file, assembly file or object file /// as a side effect. pub fn phase_5_run_llvm_passes(sess: &Session, - trans: &trans::CrateTranslation, + trans: &mut trans::CrateTranslation, outputs: &OutputFilenames) { - if sess.opts.cg.no_integrated_as { + if sess.opts.cg.no_integrated_as && !sess.target.target.options.is_like_pnacl { let output_type = config::OutputTypeAssembly; time(sess.time_passes(), "LLVM passes", (), |_| @@ -767,11 +769,18 @@ pub fn phase_6_link_output(sess: &Session, new_path.extend(env::split_paths(&old_path)); env::set_var("PATH", &env::join_paths(new_path.iter()).unwrap()); - time(sess.time_passes(), "linking", (), |_| - link::link_binary(sess, - trans, - outputs, - &trans.link.crate_name)); + time(sess.time_passes(), "linking", (), |_| { + if !sess.target.target.options.is_like_pnacl { + link::link_binary(sess, + trans, + outputs, + &trans.link.crate_name); + } else { + link::link_outputs_for_pnacl(&sess, + &trans, + &outputs); + } + }); env::set_var("PATH", &old_path); } @@ -793,6 +802,10 @@ fn write_out_deps(sess: &Session, match *output_type { config::OutputTypeExe => { for output in &*sess.crate_types.borrow() { + if !sess.target.target.options.dynamic_linking && + *output == config::CrateTypeDylib { + continue; + } let p = link::filename_for_input(sess, *output, id, &file); out_filenames.push(p); diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 4d7f00e95230a..63cc4975798cb 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -2065,6 +2065,8 @@ extern { pub fn LLVMRustLinkInExternalBitcode(M: ModuleRef, bc: *const c_char, len: size_t) -> bool; + pub fn LLVMRustLinkInModule(Dest: ModuleRef, + Src: ModuleRef) -> bool; pub fn LLVMRustRunRestrictionPass(M: ModuleRef, syms: *const *const c_char, len: size_t); @@ -2109,6 +2111,16 @@ extern { pub fn LLVMWriteDebugLocToString(C: ContextRef, DL: DebugLocRef, s: RustStringRef); + pub fn LLVMRustParseBitcode(C: ContextRef, + N: *const c_char, + P: *const c_void, + L: size_t) -> ModuleRef; + + pub fn LLVMRustWritePNaClBitcode(M: ModuleRef, + Path: *const c_char, + AcceptSupportedOnly: bool) -> bool; + pub fn LLVMRustStripDebugInfo(M: ModuleRef); + pub fn LLVMSetInlineAsmDiagnosticHandler(C: ContextRef, H: InlineAsmDiagHandler, CX: *mut c_void); diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 3de91d58740b8..8184b1d67870a 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -29,13 +29,16 @@ use util::sha2::{Digest, Sha256}; use util::fs::fix_windows_verbatim_for_gcc; use rustc_back::tempdir::TempDir; +use std::ffi; use std::fs::{self, PathExt}; use std::io::{self, Read, Write}; use std::mem; use std::path::{Path, PathBuf}; +use std::ptr; use std::process::Command; use std::str; use flate; +use llvm; use serialize::hex::ToHex; use syntax::ast; use syntax::ast_map::{PathElem, PathElems, PathName}; @@ -73,6 +76,33 @@ pub const RLIB_BYTECODE_OBJECT_V1_DATA_OFFSET: usize = RLIB_BYTECODE_OBJECT_V1_DATASIZE_OFFSET + 8; +pub fn llvm_actual_err(sess: &Session, msg: String) { + unsafe { + let cstr = llvm::LLVMRustGetLastError(); + if cstr == ptr::null() { + sess.err(&msg[..]); + } else { + let err = ffi::CStr::from_ptr(cstr).to_bytes(); + let err = String::from_utf8_lossy(&err[..]).to_string(); + sess.err(&format!("{}: {}", + msg, err)[..]); + } + } +} +pub fn llvm_warn(sess: &Session, msg: String) { + use llvm; + unsafe { + let cstr = llvm::LLVMRustGetLastError(); + if cstr == ptr::null() { + sess.warn(&msg[..]); + } else { + let err = ffi::CStr::from_ptr(cstr).to_bytes(); + let err = String::from_utf8_lossy(&err[..]).to_string(); + sess.warn(&format!("{}: {}", + msg, err)[..]); + } + } +} /* * Name mangling and its relationship to metadata. This is complex. Read * carefully. @@ -483,6 +513,31 @@ fn link_binary_output(sess: &Session, crate_type: config::CrateType, outputs: &OutputFilenames, crate_name: &str) -> PathBuf { + let (obj_filename, out_filename) = check_outputs(sess, crate_type, + outputs, crate_name); + assert!(!sess.target.target.options.is_like_pnacl); + match crate_type { + config::CrateTypeRlib => { + link_rlib(sess, Some(trans), &obj_filename, &out_filename).build(); + } + config::CrateTypeStaticlib => { + link_staticlib(sess, &obj_filename, &out_filename); + } + config::CrateTypeExecutable => { + link_natively(sess, trans, false, &obj_filename, &out_filename); + } + config::CrateTypeDylib => { + link_natively(sess, trans, true, &obj_filename, &out_filename); + } + } + + out_filename +} + +pub fn check_outputs(sess: &Session, + crate_type: config::CrateType, + outputs: &OutputFilenames, + crate_name: &str) -> (PathBuf, PathBuf) { let obj_filename = outputs.temp_path(OutputTypeObject); let out_filename = match outputs.single_output_file { Some(ref file) => file.clone(), @@ -508,23 +563,540 @@ fn link_binary_output(sess: &Session, permissions.", obj_filename.display())); } + (obj_filename, out_filename) +} +#[cfg(not(target_os = "nacl"))] +fn pnacl_host_tool(sess: &Session, tool: &str) -> PathBuf { + use std::env::consts; + let tool = format!("le32-nacl-{}{}", tool, consts::EXE_SUFFIX); + super::pnacl_toolchain(sess) + .join("bin") + .join(&tool) +} +#[cfg(target_os = "nacl")] +fn pnacl_host_tool(_sess: &Session, tool: &str) -> PathBuf { + use std::env::consts; + let tool = format!("{}{}", tool, consts::EXE_SUFFIX); + Path::new("/bin") + .join(&tool) +} - match crate_type { - config::CrateTypeRlib => { - link_rlib(sess, Some(trans), &obj_filename, &out_filename).build(); +// Gets the filepath for the gold LTO plugin. +#[cfg(not(target_os = "nacl"))] +fn gold_plugin_path(sess: &Session) -> PathBuf { + use std::env::consts; + let mut s = sess.sysroot().to_path_buf(); + s.push("lib"); + s.push("rustlib"); + s.push(&config::host_triple().to_string()); + s.push("lib"); + s.push(&format!("LLVMgold{}", consts::DLL_SUFFIX)); + s +} + +pub fn link_outputs_for_pnacl(sess: &Session, + trans: &CrateTranslation, + outputs: &OutputFilenames) { + // Rlibs and statics first, then exes. + for &crate_type in sess.crate_types.borrow().iter() { + match crate_type { + config::CrateTypeDylib => unreachable!(), + config::CrateTypeRlib => link_pnacl_rlib(sess, trans, outputs), + config::CrateTypeExecutable => {} + config::CrateTypeStaticlib => unimplemented!(), } - config::CrateTypeStaticlib => { - link_staticlib(sess, &obj_filename, &out_filename); + } + + for &crate_type in sess.crate_types.borrow().iter() { + match crate_type { + config::CrateTypeExecutable => link_pnacl_binary(sess, trans, + outputs), + _ => (), } - config::CrateTypeExecutable => { - link_natively(sess, trans, false, &obj_filename, &out_filename); + } + + // cleanup: + if !sess.opts.cg.save_temps { + for (index, mtrans) in trans.modules.iter().enumerate() { + let f = match mtrans.name { + Some(ref name) => outputs + .with_extension(&format!("{}.bc", + name)[..]), + None => outputs + .with_extension(&format!("{}.bc", + index)[..]), + }; + remove(sess, &f); } - config::CrateTypeDylib => { - link_natively(sess, trans, true, &obj_filename, &out_filename); + + remove(sess, &outputs.with_extension("metadata.bc")); + } +} + +fn link_pnacl_rlib(sess: &Session, + trans: &CrateTranslation, + outputs: &OutputFilenames) { + let (_, out_filename) = check_outputs(sess, config::CrateTypeRlib, + outputs, &trans.link.crate_name[..]); + + #[cfg(not(target_os = "nacl"))] + fn create_archive_config(sess: &Session, out_filename: PathBuf) -> ArchiveBuilder { + + let handler = &sess.diagnostic().handler; + let config = ArchiveConfig { + handler: handler, + dst: out_filename, + lib_search_paths: archive_search_paths(sess), + slib_prefix: "lib".to_string(), + slib_suffix: "rlib".to_string(), + gold_plugin: Some(gold_plugin_path(sess)), + ar_prog: pnacl_host_tool(sess, "ar").display().to_string(), + }; + ArchiveBuilder::create(config) + } + #[cfg(target_os = "nacl")] + fn create_archive_config(sess: &Session, out_filename: PathBuf) -> ArchiveBuilder { + let handler = &sess.diagnostic().handler; + let config = ArchiveConfig { + handler: handler, + dst: out_filename, + lib_search_paths: archive_search_paths(sess), + slib_prefix: "lib".to_string(), + slib_suffix: "rlib".to_string(), + gold_plugin: None, + maybe_ar_prog: Some("llvm-ar".to_string()), + }; + ArchiveBuilder::create(config) + } + + let mut a = create_archive_config(sess, out_filename.clone()); + + for (index, mtrans) in trans.modules.iter().enumerate() { + let f = match mtrans.name { + Some(ref name) => outputs + .with_extension(&format!("{}.bc", + name)[..]), + None => outputs + .with_extension(&format!("{}.bc", + index)[..]), + }; + let _: Result<(), ()> = a + .add_file(&f) + .or_else(|e| { + sess.err(&format!("error adding file to archive: `{}`", + e)[..]); + Ok(()) + }); + } + + // Instead of putting the metadata in an object file section, rlibs + // contain the metadata in a separate file. We use a temp directory + // here so concurrent builds in the same directory don't try to use + // the same filename for metadata (stomping over one another) + debug!("adding metadata to archive"); + let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); + let metadata = tmpdir.path().join(METADATA_FILENAME); + match fs::File::create(&metadata) + .and_then(|mut f| f.write_all(&trans.metadata[..]) ) + { + Ok(..) => {} + Err(e) => { + sess.fatal(&format!("failed to write {:?}: {}", + metadata, e)[..]); } } - out_filename + let _: Result<(), ()> = a + .add_file(&metadata) + .or_else(|e| { + sess.err(&format!("error adding file to archive: `{}`", + e)[..]); + Ok(()) + }); + + a.update_symbols(); + a.build(); + + sess.abort_if_errors(); +} + +pub fn link_pnacl_binary(sess: &Session, + trans: &CrateTranslation, + outputs: &OutputFilenames) { + // Note that we don't use pnacl-ld here. We want to avoid the costly post-link + // simplification passes pnacl-ld runs (it runs -O *post link*). Instead, + // since pnacl-ld's output is just bitcode, what we do here is pull all of + // our 'native' dependencies into a (vary large) module and, if requested, + // run the LTO passes on it. + // + // This function should not be called on non-pexe outputs. + use libc; + use std::env; + use std::fmt; + use std::fs::{File}; + use back::write; + use back::write::llvm_err; + use session::config::OutputTypeLlvmAssembly; + use metadata::cstore; + + // Emits a fatal error if path is not writeable. + fn check_writeable_output(sess: &Session, path: &T, name: &str) + where T: AsRef + AsRef + fmt::Debug + { + use std::fs::metadata; + use std::io::ErrorKind; + + let is_writeable = match metadata(path) { + Ok(m) => !m.permissions().readonly(), + Err(e) => (e.kind() == ErrorKind::NotFound), + }; + + if !is_writeable { + sess.fatal(&format!("`{}` file `{:?}` is not writeable -- check it's permissions.", + name, path)[..]); + } + } + + let post_link_path = outputs.with_extension("post-link.bc"); + + #[cfg(not(target_os = "nacl"))] + fn add_plugin_arg(sess: &Session, cmd: &mut Command) { + cmd.arg(&format!("-plugin={}", gold_plugin_path(sess).display())); + } + #[cfg(target_os = "nacl")] + fn add_plugin_arg(_sess: &Session, _cmd: &mut Command) { + // on PNaCl, the gold plugin is linked statically. + } + + let linker = pnacl_host_tool(sess, "ld.gold"); + let mut cmd = Command::new(&linker); + cmd.arg("--oformat=elf32-i386-nacl"); + add_plugin_arg(sess, &mut cmd); + cmd.arg("-plugin-opt=emit-llvm"); + cmd.arg("-nostdlib"); + cmd.arg("--undef-sym-check"); + cmd.arg("-static"); + cmd.args(&["--allow-unresolved=memcpy", + "--allow-unresolved=memset", + "--allow-unresolved=memmove", + "--allow-unresolved=setjmp", + "--allow-unresolved=longjmp", + "--allow-unresolved=__nacl_tp_tls_offset", + "--allow-unresolved=__nacl_tp_tdb_offset", + "--allow-unresolved=__nacl_get_arch", + "--undefined=__pnacl_eh_stack", + "--undefined=__pnacl_eh_resume", + "--allow-unresolved=__pnacl_eh_type_table", + "--allow-unresolved=__pnacl_eh_action_table", + "--allow-unresolved=__pnacl_eh_filter_table", + "--undefined=main", + "--undefined=exit", + "--undefined=_exit", + ]); + cmd.arg("-o").arg(&post_link_path); + + cmd.arg("-L").arg(&sess.target_filesearch(PathKind::Crate).get_lib_path()); + + match sess.opts.cg.link_args { + Some(ref args) => { + for arg in args.iter() { + cmd.arg(arg); + } + } + _ => {} + } + for arg in sess.cstore.get_used_link_args().borrow().iter() { + cmd.arg(&arg[..]); + } + cmd.arg("--start-group"); + + for (index, mtrans) in trans.modules.iter().enumerate() { + let f = match mtrans.name { + Some(ref name) => outputs + .with_extension(&format!("{}.bc", + name)[..]), + None => outputs + .with_extension(&format!("{}.bc", + index)[..]), + }; + cmd.arg(&f); + } + + let deps = sess.cstore.get_used_crates(cstore::RequireStatic); + for &(cnum, _) in deps.iter() { + let src = sess.cstore.get_used_crate_source(cnum).unwrap(); + let (path, _) = src.rlib.unwrap(); + cmd.arg(&path); + } + cmd.arg("--end-group"); + + debug!("running toolchain linker:"); + debug!("{:?}", &cmd); + + let prog = time(sess.time_passes(), + "running linker", + (), + |()| cmd.output() ); + match prog { + Ok(prog) => { + if !prog.status.success() { + sess.err(&format!("linking with `{}` failed: {}", + linker.display(), + prog.status)[..]); + sess.note(&format!("{:?}", &cmd)[..]); + let mut output = prog.stderr.clone(); + output.push_all(&prog.stdout[..]); + sess.note(&str::from_utf8(&output[..]).unwrap()[..]); + sess.abort_if_errors(); + } + }, + Err(e) => { + sess.err(&format!("could not exec the linker `{}`: {}", + linker.display(), e)[..]); + sess.abort_if_errors(); + } + } + let llcx = unsafe { llvm::LLVMContextCreate() }; + // Now load the linked module and run LTO + a limited set of passes to cleanup: + let llmod = match File::open(&post_link_path) + .and_then(|mut f| { + let mut b = Vec::new(); + try!(f.read_to_end(&mut b)); + Ok(b) + }) + { + Ok(buf) => { + let post_link_path = post_link_path + .clone() + .into_os_string() + .into_string() + .unwrap(); + unsafe { + let cname = format!("{}\0", post_link_path); + let llmod = llvm::LLVMRustParseBitcode(llcx, + cname.as_ptr() as *const i8, + buf.as_ptr() as *const libc::c_void, + buf.len() as libc::size_t); + if llmod == ptr::null_mut() { + llvm_err(&sess.diagnostic().handler, + format!("failed to parse external bitcode `{}`", + post_link_path)); + } else { + llmod + } + } + }, + Err(e) => { + sess.fatal(&format!("error reading file `{:?}`: `{}`", + post_link_path, e)[..]); + } + }; + + if !sess.opts.cg.save_temps { + remove(sess, &post_link_path); + } + + // Run LTO passes: + + // Internalize everything. + unsafe { + let reachable = vec!("_start\0".as_ptr(), + "__pnacl_eh_stack\0".as_ptr()); + llvm::LLVMRustRunRestrictionPass(llmod, + reachable.as_ptr() as *const *const libc::c_char, + reachable.len() as libc::size_t); + } + + if sess.no_landing_pads() { + unsafe { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + } + } + + if sess.opts.cg.save_temps { + let out = outputs.with_extension("pre-lto.bc"); + check_writeable_output(sess, &out, "pre lto output"); + let out = format!("{}\0", out.display().to_string()); + unsafe { llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr() as *const i8) }; + } + + // FIXME: technically there are two sets of passes Rust should accept: the + // first is at crate level (ie write.rs) and the second is here + if !sess.opts.cg.no_prepopulate_passes { + unsafe { + // PNaCl uses an ARM target machine for optimizations. + let tm = write::create_target_machine(sess); + + let pre = |pm| { + let ap = |s: &'static str| { + assert!(llvm::LLVMRustAddPass(pm, s.as_ptr() as *const i8), + "failed to add pass `{}`", &s[0 .. s.len() - 1]); + }; + + ap("pnacl-sjlj-eh\0"); + ap("internalize-used-globals\0"); + ap("expand-indirectbr\0"); + ap("lower-expect\0"); + ap("rewrite-llvm-intrinsic-calls\0"); + ap("expand-varargs\0"); + ap("expand-arith-with-overflow\0"); + ap("expand-constant-expr\0"); + ap("simplify-struct-reg-signatures\0"); + ap("expand-struct-regs\0"); + ap("nacl-expand-ctors\0"); + ap("resolve-aliases\0"); + ap("nacl-expand-tls-constant-expr\0"); + ap("nacl-expand-tls\0"); + ap("nacl-global-cleanup\0"); + }; + + let post = |pm| { + let ap = |s: &'static str| { + assert!(llvm::LLVMRustAddPass(pm, s.as_ptr() as *const i8), + "failed to add pass `{}`", &s[0 .. s.len() - 1]); + }; + + ap("rewrite-pnacl-library-calls\0"); + ap("expand-byval\0"); + ap("expand-small-arguments\0"); + ap("nacl-promote-i1-ops\0"); + ap("expand-shufflevector\0"); + ap("globalize-constant-vectors\0"); + ap("constant-insert-extract-element-index\0"); + ap("fix-vector-load-store-alignment\0"); + ap("canonicalize-mem-intrinsics\0"); + ap("constmerge\0"); + ap("flatten-globals\0"); + ap("expand-constant-expr\0"); + ap("nacl-expand-ints\0"); + ap("nacl-promote-ints\0"); + ap("expand-getelementptr\0"); + ap("nacl-rewrite-atomics\0"); + ap("expand-struct-regs\0"); + ap("remove-asm-memory\0"); + ap("simplify-allocas\0"); + ap("replace-ptrs-with-ints\0"); + ap("expand-struct-regs\0"); + ap("normalize-alignment\0"); + ap("strip-dead-prototypes\0"); + ap("die\0"); + ap("dce\0"); + ap("cleanup-used-globals-metadata\0"); + }; + + time(sess.time_passes(), "PNaCl simplification passes + LTO", (pre, post), + |(pre, post)| { + super::lto::run_passes(sess, llmod, + tm, pre, post); + }); + llvm::LLVMRustDisposeTargetMachine(tm); + } + } + + // Write out IR if asked: + if sess.opts.output_types.iter().any(|&i| i == OutputTypeLlvmAssembly) { + // emit ir + let p = outputs.path(OutputTypeLlvmAssembly).display().to_string(); + check_writeable_output(sess, &p, "LLVM assembly output"); + let cp = format!("{}\0", p); + unsafe { + let pm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustPrintModule(pm, llmod, cp.as_ptr() as *const i8); + llvm::LLVMDisposePassManager(pm); + } + } + + let force_non_stable_output = { + let t = env::var("RUSTC_FORCE_NON_STABLE_BC_EMISSION").ok(); + t != None || t.as_ref().map(|v| &v[..] ) != Some("0") + }; + if force_non_stable_output || sess.opts.debuginfo != config::NoDebugInfo { + // emit bc for translation into nexe w/ debugging info. + let outpath = outputs.with_extension("debug.pexe"); + let out = outpath.display().to_string(); + check_writeable_output(sess, &outpath, "debug output"); + let cout = format!("{}\0", out); + unsafe { + llvm::LLVMWriteBitcodeToFile(llmod, cout.as_ptr() as *const i8) + }; + set_permissions(&outpath); + } + + let emit_stable_pexe = true; // always emit stable bitcode. + if emit_stable_pexe { + unsafe { + // The PNaCl stable bitcode format doesn't accept metadata types. + llvm::LLVMRustStripDebugInfo(llmod); + + let pm = llvm::LLVMCreatePassManager(); + + let ap = |s: &'static str| { + assert!(llvm::LLVMRustAddPass(pm, s.as_ptr() as *const i8), + "failed to add pass `{}`", &s[0 .. s.len() - 1]); + }; + + // Strip unsupported metadata and things: + ap("strip-metadata\0"); + ap("strip-module-flags\0"); + ap("nacl-strip-attributes\0"); + + if !sess.no_verify() { + ap("verify-pnaclabi-module\0"); + ap("verify-pnaclabi-functions\0"); + } + + llvm::LLVMRunPassManager(pm, llmod); + + llvm::LLVMDisposePassManager(pm); + + llvm::LLVMRustStripDebugInfo(llmod); + } + } + + let out = match outputs.single_output_file { + Some(ref file) => file.clone(), + None => { + let out_filename = outputs.path(OutputTypeExe); + filename_for_input(sess, config::CrateTypeExecutable, + &trans.link.crate_name[..], &out_filename) + } + }; + let out_cstr = format!("{}\0", out.display().to_string()); + + check_writeable_output(sess, &out, "final output"); + + if emit_stable_pexe { + if !unsafe { llvm::LLVMRustWritePNaClBitcode(llmod, + out_cstr.as_ptr() as *const i8, + false) } { + llvm_err(&sess.diagnostic().handler, "failed to write output file".to_string()); + } + } else { + // regular bitcode output: + unsafe { llvm::LLVMWriteBitcodeToFile(llmod, out_cstr.as_ptr() as *const i8) }; + } + + + #[cfg(unix)] + fn set_permissions(out: &PathBuf) { + use std::os::unix::fs::PermissionsExt; + use std::fs::{metadata, set_permissions}; + + let mut perms = metadata(out) + .unwrap() + .permissions(); + perms.set_mode(0o0755); + set_permissions(out, perms) + .unwrap(); + } + #[cfg(not(unix))] + fn set_permissions(_: &PathBuf) { } + + set_permissions(&out); + + unsafe { + llvm::LLVMContextDispose(llcx); + } } fn archive_search_paths(sess: &Session) -> Vec { @@ -554,6 +1126,7 @@ fn link_rlib<'a>(sess: &'a Session, lib_search_paths: archive_search_paths(sess), slib_prefix: sess.target.target.options.staticlib_prefix.clone(), slib_suffix: sess.target.target.options.staticlib_suffix.clone(), + gold_plugin: None, ar_prog: get_ar_prog(sess), }; let mut ab = ArchiveBuilder::create(config); @@ -1178,6 +1751,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, lib_search_paths: archive_search_paths(sess), slib_prefix: sess.target.target.options.staticlib_prefix.clone(), slib_suffix: sess.target.target.options.staticlib_suffix.clone(), + gold_plugin: None, ar_prog: get_ar_prog(sess), }; let mut archive = Archive::open(config); diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs index 6457140bcc0f9..615d7ecaabb36 100644 --- a/src/librustc_trans/back/lto.rs +++ b/src/librustc_trans/back/lto.rs @@ -13,7 +13,7 @@ use super::write; use rustc::session::{self, config}; use llvm; use llvm::archive_ro::ArchiveRO; -use llvm::{ModuleRef, TargetMachineRef, True, False}; +use llvm::{ModuleRef, TargetMachineRef, False, True, PassManagerRef}; use rustc::metadata::cstore; use rustc::util::common::time; @@ -154,37 +154,62 @@ pub fn run(sess: &session::Session, llmod: ModuleRef, } } + run_passes(sess, llmod, tm, |_| (), |_| ()); +} + +pub fn run_passes(sess: &session::Session, + llmod: ModuleRef, + tm: TargetMachineRef, + pre: FA, + post: FB) + where FA: FnOnce(PassManagerRef), + FB: FnOnce(PassManagerRef), +{ // Now we have one massive module inside of llmod. Time to run the // LTO-specific optimization passes that LLVM provides. // // This code is based off the code found in llvm's LTO code generator: // tools/lto/LTOCodeGenerator.cpp debug!("running the pass manager"); - unsafe { - let pm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); - llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _); - - let opt = match sess.opts.optimize { - config::No => 0, - config::Less => 1, - config::Default => 2, - config::Aggressive => 3, - }; + if !sess.opts.cg.no_prepopulate_passes { + unsafe { + let pm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); + if !sess.no_verify() { + llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const i8); + } - let builder = llvm::LLVMPassManagerBuilderCreate(); - llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt); - llvm::LLVMPassManagerBuilderPopulateLTOPassManager(builder, pm, - /* Internalize = */ False, - /* RunInliner = */ True); - llvm::LLVMPassManagerBuilderDispose(builder); + pre(pm); - llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const _); + let opt = match sess.opts.optimize { + config::No => 0, + config::Less => 1, + config::Default => 2, + config::Aggressive => 3, + }; - time(sess.time_passes(), "LTO passes", (), |()| - llvm::LLVMRunPassManager(pm, llmod)); + if sess.opts.optimize != config::No && sess.lto() { + let builder = llvm::LLVMPassManagerBuilderCreate(); + llvm::LLVMPassManagerBuilderSetOptLevel(builder, opt); + llvm::LLVMPassManagerBuilderPopulateLTOPassManager + (builder, + pm, + /* Internalize = */ False, + /* RunInliner = */ True); + llvm::LLVMPassManagerBuilderDispose(builder); + } + + post(pm); + + if !sess.no_verify() { + llvm::LLVMRustAddPass(pm, "verify\0".as_ptr() as *const i8); + } + + time(sess.time_passes(), "LTO pases", (), |()| + llvm::LLVMRunPassManager(pm, llmod)); - llvm::LLVMDisposePassManager(pm); + llvm::LLVMDisposePassManager(pm); + } } debug!("lto done"); } diff --git a/src/librustc_trans/back/mod.rs b/src/librustc_trans/back/mod.rs new file mode 100644 index 0000000000000..9cf321e4d3c38 --- /dev/null +++ b/src/librustc_trans/back/mod.rs @@ -0,0 +1,60 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use session::Session; + +use std::path::{PathBuf, Path}; + +pub use rustc_back::abi; +pub use rustc_back::archive; +pub use rustc_back::arm; +pub use rustc_back::mips; +pub use rustc_back::mipsel; +pub use rustc_back::rpath; +pub use rustc_back::svh; +pub use rustc_back::target_strs; +pub use rustc_back::x86; +pub use rustc_back::x86_64; + +pub mod linker; +pub mod link; +pub mod lto; +pub mod write; + +pub fn expect_nacl_cross_path(sess: &Session) -> PathBuf { + use std::env; + let cross_path = sess.opts.cg.cross_path.clone(); + match cross_path.or_else(|| env::var("NACL_SDK_ROOT").ok() ) { + None => sess.fatal("need cross path (-C cross-path, or via NACL_SDK_ROOT) \ + for this target"), + Some(p) => Path::new(&p).to_path_buf(), + } +} +#[cfg(not(target_os = "nacl"))] +pub fn pnacl_toolchain(sess: &Session) -> PathBuf { + #[cfg(windows)] + fn get_os_for_nacl_toolchain(_sess: &Session) -> String { "win".to_string() } + #[cfg(target_os = "linux")] + fn get_os_for_nacl_toolchain(_sess: &Session) -> String { "linux".to_string() } + #[cfg(target_os = "macos")] + fn get_os_for_nacl_toolchain(_sess: &Session) -> String { "mac".to_string() } + #[cfg(all(not(windows), + not(target_os = "linux"), + not(target_os = "macos"), + not(target_os = "nacl")))] + fn get_os_for_nacl_toolchain(sess: &Session) -> ! { + sess.fatal("NaCl/PNaCl toolchain unsupported on this OS (update this if that's changed)"); + } + + let mut tc = expect_nacl_cross_path(sess); + tc.push("toolchain"); + tc.push(&format!("{}_pnacl", get_os_for_nacl_toolchain(sess))); + tc +} diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 5352c61d8c0ad..e82c44950ca80 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -165,7 +165,7 @@ fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { } } -fn create_target_machine(sess: &Session) -> TargetMachineRef { +pub fn create_target_machine(sess: &Session) -> TargetMachineRef { let reloc_model_arg = match sess.opts.cg.relocation_model { Some(ref s) => &s[..], None => &sess.target.target.options.relocation_model[..], @@ -220,7 +220,13 @@ fn create_target_machine(sess: &Session) -> TargetMachineRef { } }; - let triple = &sess.target.target.llvm_target; + let triple = if sess.target.target.options.is_like_pnacl { + // Pretend that we are ARM for name mangling and assembly conventions. + // https://code.google.com/p/nativeclient/issues/detail?id=2554 + "armv7a-none-nacl-gnueabi" + } else { + &sess.target.target.llvm_target[..] + }; let tm = unsafe { let triple = CString::new(triple.as_bytes()).unwrap(); @@ -256,7 +262,7 @@ fn create_target_machine(sess: &Session) -> TargetMachineRef { /// Module-specific configuration for `optimize_and_codegen`. #[derive(Clone)] -struct ModuleConfig { +pub struct ModuleConfig { /// LLVM TargetMachine to use for codegen. tm: TargetMachineRef, /// Names of additional optimization passes to run. @@ -284,7 +290,7 @@ struct ModuleConfig { unsafe impl Send for ModuleConfig { } impl ModuleConfig { - fn new(tm: TargetMachineRef, passes: Vec) -> ModuleConfig { + pub fn new(tm: TargetMachineRef, passes: Vec) -> ModuleConfig { ModuleConfig { tm: tm, passes: passes, @@ -304,7 +310,7 @@ impl ModuleConfig { } } - fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) { + pub fn set_flags(&mut self, sess: &Session, trans: &CrateTranslation) { self.no_verify = sess.no_verify(); self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; self.no_builtins = trans.no_builtins; @@ -413,7 +419,7 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, config: ModuleConfig, name_extra: String, output_names: OutputFilenames) { - let ModuleTranslation { llmod, llcx } = mtrans; + let ModuleTranslation { llmod, llcx, .. } = mtrans; let tm = config.tm; // llcx doesn't outlive this function, so we can put this on the stack. @@ -561,15 +567,25 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext, } pub fn run_passes(sess: &Session, - trans: &CrateTranslation, + trans: &mut CrateTranslation, output_types: &[config::OutputType], crate_output: &OutputFilenames) { + use llvm::archive_ro::ArchiveRO; + use std::collections::HashSet; + use metadata::cstore; + use libc; + use back::link::llvm_actual_err; + + use std::path::{PathBuf}; + // It's possible that we have `codegen_units > 1` but only one item in // `trans.modules`. We could theoretically proceed and do LTO in that // case, but it would be confusing to have the validity of // `-Z lto -C codegen-units=2` depend on details of the crate being // compiled, so we complain regardless. - if sess.lto() && sess.opts.cg.codegen_units > 1 { + // PNaCl uses a bitcode linker, so this isn't an issue. + if sess.lto() && sess.opts.cg.codegen_units > 1 && + !sess.target.target.options.is_like_pnacl { // This case is impossible to handle because LTO expects to be able // to combine the entire crate and all its dependencies into a // single compilation unit, but each codegen unit is in a separate @@ -636,6 +652,23 @@ pub fn run_passes(sess: &Session, modules_config.set_flags(sess, trans); metadata_config.set_flags(sess, trans); + if sess.target.target.options.is_like_pnacl { + // If targeting PNaCl, we only want to emit bitcode. + metadata_config.emit_bc = true; + metadata_config.emit_no_opt_bc = false; + metadata_config.emit_lto_bc = false; + metadata_config.emit_ir = false; + metadata_config.emit_asm = false; + metadata_config.emit_obj = false; + + modules_config.emit_bc = true; + modules_config.emit_no_opt_bc = false; + modules_config.emit_lto_bc = false; + modules_config.emit_ir = false; + modules_config.emit_asm = false; + modules_config.emit_obj = false; + } + // Populate a buffer with a list of codegen threads. Items are processed in // LIFO order, just because it's a tiny bit simpler that way. (The order @@ -644,7 +677,7 @@ pub fn run_passes(sess: &Session, { let work = build_work_item(sess, - trans.metadata_module, + trans.metadata_module.clone(), metadata_config.clone(), crate_output.clone(), "metadata".to_string()); @@ -653,23 +686,273 @@ pub fn run_passes(sess: &Session, for (index, mtrans) in trans.modules.iter().enumerate() { let work = build_work_item(sess, - *mtrans, + mtrans.clone(), modules_config.clone(), crate_output.clone(), format!("{}", index)); work_items.push(work); } + if sess.target.target.options.is_like_pnacl { + + // In contrast to the NaCl SDK PNaCl toolchain, we do things a little + // differently. + // `pnacl-clang` or `pnacl-ld` run all optimization passes (set by + // -O[0-3sz]) after linking. This isn't such a problem when you have a + // compiler (ie clang) that generates reasonably sized modules to begin + // with, or when you don't have a test suite with 1500+ separate binary + // tests that you'd like to run and complete sometime before you + // retire. Rust isn't so lucky. + // So to make things more manageable, Rust runs the optimization passes + // on each of its modules as per usual, and uses LLVM's LTO passes for, + // well, LTO, with the IR simplification passes before and + // after. However, `pnacl-clang`s behavior is unchanged, which means we + // need to run the regular optimization passes on the current crate's + // link deps. + + fn already_linked_libs(sess: &Session, + crates: &Vec<(u32, Option)>) -> HashSet { + use metadata::csearch; + // Go though all extern crates and insert their deps into our linked set. + let linked = crates + .iter() + .fold(HashSet::new(), |mut led: HashSet, &(cnum, _)| { + let libs = csearch::get_native_libraries(&sess.cstore, cnum); + for (_, name) in libs.into_iter() { + led.insert(name); + } + led + }); + debug!("linked: `{:?}`", linked); + linked + } + #[cfg(not(target_os = "nacl"))] + fn pnacl_lib_paths(sess: &Session) -> Vec { + fn make_absolute(p: &Path) -> ::std::io::Result { + use std::env; + env::current_dir() + .map(|cwd| { + let v = cwd.join(p) + .into_os_string() + .into_string() + .unwrap(); + Path::new(&v) + .to_path_buf() + }) + } + + use rustc::session::search_paths::PathKind; + let native_dep_lib_path = { + make_absolute(&super::pnacl_toolchain(sess) + .join("le32-nacl") + .join("lib")) + .unwrap() + }; + let builtin_ports_lib_path = { + make_absolute(&super::expect_nacl_cross_path(sess) + .join("lib") + .join("pnacl") + .join(if sess.opts.optimize == config::No { + "Debug" + } else { + "Release" + })) + .unwrap() + }; + // NaClports installs here: + let ports_lib_path = { + make_absolute(&super::pnacl_toolchain(sess) + .join("le32-nacl/usr/lib")) + .unwrap() + }; + let mut paths: Vec = sess.opts.search_paths + .iter(PathKind::Dependency) + .map(|(p, _): (&Path, _)| p.to_path_buf() ) + .collect(); + paths.extend({ + sess.opts.search_paths + .iter(PathKind::Native) + .map(|(p, _): (&Path, _)| p.to_path_buf() ) + }); + paths.push(native_dep_lib_path); + paths.push(builtin_ports_lib_path); + paths.push(ports_lib_path); + paths + } + #[cfg(target_os = "nacl")] + fn pnacl_lib_paths(sess: &Session) -> Vec { + use rustc::session::search_paths::PathKind; + let ports = Path::new("/lib/ports"); + let ports = ports.join(if sess.opts.optimize == config::No { + "Debug" + } else { + "Release" + }); + let base = Path::new("/lib"); + let mut paths: Vec = sess.opts.search_paths + .iter(PathKind::Dependency) + .map(|(p, _): (&Path, _)| p.to_path_buf() ) + .collect(); + paths.extend({ + sess.opts.search_paths + .iter(PathKind::Native) + .map(|(p, _): (&Path, _)| p.to_path_buf() ) + }); + paths.push(base.to_path_buf()); + paths.push(ports.to_path_buf()); + paths + } + + fn link_attrs_filter(sess: &Session, + lib: &String, + kind: cstore::NativeLibraryKind, + linked: &mut HashSet, + lib_paths: &Vec) -> Option<(String, PathBuf)> { + let lib_name = { + let mut i = lib.rsplit(':'); + i.next(); + let name = i.next(); + if name.is_some() { + name + .unwrap() + .to_string() + } else { + lib.clone() + } + }; + match kind { + cstore::NativeFramework => { + sess.bug("can't link MacOS frameworks into PNaCl modules"); + } + cstore::NativeStatic | cstore::NativeUnknown + if !linked.contains(&lib_name) => { + // Don't link archives twice: + linked.insert(lib_name.clone()); + let lib_path = search_for_native(lib_paths, &lib_name); + maybe_lib(sess, + &lib_name, + lib_path.map(|p| (lib_name.clone(), p) )) + } + cstore::NativeStatic | cstore::NativeUnknown => { None } + } + } + + fn search_for_native(paths: &Vec, + name: &String) -> Option { + use std::fs::metadata; + let name_path = Path::new(&format!("lib{}.a", name)) + .to_path_buf(); + debug!(">> searching for native lib `{}` starting", name); + let found = paths.iter().find(|dir| { + debug!(" searching in `{:?}`", dir); + match metadata(&dir.join(&name_path)) { + Ok(_) => true, + Err(_) => false, + } + }); + let found = found.map(|f| f.join(&name_path) ); + debug!("<< searching for native lib `{}` finished, result=`{:?}`", + name, found.as_ref()); + found + } + fn maybe_lib(sess: &Session, name: &String, p_opt: Option) -> Option { + p_opt.or_else(|| { + sess.err(&format!("couldn't find library `{}`", name)[..]); + sess.note("maybe missing `-L`?"); + None + }) + } + + + let used = sess.cstore.get_used_libraries().borrow(); + let mut linked = already_linked_libs + (sess, &sess.cstore.get_used_crates(cstore::RequireStatic)); + let lib_paths = pnacl_lib_paths(sess); + let mut index = 0usize; + let bitcodes: Vec = (*used) + .iter() + .filter_map(|&(ref lib, kind)| { + link_attrs_filter(sess, lib, kind, &mut linked, &lib_paths) + }) + .flat_map(|(l, p): (String, PathBuf)| { + debug!("processing archive `{}`", p.display()); + let archive = ArchiveRO::open(&p) + .unwrap_or_else(|| { + sess.fatal(&format!("invalid archive: `{}`", + p.display())[..]); + }); + let mut bitcodes = Vec::new(); + for (id, object) in archive.iter().enumerate() { + let name = object.name() + .map(|name| name.to_string() ) + .unwrap_or_else(|| { + format!("unnamed_object_{}", id) + }); + let bc = object.data(); + debug!("processing object `{}`", name); + let llctx = unsafe { llvm::LLVMContextCreate() }; + let llmod = unsafe { + let name = format!("{}\0", name); + llvm::LLVMRustParseBitcode(llctx, + name.as_ptr() as *const i8, + bc.as_ptr() as *const libc::c_void, + bc.len() as libc::size_t) + }; + if llmod == ptr::null_mut() { + let msg = format!("failed to parse external bitcode + `{}` in archive `{:?}`", + name, p); + unsafe { llvm::LLVMContextDispose(llctx) }; + llvm_actual_err(sess, msg); + } else { + // Some globals in the bitcode from PNaCl have what is + // considered invalid linkage in our LLVM (their LLVM is + // old). Fortunately, all linkage types get stripped later, so + // it's safe to just ignore them all. + + let name = format!("r-{}-{}-{}", + l, name, index); + index = index + 1; + + let mtrans = ModuleTranslation { + llmod: llmod, + llcx: llctx, + name: Some(name.clone()), + }; + bitcodes.push(mtrans.clone()); + let work_item = build_work_item(sess, + mtrans, + modules_config.clone(), + crate_output.clone(), + name); + work_items.push(work_item); + } + } + bitcodes.into_iter() + }) + .collect(); + trans.modules.push_all(&bitcodes[..]); + } + // Process the work items, optionally using worker threads. if sess.opts.cg.codegen_units == 1 { - run_work_singlethreaded(sess, &trans.reachable, work_items); + run_work_singlethreaded(sess, &trans.reachable[..], work_items); } else { run_work_multithreaded(sess, work_items, sess.opts.cg.codegen_units); } - // All codegen is finished. - unsafe { - llvm::LLVMRustDisposeTargetMachine(tm); + if sess.target.target.options.is_like_pnacl { + // PNaCl targets (ie PNaCl/ASM.js) use a separate tool for native/js codegen. + // Therefore we just return. + unsafe { + llvm::LLVMRustDisposeTargetMachine(tm); + } + // FIXME: time_llvm_passes support - does this use a global context or + // something? + if sess.opts.cg.codegen_units == 1 && sess.time_llvm_passes() { + unsafe { llvm::LLVMRustPrintPassTimings(); } + } + return; } // Produce final compile outputs. @@ -1057,23 +1340,30 @@ unsafe fn configure_llvm(sess: &Session) { llvm::LLVMInitializeARMAsmPrinter(); llvm::LLVMInitializeARMAsmParser(); - llvm::LLVMInitializeAArch64TargetInfo(); - llvm::LLVMInitializeAArch64Target(); - llvm::LLVMInitializeAArch64TargetMC(); - llvm::LLVMInitializeAArch64AsmPrinter(); - llvm::LLVMInitializeAArch64AsmParser(); - llvm::LLVMInitializeMipsTargetInfo(); llvm::LLVMInitializeMipsTarget(); llvm::LLVMInitializeMipsTargetMC(); llvm::LLVMInitializeMipsAsmPrinter(); llvm::LLVMInitializeMipsAsmParser(); - llvm::LLVMInitializePowerPCTargetInfo(); - llvm::LLVMInitializePowerPCTarget(); - llvm::LLVMInitializePowerPCTargetMC(); - llvm::LLVMInitializePowerPCAsmPrinter(); - llvm::LLVMInitializePowerPCAsmParser(); + #[cfg(not(target_os = "nacl"))] + unsafe fn init() { + llvm::LLVMInitializeAArch64TargetInfo(); + llvm::LLVMInitializeAArch64Target(); + llvm::LLVMInitializeAArch64TargetMC(); + llvm::LLVMInitializeAArch64AsmPrinter(); + llvm::LLVMInitializeAArch64AsmParser(); + + llvm::LLVMInitializePowerPCTargetInfo(); + llvm::LLVMInitializePowerPCTarget(); + llvm::LLVMInitializePowerPCTargetMC(); + llvm::LLVMInitializePowerPCAsmPrinter(); + llvm::LLVMInitializePowerPCAsmParser(); + } + #[cfg(target_os = "nacl")] + unsafe fn init() { } + + init(); llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr()); diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index f25c6eb21a47b..bd969a0e36fad 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -41,6 +41,7 @@ #![feature(fs)] #![feature(path_relative_from)] #![feature(std_misc)] +#![feature(fs_ext)] #![allow(trivial_casts)] @@ -64,25 +65,7 @@ pub use rustc::lint; pub use rustc::plugin; pub use rustc::util; -pub mod back { - pub use rustc_back::abi; - pub use rustc_back::archive; - pub use rustc_back::arm; - pub use rustc_back::mips; - pub use rustc_back::mipsel; - pub use rustc_back::rpath; - pub use rustc_back::svh; - pub use rustc_back::target_strs; - pub use rustc_back::x86; - pub use rustc_back::x86_64; - - pub mod linker; - pub mod link; - pub mod lto; - pub mod write; - -} - +pub mod back; pub mod trans; pub mod save; diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index 6d4c72c132a59..3f590381b76da 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -733,12 +733,19 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // // FIXME #10604: this breaks when vector types are present. let (size, align) = union_size_and_align(&sts[..]); - let align_s = align as u64; - assert_eq!(size % align_s, 0); - let align_units = size / align_s - 1; - + let align_s = if align > 8 && cx.sess().target.target.options.is_like_pnacl { + // On PNaCl, we have no way to represent any alignment larger + // than 8 (well, we do, but the common 16 byte vector is out). + // Fortunately, due to PNaCl's restricted IR, we don't have to + // worry about alignment (I think). + 8u64 + } else { + align as u64 + }; let discr_ty = ll_inttype(cx, ity); let discr_size = machine::llsize_of_alloc(cx, discr_ty); + assert_eq!(size % align_s, 0); + let align_units = size / align_s - 1; let fill_ty = match align_s { 1 => Type::array(&Type::i8(cx), align_units), 2 => Type::array(&Type::i16(cx), align_units), @@ -749,6 +756,13 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, align_units), _ => panic!("unsupported enum alignment: {}", align) }; + + // This check will fail in the presence of SIMD types while + // targeting PNaCl. This is because the DataLayout PNaCl uses + // specifies that vectors of 128 bits get an alignment of only 32 + // bits, preventing us from using them to force alignment. + // However, on PNaCl, it shouldn't matter anyway, hence the hack + // above. assert_eq!(machine::llalign_of_min(cx, fill_ty), align); assert_eq!(align_s % discr_size, 0); let fields = [discr_ty, diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 390c0b035fdd5..99bc3ace136bd 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -2126,7 +2126,7 @@ fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool { match *sess.entry_fn.borrow() { - Some((entry_id, _)) => node_id == entry_id, + Some((entry_id, _, _)) => node_id == entry_id, None => false } } @@ -2152,7 +2152,12 @@ pub fn create_entry_wrapper(ccx: &CrateContext, let llfty = Type::func(&[ccx.int_type(), Type::i8p(ccx).ptr_to()], &ccx.int_type()); - let llfn = declare::define_cfn(ccx, "main", llfty, + let main_name = match *ccx.sess().entry_fn.borrow() { + Some((_, ref name, _)) => (*name).to_string(), + None => "main".to_string(), + }; + + let llfn = declare::define_cfn(ccx, &main_name[..], llfty, ty::mk_nil(ccx.tcx())).unwrap_or_else(||{ ccx.sess().span_err(sp, "entry symbol `main` defined multiple times"); // FIXME: We should be smart and show a better diagnostic here. @@ -2712,7 +2717,7 @@ pub fn trans_crate<'tcx>(analysis: ty::CrateAnalysis<'tcx>) } let modules = shared_ccx.iter() - .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() }) + .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod(), name: None, }) .collect(); let mut reachable: Vec = shared_ccx.reachable().iter().filter_map(|id| { @@ -2750,6 +2755,7 @@ pub fn trans_crate<'tcx>(analysis: ty::CrateAnalysis<'tcx>) let metadata_module = ModuleTranslation { llcx: shared_ccx.metadata_llcx(), llmod: shared_ccx.metadata_llmod(), + name: Some("metadata".to_string()), }; let formats = shared_ccx.tcx().dependency_formats.borrow().clone(); let no_builtins = attr::contains_name(&krate.attrs, "no_builtins"); diff --git a/src/librustc_trans/trans/cabi.rs b/src/librustc_trans/trans/cabi.rs index 0ff5264c00f0f..2aac76eecd458 100644 --- a/src/librustc_trans/trans/cabi.rs +++ b/src/librustc_trans/trans/cabi.rs @@ -111,6 +111,7 @@ pub fn compute_abi_info(ccx: &CrateContext, ret_def: bool) -> FnType { match &ccx.sess().target.target.arch[..] { "x86" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def), + "le32" => cabi_x86::compute_abi_info(ccx, atys, rty, ret_def), "x86_64" => if ccx.sess().target.target.options.is_like_windows { cabi_x86_win64::compute_abi_info(ccx, atys, rty, ret_def) } else { diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 6259f8fe1cc2e..41118b3e2d6c1 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -230,10 +230,29 @@ fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx } } +// Does this type need to be passed as a ptr for PNaCl? +pub fn pnacl_type_needs_indirection<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + use llvm::{Struct, Array}; + ccx.sess().target.target.options.is_like_pnacl && { + match type_of::arg_type_of(ccx, ty).kind() { + Struct | Array => true, + _ => false, + } + } +} +#[inline] pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + type_is_immediate_pnacl_check(ccx, ty, true) +} +pub fn type_is_immediate_pnacl_check<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>, + check: bool) -> bool { use trans::machine::llsize_of_alloc; use trans::type_of::sizing_type_of; + if check && pnacl_type_needs_indirection(ccx, ty) { + return false; + } + let tcx = ccx.tcx(); let simple = ty::type_is_scalar(ty) || ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) || diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 663a01e19f342..476c82d87ef15 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -793,22 +793,37 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { /// Declare any llvm intrinsics that you might need fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option { macro_rules! ifn { - ($name:expr, fn() -> $ret:expr) => ( - if *key == $name { - let f = declare::declare_cfn(ccx, $name, Type::func(&[], &$ret), + ($name:expr, fn() -> $ret:expr, if ($cond:expr)) => ({ + let is_key = *key == $name; + if $cond && is_key { + let name = $name; + let f = declare::declare_cfn(ccx, name, Type::func(&[], &$ret), ty::mk_nil(ccx.tcx())); - ccx.intrinsics().borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - ($name:expr, fn($($arg:expr),*) -> $ret:expr) => ( - if *key == $name { - let f = declare::declare_cfn(ccx, $name, Type::func(&[$($arg),*], &$ret), - ty::mk_nil(ccx.tcx())); - ccx.intrinsics().borrow_mut().insert($name, f.clone()); + if ccx.sess().target.target.options.is_like_pnacl { + llvm::SetUnnamedAddr(f, false); + } + ccx.intrinsics().borrow_mut().insert(name, f); return Some(f); - } - ) + } else if is_key { return None; } + }); + ($name:expr, fn() -> $ret:expr) => (ifn!($name, fn() -> $ret, if (true))); + ($name:expr, fn($($arg:expr),*) -> $ret:expr, + if ($cond:expr)) => ({ + let is_key = *key == $name; + if $cond && is_key { + let name = $name; + let f = declare::declare_cfn(ccx, name, + Type::func(&[$($arg),*], &$ret), + ty::mk_nil(ccx.tcx())); + if ccx.sess().target.target.options.is_like_pnacl { + llvm::SetUnnamedAddr(f, false); + } + ccx.intrinsics().borrow_mut().insert(name, f); + return Some(f); + } else if is_key { return None; } + }); + ($name:expr, fn($($arg:expr),*) -> $ret:expr) => + (ifn!($name, fn($($arg),*) -> $ret, if (true))) } macro_rules! mk_struct { ($($field_ty:expr),*) => (Type::struct_(ccx, &[$($field_ty),*], false)) @@ -833,65 +848,35 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option void); ifn!("llvm.debugtrap", fn() -> void); - ifn!("llvm.frameaddress", fn(t_i32) -> i8p); + ifn!("llvm.frameaddress", fn(t_i32) -> i8p, + if (!ccx.sess().target.target.options.is_like_pnacl)); - ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64); + ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32, + if (!ccx.sess().target.target.options.is_like_pnacl)); + ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64, + if (!ccx.sess().target.target.options.is_like_pnacl)); ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32); ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64); - ifn!("llvm.sin.f32", fn(t_f32) -> t_f32); - ifn!("llvm.sin.f64", fn(t_f64) -> t_f64); - ifn!("llvm.cos.f32", fn(t_f32) -> t_f32); - ifn!("llvm.cos.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log10.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32); - ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); - - ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); - ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); - ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); - ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32); - ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); - ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32); - ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.copysign.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.copysign.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.round.f32", fn(t_f32) -> t_f32); - ifn!("llvm.round.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.rint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.rint.f64", fn(t_f64) -> t_f64); - ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8); - ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16); + + ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8, + if (!ccx.sess().target.target.options.is_like_pnacl)); + ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16, + if (!ccx.sess().target.target.options.is_like_pnacl)); ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32); ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64); - ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8); - ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8, + if (!ccx.sess().target.target.options.is_like_pnacl)); + ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16, + if (!ccx.sess().target.target.options.is_like_pnacl)); ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32); ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8); - ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8, + if (!ccx.sess().target.target.options.is_like_pnacl)); + ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16, + if (!ccx.sess().target.target.options.is_like_pnacl)); ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32); ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64); @@ -936,6 +921,8 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option void), $llvm_version:expr) => ( if unsafe { llvm::LLVMVersionMinor() >= $llvm_version } { @@ -961,7 +948,20 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option $ret:expr, $llvm_version:expr) => ( - if unsafe { llvm::LLVMVersionMinor() >= $llvm_version } { + if unsafe { llvm::LLVMVersionMinor() >= $llvm_version } && + !ccx.sess().target.target.options.is_like_pnacl { + // The `if key == $name` is already in ifn! + ifn!($name, fn($($arg),*) -> $ret); + } else if *key == $name { + let f = declare::declare_cfn(ccx, stringify!($cname), + Type::func(&[$($arg),*], &$ret), + ty::mk_nil(ccx.tcx())); + ccx.intrinsics().borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + ($name:expr, $cname:ident ($($arg:expr),*) -> $ret:expr) => ( + if !ccx.sess().target.target.options.is_like_pnacl { // The `if key == $name` is already in ifn! ifn!($name, fn($($arg),*) -> $ret); } else if *key == $name { @@ -974,7 +974,47 @@ fn declare_intrinsic(ccx: &CrateContext, key: & &'static str) -> Option t_f32); + compatible_ifn!("llvm.copysign.f64", copysign(t_f64, t_f64) -> t_f64); + compatible_ifn!("llvm.round.f32", roundf(t_f32) -> t_f32); + compatible_ifn!("llvm.round.f64", round(t_f64) -> t_f64); + compatible_ifn!("llvm.assume", noop(llvmcompat_assume(i1) -> void), 6); + compatible_ifn!("llvm.pow.f32", powf(t_f32, t_f32) -> t_f32); + compatible_ifn!("llvm.pow.f64", pow (t_f64, t_f64) -> t_f64); + + compatible_ifn!("llvm.sin.f32", sinf(t_f32) -> t_f32); + compatible_ifn!("llvm.sin.f64", sin (t_f64) -> t_f64); + compatible_ifn!("llvm.cos.f32", cosf(t_f32) -> t_f32); + compatible_ifn!("llvm.cos.f64", cos (t_f64) -> t_f64); + compatible_ifn!("llvm.exp.f32", expf(t_f32) -> t_f32); + compatible_ifn!("llvm.exp.f64", exp (t_f64) -> t_f64); + compatible_ifn!("llvm.exp2.f32", exp2f(t_f32) -> t_f32); + compatible_ifn!("llvm.exp2.f64", exp2(t_f64) -> t_f64); + compatible_ifn!("llvm.log.f32", logf(t_f32) -> t_f32); + compatible_ifn!("llvm.log.f64", log (t_f64) -> t_f64); + compatible_ifn!("llvm.log10.f32",log10f(t_f32) -> t_f32); + compatible_ifn!("llvm.log10.f64",log10(t_f64) -> t_f64); + compatible_ifn!("llvm.log2.f32", log2f(t_f32) -> t_f32); + compatible_ifn!("llvm.log2.f64", log2(t_f64) -> t_f64); + + compatible_ifn!("llvm.fma.f32", fmaf(t_f32, t_f32, t_f32) -> t_f32); + compatible_ifn!("llvm.fma.f64", fma (t_f64, t_f64, t_f64) -> t_f64); + + compatible_ifn!("llvm.fabs.f32", fabsf(t_f32) -> t_f32); + compatible_ifn!("llvm.fabs.f64", fabs(t_f64) -> t_f64); + + compatible_ifn!("llvm.floor.f32",floorf(t_f32) -> t_f32); + compatible_ifn!("llvm.floor.f64",floor(t_f64) -> t_f64); + compatible_ifn!("llvm.ceil.f32", ceilf(t_f32) -> t_f32); + compatible_ifn!("llvm.ceil.f64", ceil(t_f64) -> t_f64); + compatible_ifn!("llvm.trunc.f32",truncf(t_f32) -> t_f32); + compatible_ifn!("llvm.trunc.f64",trunc(t_f64) -> t_f64); + + compatible_ifn!("llvm.rint.f32", rintf(t_f32) -> t_f32); + compatible_ifn!("llvm.rint.f64", rint(t_f64) -> t_f64); + compatible_ifn!("llvm.nearbyint.f32", nearbyintf(t_f32) -> t_f32); + compatible_ifn!("llvm.nearbyint.f64", nearbyint(t_f64) -> t_f64); if ccx.sess().opts.debuginfo != NoDebugInfo { ifn!("llvm.dbg.declare", fn(Type::metadata(ccx), Type::metadata(ccx)) -> void); diff --git a/src/librustc_trans/trans/debuginfo/gdb.rs b/src/librustc_trans/trans/debuginfo/gdb.rs index a6f1199d0ffe7..57dbde5c409d1 100644 --- a/src/librustc_trans/trans/debuginfo/gdb.rs +++ b/src/librustc_trans/trans/debuginfo/gdb.rs @@ -27,14 +27,24 @@ use syntax::attr; /// Inserts a side-effect free instruction sequence that makes sure that the /// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. pub fn insert_reference_to_gdb_debug_scripts_section_global(ccx: &CrateContext) { + use trans::common::C_i32; if needs_gdb_debug_scripts_section(ccx) { let empty = CString::new("").unwrap(); let gdb_debug_scripts_section_global = get_or_insert_gdb_debug_scripts_section_global(ccx); unsafe { + // PNaCl's -nacl-rewrite-atomics can't rewrite loading directly from + // gdb_debug_scripts_section_global, ie a large array of i8. + let indices = [C_i32(ccx, 0), C_i32(ccx, 0)]; + let element = + llvm::LLVMBuildInBoundsGEP(ccx.raw_builder(), + gdb_debug_scripts_section_global, + indices.as_ptr(), + indices.len() as ::libc::c_uint, + empty.as_ptr()); let volative_load_instruction = llvm::LLVMBuildLoad(ccx.raw_builder(), - gdb_debug_scripts_section_global, + element, empty.as_ptr()); llvm::LLVMSetVolatile(volative_load_instruction, llvm::True); } diff --git a/src/librustc_trans/trans/mod.rs b/src/librustc_trans/trans/mod.rs index 7d568ff90ea43..c90e2730f6c75 100644 --- a/src/librustc_trans/trans/mod.rs +++ b/src/librustc_trans/trans/mod.rs @@ -59,10 +59,11 @@ mod type_; mod type_of; mod value; -#[derive(Copy, Clone)] +#[derive(Clone)] pub struct ModuleTranslation { pub llcx: ContextRef, pub llmod: ModuleRef, + pub name: Option, } unsafe impl Send for ModuleTranslation { } diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs index 9f01521313633..d35ee15e9102f 100644 --- a/src/librustc_trans/trans/type_of.rs +++ b/src/librustc_trans/trans/type_of.rs @@ -43,7 +43,12 @@ pub fn arg_is_indirect<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, pub fn return_uses_outptr<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { - !type_is_immediate(ccx, ty) + if ccx.sess().target.target.options.is_like_pnacl && + return_type_is_void(ccx, ty) { + false + } else { + !type_is_immediate(ccx, ty) + } } pub fn type_of_explicit_arg<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, @@ -250,12 +255,15 @@ pub fn foreign_arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) - pub fn arg_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { if ty::type_is_bool(t) { Type::i1(cx) - } else if type_is_immediate(cx, t) && type_of(cx, t).is_aggregate() { + } else if type_is_immediate_pnacl_check(cx, t, false) && type_of(cx, t).is_aggregate() { // We want to pass small aggregates as immediate values, but using an aggregate LLVM type // for this leads to bad optimizations, so its arg type is an appropriately sized integer match machine::llsize_of_alloc(cx, sizing_type_of(cx, t)) { - 0 => type_of(cx, t), - n => Type::ix(cx, n * 8), + n if n == 0 || (cx.sess().target.target.options.is_like_pnacl && n > 8) => + type_of(cx, t), + n if !cx.sess().target.target.options.is_like_pnacl || n <= 8 => + Type::ix(cx, n * 8), + _ => unreachable!(), } } else { type_of(cx, t) diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index ed398c2cdedfc..10d8246d7fda0 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -306,7 +306,7 @@ fn check_start_fn_ty(ccx: &CrateCtxt, fn check_for_entry_fn(ccx: &CrateCtxt) { let tcx = ccx.tcx; match *tcx.sess.entry_fn.borrow() { - Some((id, sp)) => match tcx.sess.entry_type.get() { + Some((id, _, sp)) => match tcx.sess.entry_type.get() { Some(config::EntryMain) => check_main_fn_ty(ccx, id, sp), Some(config::EntryStart) => check_start_fn_ty(ccx, id, sp), Some(config::EntryNone) => {} diff --git a/src/librustdoc/flock.rs b/src/librustdoc/flock.rs index 760fa329fd986..80128845ede1d 100644 --- a/src/librustdoc/flock.rs +++ b/src/librustdoc/flock.rs @@ -47,6 +47,27 @@ mod imp { pub const F_SETLKW: libc::c_int = 7; } + #[cfg(target_os = "nacl")] + mod os { + use libc; + + pub struct flock { + pub l_type: libc::c_short, + pub l_whence: libc::c_short, + pub l_start: libc::off_t, + pub l_len: libc::off_t, + pub l_pid: libc::pid_t, + + // not actually here, but brings in line with freebsd + pub l_sysid: libc::c_int, + } + + pub const F_WRLCK: libc::c_short = 2; + pub const F_UNLCK: libc::c_short = 3; + pub const F_SETLK: libc::c_int = 8; + pub const F_SETLKW: libc::c_int = 9; + } + #[cfg(target_os = "freebsd")] mod os { use libc; diff --git a/src/libstd/dynamic_lib.rs b/src/libstd/dynamic_lib.rs index ebdc049bc7f5d..87b4537d6fe26 100644 --- a/src/libstd/dynamic_lib.rs +++ b/src/libstd/dynamic_lib.rs @@ -116,7 +116,7 @@ impl DynamicLibrary { } } -#[cfg(all(test, not(target_os = "ios")))] +#[cfg(all(test, not(target_os = "ios"), not(target_os = "nacl")))] mod tests { use super::*; use prelude::v1::*; @@ -368,3 +368,30 @@ mod dl { fn SetErrorMode(uMode: libc::c_uint) -> libc::c_uint; } } + +#[cfg(target_os = "nacl")] +pub mod dl { + use ffi::OsStr; + use ptr; + use result::Result; + use result::Result::Err; + use libc; + use string::String; + use ops::FnOnce; + use option::Option; + + pub fn open(_filename: Option<&OsStr>) -> Result<*mut u8, String> { + Err(format!("NaCl + Newlib doesn't impl loading shared objects")) + } + + pub fn check_for_errors_in(_f: F) -> Result + where F: FnOnce() -> T, + { + Err(format!("NaCl doesn't support shared objects")) + } + + pub unsafe fn symbol(_handle: *mut u8, _symbol: *const libc::c_char) -> *mut u8 { + ptr::null_mut() + } + pub unsafe fn close(_handle: *mut u8) { } +} diff --git a/src/libstd/env.rs b/src/libstd/env.rs index 379c925b5750e..32b16e96853c7 100644 --- a/src/libstd/env.rs +++ b/src/libstd/env.rs @@ -790,6 +790,27 @@ mod os { pub const EXE_EXTENSION: &'static str = "exe"; } +#[cfg(all(target_os = "nacl", not(target_arch = "le32")))] +mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "nacl"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".so"; + pub const DLL_EXTENSION: &'static str = "so"; + pub const EXE_SUFFIX: &'static str = ".nexe"; + pub const EXE_EXTENSION: &'static str = "nexe"; +} +#[cfg(all(target_os = "nacl", target_arch = "le32"))] +mod os { + pub const FAMILY: &'static str = "unix"; + pub const OS: &'static str = "pnacl"; + pub const DLL_PREFIX: &'static str = "lib"; + pub const DLL_SUFFIX: &'static str = ".pso"; + pub const DLL_EXTENSION: &'static str = "pso"; + pub const EXE_SUFFIX: &'static str = ".pexe"; + pub const EXE_EXTENSION: &'static str = "pexe"; +} + #[cfg(target_arch = "x86")] mod arch { pub const ARCH: &'static str = "x86"; @@ -825,6 +846,11 @@ mod arch { pub const ARCH: &'static str = "powerpc"; } +#[cfg(target_arch = "le32")] +mod arch { + pub const ARCH: &'static str = "le32"; +} + #[cfg(test)] mod tests { use prelude::v1::*; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 8305088057c41..ce847ea80527e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -268,9 +268,13 @@ pub mod sync; pub mod time; #[macro_use] +#[cfg_attr(target_os = "nacl", allow(dead_code))] +#[cfg_attr(target_os = "nacl", allow(unused_imports))] #[path = "sys/common/mod.rs"] mod sys_common; #[cfg(unix)] +#[cfg_attr(target_os = "nacl", allow(dead_code))] +#[cfg_attr(target_os = "nacl", allow(unused_imports))] #[path = "sys/unix/mod.rs"] mod sys; #[cfg(windows)] #[path = "sys/windows/mod.rs"] mod sys; diff --git a/src/libstd/os/nacl/raw.rs b/src/libstd/os/nacl/raw.rs index 9defa8301ea34..c05fd70115bfe 100644 --- a/src/libstd/os/nacl/raw.rs +++ b/src/libstd/os/nacl/raw.rs @@ -10,160 +10,6 @@ //! Nacl-specific raw type definitions -pub type dev_t = u64; -pub type mode_t = u32; - -pub use self::arch::{off_t, ino_t, nlink_t, blksize_t, blkcnt_t, stat, time_t}; - -#[cfg(any(target_arch = "x86", - target_arch = "le32", - target_arch = "powerpc", - target_arch = "arm"))] -mod arch { - use super::{dev_t, mode_t}; - use os::raw::{c_long, c_short}; - use os::unix::raw::{gid_t, uid_t}; - - pub type blkcnt_t = i32; - pub type blksize_t = i32; - pub type ino_t = u32; - pub type nlink_t = u32; - pub type off_t = i32; - pub type time_t = i32; - - #[repr(C)] - pub struct stat { - pub st_dev: dev_t, - pub __pad1: c_short, - pub st_ino: ino_t, - pub st_mode: mode_t, - pub st_nlink: nlink_t, - pub st_uid: uid_t, - pub st_gid: gid_t, - pub st_rdev: dev_t, - pub __pad2: c_short, - pub st_size: off_t, - pub st_blksize: blksize_t, - pub st_blocks: blkcnt_t, - pub st_atime: time_t, - pub st_atime_nsec: c_long, - pub st_mtime: time_t, - pub st_mtime_nsec: c_long, - pub st_ctime: time_t, - pub st_ctime_nsec: c_long, - pub __unused4: c_long, - pub __unused5: c_long, - } -} - -#[cfg(any(target_arch = "mips", - target_arch = "mipsel"))] -mod arch { - use super::{dev_t, mode_t}; - use os::raw::c_long; - use os::unix::raw::{gid_t, uid_t}; - - pub type blkcnt_t = i32; - pub type blksize_t = i32; - pub type ino_t = u32; - pub type nlink_t = u32; - pub type off_t = i32; - pub type time_t = i32; - - #[repr(C)] - pub struct stat { - pub st_dev: c_ulong, - pub st_pad1: [c_long; 3], - pub st_ino: ino_t, - pub st_mode: mode_t, - pub st_nlink: nlink_t, - pub st_uid: uid_t, - pub st_gid: gid_t, - pub st_rdev: c_ulong, - pub st_pad2: [c_long; 2], - pub st_size: off_t, - pub st_pad3: c_long, - pub st_atime: time_t, - pub st_atime_nsec: c_long, - pub st_mtime: time_t, - pub st_mtime_nsec: c_long, - pub st_ctime: time_t, - pub st_ctime_nsec: c_long, - pub st_blksize: blksize_t, - pub st_blocks: blkcnt_t, - pub st_pad5: [c_long; 14], - } -} - -#[cfg(target_arch = "aarch64")] -mod arch { - use super::{dev_t, mode_t}; - use os::raw::{c_long, c_int}; - use os::unix::raw::{gid_t, uid_t}; - - pub type blkcnt_t = i64; - pub type blksize_t = i32; - pub type ino_t = u64; - pub type nlink_t = u32; - pub type off_t = i64; - pub type time_t = i64; - - #[repr(C)] - pub struct stat { - pub st_dev: dev_t, - pub st_ino: ino_t, - pub st_mode: mode_t, - pub st_nlink: nlink_t, - pub st_uid: uid_t, - pub st_gid: gid_t, - pub st_rdev: dev_t, - pub __pad1: dev_t, - pub st_size: off_t, - pub st_blksize: blksize_t, - pub __pad2: c_int, - pub st_blocks: blkcnt_t, - pub st_atime: time_t, - pub st_atime_nsec: c_long, - pub st_mtime: time_t, - pub st_mtime_nsec: c_long, - pub st_ctime: time_t, - pub st_ctime_nsec: c_long, - pub __unused: [c_int; 2], - } -} - -#[cfg(target_arch = "x86_64")] -mod arch { - use super::{dev_t, mode_t}; - use os::raw::{c_long, c_int}; - use os::unix::raw::{gid_t, uid_t}; - - pub type blkcnt_t = i64; - pub type blksize_t = i64; - pub type ino_t = u64; - pub type nlink_t = u64; - pub type off_t = i64; - pub type time_t = i64; - - #[repr(C)] - pub struct stat { - pub st_dev: dev_t, - pub st_ino: ino_t, - pub st_nlink: nlink_t, - pub st_mode: mode_t, - pub st_uid: uid_t, - pub st_gid: gid_t, - pub __pad0: c_int, - pub st_rdev: dev_t, - pub st_size: off_t, - pub st_blksize: blksize_t, - pub st_blocks: blkcnt_t, - pub st_atime: time_t, - pub st_atime_nsec: c_long, - pub st_mtime: time_t, - pub st_mtime_nsec: c_long, - pub st_ctime: time_t, - pub st_ctime_nsec: c_long, - pub __unused: [c_long; 3], - } -} +// Reuse the definitions from libc. +pub use libc::{dev_t, ino_t, mode_t, nlink_t, uid_t, gid_t, off_t, time_t, + blkcnt_t, blksize_t, stat}; diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs index d23a124a6ecda..6fe3c6e9d72ee 100644 --- a/src/libstd/rt/args.rs +++ b/src/libstd/rt/args.rs @@ -44,7 +44,8 @@ pub fn clone() -> Option>> { imp::clone() } target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] mod imp { use prelude::v1::*; diff --git a/src/libstd/rt/libunwind.rs b/src/libstd/rt/libunwind.rs index 8f75ae5ef5cc8..9524803b38cf9 100644 --- a/src/libstd/rt/libunwind.rs +++ b/src/libstd/rt/libunwind.rs @@ -84,6 +84,9 @@ pub const unwinder_private_data_size: usize = 2; #[cfg(target_arch = "powerpc")] pub const unwinder_private_data_size: usize = 2; +#[cfg(target_os = "nacl")] +pub const unwinder_private_data_size: usize = 2; + #[repr(C)] pub struct _Unwind_Exception { pub exception_class: _Unwind_Exception_Class, @@ -97,8 +100,9 @@ pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), - target_os = "freebsd"))] +#[cfg(all(any(all(target_os = "linux", not(target_env = "musl")), + target_os = "freebsd"), + not(target_os = "nacl")))] #[link(name = "gcc_s")] extern {} @@ -118,6 +122,7 @@ extern {} #[link(name = "c++abi")] extern {} +#[cfg(not(target_os = "nacl"))] extern "C" { // iOS on armv7 uses SjLj exceptions and requires to link // against corresponding routine (..._SjLj_...) @@ -141,3 +146,12 @@ pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code { _Unwind_SjLj_RaiseException(exc) } + +#[cfg(target_os = "nacl")] +extern "C" { + #[link_name = "__pnacl_eh_sjlj_Unwind_RaiseException"] + pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) + -> _Unwind_Reason_Code; + #[link_name = "__pnacl_eh_sjlj_Unwind_DeleteException"] + pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); +} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 0d26206f26bcf..107db60fd6073 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -111,8 +111,8 @@ fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { // // Hence, we set SIGPIPE to ignore when the program starts up in order // to prevent this problem. - #[cfg(windows)] fn ignore_sigpipe() {} - #[cfg(unix)] fn ignore_sigpipe() { + #[cfg(any(windows, target_os = "nacl"))] fn ignore_sigpipe() {} + #[cfg(all(unix, not(target_os = "nacl")))] fn ignore_sigpipe() { use libc; use libc::funcs::posix01::signal::signal; unsafe { diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs index 39b32a3f08e49..bdcd1c20ea269 100644 --- a/src/libstd/rt/unwind/gcc.rs +++ b/src/libstd/rt/unwind/gcc.rs @@ -90,6 +90,7 @@ pub mod eabi { use rt::libunwind as uw; use libc::c_int; + #[cfg(not(target_os = "nacl"))] extern "C" { fn __gcc_personality_v0(version: c_int, actions: uw::_Unwind_Action, @@ -98,6 +99,20 @@ pub mod eabi { context: *mut uw::_Unwind_Context) -> uw::_Unwind_Reason_Code; } + #[cfg(target_os = "nacl")] + unsafe fn __gcc_personality_v0(_version: c_int, + _actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context) -> + uw::_Unwind_Reason_Code + { + // we just need to be here to prevent linker errors. + // based on my analysis of PNaClSJLJ.cpp and ExceptionInfoWriter.cpp, + // the personality functions are just discarded without any consultation. + use core::intrinsics::abort; + abort() + } #[lang="eh_personality"] #[no_mangle] // referenced from rust_try.ll diff --git a/src/libstd/rtdeps.rs b/src/libstd/rtdeps.rs index a7f3bc2bdc82c..07ed04792c5c3 100644 --- a/src/libstd/rtdeps.rs +++ b/src/libstd/rtdeps.rs @@ -45,6 +45,19 @@ extern {} #[link(name = "pthread")] extern {} +// For PNaCl targets, nacl_io is a Pepper wrapper for some IO functions +// missing (ie always error) in Newlib. +#[cfg(all(target_os = "nacl", not(test)))] +#[link(name = "nacl_io", kind = "static")] +#[link(name = "c++", kind = "static")] // for `nacl_io` and EH. +#[link(name = "pthread", kind = "static")] +extern {} + + +#[cfg(all(target_os = "nacl", not(test)))] +#[link(name = "c++", kind = "static")] +extern {} + #[cfg(target_os = "macos")] #[link(name = "System")] extern {} diff --git a/src/libstd/sys/common/stack.rs b/src/libstd/sys/common/stack.rs index 11982ebc572e5..4459346205c4e 100644 --- a/src/libstd/sys/common/stack.rs +++ b/src/libstd/sys/common/stack.rs @@ -202,7 +202,8 @@ pub unsafe fn record_sp_limit(limit: usize) { target_arch = "powerpc", all(target_arch = "arm", target_os = "ios"), target_os = "bitrig", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] unsafe fn target_record_sp_limit(_: usize) { } } @@ -299,7 +300,8 @@ pub unsafe fn get_sp_limit() -> usize { target_arch = "powerpc", all(target_arch = "arm", target_os = "ios"), target_os = "bitrig", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] #[inline(always)] unsafe fn target_get_sp_limit() -> usize { 1024 diff --git a/src/libstd/sys/unix/backtrace.rs b/src/libstd/sys/unix/backtrace.rs index b23a3eee1a173..cf206b7284818 100644 --- a/src/libstd/sys/unix/backtrace.rs +++ b/src/libstd/sys/unix/backtrace.rs @@ -133,7 +133,17 @@ pub fn write(w: &mut Write) -> io::Result<()> { result::fold(iter, (), |_, _| ()) } -#[cfg(not(all(target_os = "ios", target_arch = "arm")))] +#[cfg(target_os = "nacl")] +#[inline(never)] +pub fn write(_w: &mut Write) -> io::Result<()> { + use io::ErrorKind; + Err(io::Error::new(ErrorKind::PermissionDenied, + "can't read the stack instruction pointer \ + to find the backtrace")) +} + +#[cfg(not(any(all(target_os = "ios", target_arch = "arm"), + target_os = "nacl")))] #[inline(never)] // if we know this is a function call, we can skip it when // tracing pub fn write(w: &mut Write) -> io::Result<()> { @@ -247,7 +257,16 @@ fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, } } -#[cfg(not(any(target_os = "macos", target_os = "ios")))] +#[cfg(target_os = "nacl")] +fn print(_w: &mut Write, _idx: isize, _addr: *mut libc::c_void, + _symaddr: *mut libc::c_void) -> io::Result<()> { + use io::ErrorKind; + Err(io::Error::new(ErrorKind::PermissionDenied, + "can't read the stack instruction pointer \ + to find the backtrace")) +} + +#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "nacl")))] fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, symaddr: *mut libc::c_void) -> io::Result<()> { use env; @@ -497,20 +516,23 @@ mod uw { extern { // No native _Unwind_Backtrace on iOS - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] + #[cfg(not(any(all(target_os = "ios", target_arch = "arm"), + target_os = "nacl")))] pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, trace_argument: *mut libc::c_void) -> _Unwind_Reason_Code; // available since GCC 4.2.0, should be fine for our purpose #[cfg(all(not(all(target_os = "android", target_arch = "arm")), - not(all(target_os = "linux", target_arch = "arm"))))] + not(all(target_os = "linux", target_arch = "arm")), + not(target_os = "nacl")))] pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, ip_before_insn: *mut libc::c_int) -> libc::uintptr_t; #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] + not(all(target_os = "linux", target_arch = "arm")), + not(target_os = "nacl")))] pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) -> *mut libc::c_void; } diff --git a/src/libstd/sys/unix/c.rs b/src/libstd/sys/unix/c.rs index 1e68eac5a6735..2d4188ccec34f 100644 --- a/src/libstd/sys/unix/c.rs +++ b/src/libstd/sys/unix/c.rs @@ -14,9 +14,7 @@ #![allow(non_camel_case_types)] pub use self::select::fd_set; -pub use self::signal::{sigaction, siginfo, sigset_t}; -pub use self::signal::{SA_ONSTACK, SA_RESTART, SA_RESETHAND, SA_NOCLDSTOP}; -pub use self::signal::{SA_NODEFER, SA_NOCLDWAIT, SA_SIGINFO, SIGCHLD}; +pub use self::signal::*; use libc; @@ -54,6 +52,10 @@ mod consts { pub const FIOCLEX: libc::c_ulong = 0x6601; pub const FIONCLEX: libc::c_ulong = 0x6600; } + +#[cfg(target_os = "nacl")] +mod consts { } + pub use self::consts::*; #[cfg(any(target_os = "macos", @@ -61,7 +63,8 @@ pub use self::consts::*; target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] pub const MSG_DONTWAIT: libc::c_int = 0x80; #[cfg(any(target_os = "linux", target_os = "android"))] pub const MSG_DONTWAIT: libc::c_int = 0x40; @@ -91,6 +94,18 @@ pub struct passwd { pub pw_dir: *mut libc::c_char, pub pw_shell: *mut libc::c_char, } +#[repr(C)] +#[cfg(target_os = "nacl")] +pub struct passwd { + pub pw_name: *mut libc::c_char, + pub pw_passwd: *mut libc::c_char, + pub pw_uid: libc::uid_t, + pub pw_gid: libc::gid_t, + pub pw_comment: *mut libc::c_char, + pub pw_gecos: *mut libc::c_char, + pub pw_dir: *mut libc::c_char, + pub pw_shell: *mut libc::c_char, +} #[repr(C)] #[cfg(any(target_os = "macos", @@ -184,7 +199,8 @@ mod select { target_os = "dragonfly", target_os = "bitrig", target_os = "openbsd", - target_os = "linux"))] + target_os = "linux", + target_os = "nacl"))] mod select { use usize; use libc; @@ -203,6 +219,27 @@ mod select { } } +#[cfg(target_os = "nacl")] +mod signal { + use libc; + + pub static SA_NOCLDSTOP: libc::c_ulong = 1; + pub static SA_SIGINFO: libc::c_ulong = 2; + #[repr(C)] + pub struct siginfo { + pub si_signo: libc::c_int, + pub si_code: libc::c_int, + pub si_val: usize, + } + pub type sigset_t = libc::c_ulong; + #[repr(C)] + pub struct sigaction { + pub sa_flags: libc::c_int, + pub sa_mask: sigset_t, + pub handler: extern fn(libc::c_int), + } +} + #[cfg(any(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64", diff --git a/src/libstd/sys/unix/fd.rs b/src/libstd/sys/unix/fd.rs index 026380027d29e..c70e03debfbc3 100644 --- a/src/libstd/sys/unix/fd.rs +++ b/src/libstd/sys/unix/fd.rs @@ -53,12 +53,15 @@ impl FileDesc { Ok(ret as usize) } + #[cfg(not(target_os = "nacl"))] pub fn set_cloexec(&self) { unsafe { let ret = c::ioctl(self.fd, c::FIOCLEX); debug_assert_eq!(ret, 0); } } + #[cfg(target_os = "nacl")] + pub fn set_cloexec(&self) { } } impl AsInner for FileDesc { diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs index 51a85a276eda5..32390e9b7b0eb 100644 --- a/src/libstd/sys/unix/fs.rs +++ b/src/libstd/sys/unix/fs.rs @@ -459,9 +459,18 @@ pub fn rmdir(p: &Path) -> io::Result<()> { } pub fn readlink(p: &Path) -> io::Result { + #[cfg(not(target_libc = "newlib"))] + unsafe fn pathconf(p: *mut libc::c_char) -> i64 { + libc::pathconf(p, libc::_PC_NAME_MAX) as i64 + } + #[cfg(target_libc = "newlib")] + unsafe fn pathconf(_: *mut libc::c_char) -> i64 { + libc::sysconf(libc::_PC_NAME_MAX) as i64 + } + let c_path = try!(cstr(p)); let p = c_path.as_ptr(); - let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) }; + let mut len = unsafe { pathconf(p as *mut _) }; if len < 0 { len = 1024; // FIXME: read PATH_MAX from C ffi? } diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs index 5178d7b8fb1a0..4456b9487ff18 100644 --- a/src/libstd/sys/unix/os.rs +++ b/src/libstd/sys/unix/os.rs @@ -78,6 +78,15 @@ pub fn errno() -> i32 { extern { fn __errno_location() -> *const c_int; } __errno_location() } + #[cfg(target_libc = "newlib")] + fn errno_location() -> *const c_int { + extern { + fn __errno() -> *const c_int; + } + unsafe { + __errno() + } + } unsafe { (*errno_location()) as i32 @@ -86,13 +95,13 @@ pub fn errno() -> i32 { /// Gets a detailed string description for the given error number. pub fn error_string(errno: i32) -> String { - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "nacl"))] extern { #[link_name = "__xpg_strerror_r"] fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; } - #[cfg(not(target_os = "linux"))] + #[cfg(not(any(target_os = "linux", target_os = "nacl")))] extern { fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t) -> c_int; @@ -236,7 +245,7 @@ pub fn current_exe() -> io::Result { } } -#[cfg(any(target_os = "linux", target_os = "android"))] +#[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))] pub fn current_exe() -> io::Result { ::fs::read_link("/proc/self/exe") } @@ -356,7 +365,8 @@ pub fn args() -> Args { target_os = "freebsd", target_os = "dragonfly", target_os = "bitrig", - target_os = "openbsd"))] + target_os = "openbsd", + target_os = "nacl"))] pub fn args() -> Args { use rt; let bytes = rt::args::clone().unwrap_or(Vec::new()); @@ -468,10 +478,12 @@ pub fn home_dir() -> Option { }).map(os2path); #[cfg(any(target_os = "android", - target_os = "ios"))] + target_os = "ios", + target_os = "nacl"))] unsafe fn fallback() -> Option { None } #[cfg(not(any(target_os = "android", - target_os = "ios")))] + target_os = "ios", + target_os = "nacl")))] unsafe fn fallback() -> Option { let amt = match libc::sysconf(c::_SC_GETPW_R_SIZE_MAX) { n if n < 0 => 512 as usize, diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs index 76af42d9348de..998cc5a2e6d96 100644 --- a/src/libstd/sys/unix/process.rs +++ b/src/libstd/sys/unix/process.rs @@ -393,7 +393,8 @@ fn make_envp(env: Option<&HashMap>) fn translate_status(status: c_int) -> ExitStatus { #![allow(non_snake_case)] - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", + target_os = "nacl"))] mod imp { pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 } pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff } diff --git a/src/libstd/sys/unix/sync.rs b/src/libstd/sys/unix/sync.rs index 41e1e206a423a..5744551263fc8 100644 --- a/src/libstd/sys/unix/sync.rs +++ b/src/libstd/sys/unix/sync.rs @@ -34,6 +34,8 @@ extern { // cvars pub fn pthread_cond_wait(cond: *mut pthread_cond_t, lock: *mut pthread_mutex_t) -> libc::c_int; + + #[cfg_attr(target_os = "nacl", link_name = "pthread_cond_timedwait_abs")] pub fn pthread_cond_timedwait(cond: *mut pthread_cond_t, lock: *mut pthread_mutex_t, abstime: *const libc::timespec) -> libc::c_int; @@ -246,3 +248,55 @@ mod os { }; pub const PTHREAD_MUTEX_RECURSIVE: libc::c_int = 1; } +#[cfg(target_os = "nacl")] +mod os { + use libc; + use ptr; + + #[repr(C)] + pub struct __nc_basic_thread_data; + + #[repr(C)] + pub struct pthread_mutex_t { + mutex_state: libc::c_int, + mutex_type: libc::c_int, + owner_thread_id: *mut __nc_basic_thread_data, + recursion_counter: libc::uint32_t, + _unused: libc::c_int, + } + #[repr(C)] + pub struct pthread_mutexattr_t { + kind: libc::c_int, + } + #[repr(C)] + pub struct pthread_cond_t { + sequence_number: libc::c_int, + _unused: libc::c_int, + } + #[repr(C)] + pub struct pthread_rwlock_t { + readers: libc::c_int, + writers: libc::c_int, + } + + const NC_INVALID_HANDLE: libc::c_int = -1; + const NACL_PTHREAD_ILLEGAL_THREAD_ID: *mut __nc_basic_thread_data + = 0 as *mut __nc_basic_thread_data; + + pub const PTHREAD_MUTEX_INITIALIZER: pthread_mutex_t = pthread_mutex_t { + mutex_state: 0, + mutex_type: 1, + owner_thread_id: NACL_PTHREAD_ILLEGAL_THREAD_ID, + recursion_counter: 0, + _unused: NC_INVALID_HANDLE, + }; + pub const PTHREAD_COND_INITIALIZER: pthread_cond_t = pthread_cond_t { + sequence_number: 0, + _unused: NC_INVALID_HANDLE, + }; + pub const PTHREAD_RWLOCK_INITIALIZER: pthread_rwlock_t = pthread_rwlock_t { + readers: 0, + writers: 0, + }; + pub const PTHREAD_MUTEX_RECURSIVE: libc::c_int = 1; +} diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index bb0e12e8df899..75a1d5089678e 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -127,6 +127,10 @@ impl Thread { pthread_setname_np(cname.as_ptr()); } } + #[cfg(target_libc = "newlib")] + pub unsafe fn set_name(_name: &str) { + // Newlib has no way to set a thread name. + } pub fn sleep(dur: Duration) { let mut ts = libc::timespec { @@ -198,7 +202,7 @@ pub mod guard { current() as *mut libc::c_void } - #[cfg(any(target_os = "linux", target_os = "android"))] + #[cfg(any(target_os = "linux", target_os = "android", target_os = "nacl"))] unsafe fn get_stack_start() -> *mut libc::c_void { let mut attr: libc::pthread_attr_t = mem::zeroed(); assert_eq!(pthread_getattr_np(pthread_self(), &mut attr), 0); diff --git a/src/libstd/sys/unix/time.rs b/src/libstd/sys/unix/time.rs index 6b84baeca7dc9..facd8044a83b7 100644 --- a/src/libstd/sys/unix/time.rs +++ b/src/libstd/sys/unix/time.rs @@ -77,11 +77,12 @@ mod inner { // Apparently android provides this in some other library? // Bitrig's RT extensions are in the C library, not a separate librt - // OpenBSD provide it via libc + // OpenBSD and NaCl provide it via libc #[cfg(not(any(target_os = "android", target_os = "bitrig", target_os = "openbsd", - target_env = "musl")))] + target_env = "musl", + target_os = "nacl")))] #[link(name = "rt")] extern {} diff --git a/src/libstd/thread/local.rs b/src/libstd/thread/local.rs index cdd7dff010837..d301bc3130e87 100644 --- a/src/libstd/thread/local.rs +++ b/src/libstd/thread/local.rs @@ -109,14 +109,16 @@ macro_rules! thread_local { (static $name:ident: $t:ty = $init:expr) => ( static $name: ::std::thread::LocalKey<$t> = __thread_local_inner!($t, $init, - #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), + #[cfg_attr(all(any(target_os = "macos", target_os = "linux", + target_os = "nacl"), not(target_arch = "aarch64")), thread_local)]); ); (pub static $name:ident: $t:ty = $init:expr) => ( pub static $name: ::std::thread::LocalKey<$t> = __thread_local_inner!($t, $init, - #[cfg_attr(all(any(target_os = "macos", target_os = "linux"), + #[cfg_attr(all(any(target_os = "macos", target_os = "linux", + target_os = "nacl"), not(target_arch = "aarch64")), thread_local)]); ); @@ -266,7 +268,8 @@ impl LocalKey { } } -#[cfg(all(any(target_os = "macos", target_os = "linux"), +#[cfg(all(any(target_os = "macos", target_os = "linux", + target_os = "nacl"), not(target_arch = "aarch64"), not(no_elf_tls)))] #[doc(hidden)] @@ -324,7 +327,7 @@ mod imp { // fallback implementation to use as well. // // Due to rust-lang/rust#18804, make sure this is not generic! - #[cfg(target_os = "linux")] + #[cfg(any(target_os = "linux", target_os = "nacl"))] unsafe fn register_dtor(t: *mut u8, dtor: unsafe extern fn(*mut u8)) { use boxed; use mem; @@ -399,7 +402,8 @@ mod imp { } } -#[cfg(any(not(any(target_os = "macos", target_os = "linux")), +#[cfg(any(not(any(target_os = "macos", target_os = "linux", + target_os = "nacl")), target_arch = "aarch64", no_elf_tls))] #[doc(hidden)] diff --git a/src/libsyntax/abi.rs b/src/libsyntax/abi.rs index 27e331893e5d7..743063f5d6eef 100644 --- a/src/libsyntax/abi.rs +++ b/src/libsyntax/abi.rs @@ -26,6 +26,7 @@ pub enum Os { OsDragonfly, OsBitrig, OsOpenbsd, + OsNaCl, } #[derive(PartialEq, Eq, Hash, RustcEncodable, RustcDecodable, Clone, Copy, Debug)] @@ -55,7 +56,8 @@ pub enum Architecture { X86_64, Arm, Mips, - Mipsel + Mipsel, + Le32, } #[derive(Copy, Clone)] @@ -139,6 +141,7 @@ impl fmt::Display for Os { OsDragonfly => "dragonfly".fmt(f), OsBitrig => "bitrig".fmt(f), OsOpenbsd => "openbsd".fmt(f), + OsNaCl => "nacl".fmt(f), } } } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 34879606b9e4e..26838d80142d6 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -288,6 +288,7 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ ("no_main", CrateLevel), ("no_builtins", CrateLevel), ("recursion_limit", CrateLevel), + ("main_link_name", Normal), ]; #[derive(PartialEq, Copy, Clone, Debug)] diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 1a2917a1dd67f..3c4eded6e40e7 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -23,6 +23,11 @@ #include #include #include + +#if _POSIX_C_SOURCE < 1 +#include +#endif + #else #include #include @@ -84,13 +89,31 @@ rust_opendir(char *dirname) { } int -rust_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) { - return readdir_r(dirp, entry, result); +rust_dirent_t_size() { + return sizeof(struct dirent); } int -rust_dirent_t_size() { - return sizeof(struct dirent); +rust_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result) { +#if _POSIX_C_SOURCE < 1 + /// This is needed for Newlib. + if(result == NULL || entry == NULL || dirp == NULL) { + errno = EBADF; + return EBADF; + } + + errno = 0; + struct dirent* next_entry = readdir(dirp); + if(next_entry == NULL) { + *result = NULL; + } else { + memcpy(entry, next_entry, rust_dirent_t_size()); + *result = next_entry; + } + return 0; +#else + return readdir_r(dirp, entry, result); +#endif } #endif @@ -484,6 +507,60 @@ const char * rust_current_exe() { #endif +#ifdef __native_client__ +#undef __arm__ +#include + +#define STUB \ + static const char MSG1[] = "ABORT: "; \ + static const char MSG2[] = " called!"; \ + write(2, MSG1, sizeof(MSG1) - 1); \ + write(2, __func__, sizeof(__func__) - 1); \ + write(2, MSG2, sizeof(MSG2) - 1); \ + abort() + +void __pnacl_eh_sjlj_Unwind_DeleteException(struct _Unwind_Exception*); +_Unwind_Reason_Code __pnacl_eh_sjlj_Unwind_RaiseException(struct _Unwind_Exception*); +_Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *e) { + return __pnacl_eh_sjlj_Unwind_RaiseException(e); +} +void _Unwind_DeleteException(struct _Unwind_Exception *e) { + __pnacl_eh_sjlj_Unwind_DeleteException(e); +} + +void _Unwind_PNaClSetResult0(struct _Unwind_Context *c, _Unwind_Word w) { + STUB; +} +void _Unwind_PNaClSetResult1(struct _Unwind_Context *c, _Unwind_Word w) { + STUB; +} +_Unwind_Ptr _Unwind_GetIP(struct _Unwind_Context *c) { + STUB; +} +void _Unwind_SetIP(struct _Unwind_Context *c, _Unwind_Ptr p) { + STUB; +} +void *_Unwind_GetLanguageSpecificData(struct _Unwind_Context *c) { + STUB; +} +_Unwind_Ptr _Unwind_GetRegionStart(struct _Unwind_Context *c) { + STUB; +} +_Unwind_Reason_Code _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *e) { + STUB; +} +_Unwind_Ptr _Unwind_GetIPInfo(struct _Unwind_Context *c, int *i) { + STUB; +} +_Unwind_Ptr _Unwind_GetTextRelBase(struct _Unwind_Context *c) { + STUB; +} +_Unwind_Ptr _Unwind_GetDataRelBase(struct _Unwind_Context *c) { + STUB; +} + +#endif + // // Local Variables: // mode: C++ diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index a2ab8040198ff..af7c4bd7d1e01 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -12,6 +12,7 @@ #include "rustllvm.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.h" #include "llvm/Support/CBindingWrapping.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -45,6 +46,59 @@ LLVMInitializePasses() { initializeInstCombine(Registry); initializeInstrumentation(Registry); initializeTarget(Registry); + +#if ENABLE_PNACL + initializeAddPNaClExternalDeclsPass(Registry); + initializeBackendCanonicalizePass(Registry); + initializeCanonicalizeMemIntrinsicsPass(Registry); + initializeCleanupUsedGlobalsMetadataPass(Registry); + initializeConstantInsertExtractElementIndexPass(Registry); + initializeExpandArithWithOverflowPass(Registry); + initializeExpandByValPass(Registry); + initializeExpandConstantExprPass(Registry); + initializeExpandCtorsPass(Registry); + initializeExpandGetElementPtrPass(Registry); + initializeExpandIndirectBrPass(Registry); + initializeExpandLargeIntegersPass(Registry); + initializeExpandShuffleVectorPass(Registry); + initializeExpandSmallArgumentsPass(Registry); + initializeExpandStructRegsPass(Registry); + initializeExpandTlsConstantExprPass(Registry); + initializeExpandTlsPass(Registry); + initializeExpandVarArgsPass(Registry); + initializeFixVectorLoadStoreAlignmentPass(Registry); + initializeFlattenGlobalsPass(Registry); + initializeGlobalCleanupPass(Registry); + initializeGlobalizeConstantVectorsPass(Registry); + initializeInsertDivideCheckPass(Registry); + initializeInternalizeUsedGlobalsPass(Registry); + initializeNormalizeAlignmentPass(Registry); + initializePNaClABIVerifyFunctionsPass(Registry); + initializePNaClABIVerifyModulePass(Registry); + initializePNaClSjLjEHPass(Registry); + initializePromoteI1OpsPass(Registry); + initializePromoteIntegersPass(Registry); + initializeRemoveAsmMemoryPass(Registry); + initializeReplacePtrsWithIntsPass(Registry); + initializeResolveAliasesPass(Registry); + initializeResolvePNaClIntrinsicsPass(Registry); + initializeRewriteAtomicsPass(Registry); + initializeRewriteLLVMIntrinsicsPass(Registry); + initializeRewritePNaClLibraryCallsPass(Registry); + initializeSimplifyAllocasPass(Registry); + initializeSimplifyStructRegSignaturesPass(Registry); + initializeStripAttributesPass(Registry); + initializeStripMetadataPass(Registry); + initializeStripModuleFlagsPass(Registry); + // Emscripten passes: + initializeExpandI64Pass(Registry); + initializeExpandInsertExtractElementPass(Registry); + initializeLowerEmAsyncifyPass(Registry); + initializeLowerEmExceptionsPass(Registry); + initializeLowerEmSetjmpPass(Registry); + initializeNoExitRuntimePass(Registry); + // Emscripten passes end. +#endif } extern "C" bool @@ -97,6 +151,7 @@ LLVMRustCreateTargetMachine(const char *triple, if (UseSoftFloat) { Options.FloatABIType = FloatABI::Soft; } + Options.MCOptions = InitMCTargetOptionsFromFlags(); TargetMachine *TM = TheTarget->createTargetMachine(Trip.getTriple(), real_cpu, @@ -128,7 +183,9 @@ LLVMRustAddAnalysisPasses(LLVMTargetMachineRef TM, #else PM->add(new DataLayoutPass(unwrap(M))); #endif - unwrap(TM)->addAnalysisPasses(*PM); + if(TM != NULL) { + unwrap(TM)->addAnalysisPasses(*PM); + } } // Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo` diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index ad6533e5480b1..f57899c81afee 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -11,6 +11,11 @@ #include "rustllvm.h" #include "llvm/Object/Archive.h" #include "llvm/Object/ObjectFile.h" + +#if ENABLE_PNACL +#include "llvm/Bitcode/NaCl/NaClReaderWriter.h" +#endif + #include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/DiagnosticPrinter.h" @@ -716,6 +721,19 @@ extern "C" void LLVMWriteValueToString(LLVMValueRef Value, RustStringRef str) { os << ")"; } +static bool MaterializeModule(Module* M) { + // LinkModules overrides the module materializer, orphaning any globalvalues + // still unmaterialized. This is here to force the loading of any lazy + // bitcode parsing. + const auto ec = M->materializeAllPermanently(); + if(ec) { + LLVMRustSetLastError(ec.message().c_str()); + return false; + } else { + return true; + } +} + extern "C" bool LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) { Module *Dst = unwrap(dst); @@ -734,6 +752,10 @@ LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) { return false; } + if(!MaterializeModule(Dst)) { + return false; + } + std::string Err; #if LLVM_VERSION_MINOR >= 6 @@ -749,6 +771,26 @@ LLVMRustLinkInExternalBitcode(LLVMModuleRef dst, char *bc, size_t len) { return true; } +extern "C" bool +LLVMRustLinkInModule(LLVMModuleRef dest, LLVMModuleRef src) { + Module* Dst = unwrap(dest); + Module* Src = unwrap(src); + + std::string Err; + +#if LLVM_VERSION_MINOR >= 6 + raw_string_ostream Stream(Err); + DiagnosticPrinterRawOStream DP(Stream); + if (Linker::LinkModules(Dst, Src, [&](const DiagnosticInfo &DI) { DI.print(DP); })) { +#else + if (Linker::LinkModules(Dst, Src, Linker::DestroySource, &Err)) { +#endif + LLVMRustSetLastError(Err.c_str()); + return false; + } + return true; +} + extern "C" void* LLVMRustOpenArchive(char *path) { ErrorOr> buf_or = MemoryBuffer::getFile(path, @@ -877,6 +919,91 @@ LLVMRustGetSectionName(LLVMSectionIteratorRef SI, const char **ptr) { return ret.size(); } +#if ENABLE_PNACL +extern "C" bool +LLVMRustWritePNaClBitcode(LLVMModuleRef M, + const char* Path, + const bool AcceptSupportedOnly) { + std::string ErrorInfo; +#if LLVM_VERSION_MINOR >= 6 + std::error_code EC; + raw_fd_ostream OS(Path, EC, sys::fs::F_None); + if (EC) + ErrorInfo = EC.message(); +#elif LLVM_VERSION_MINOR >= 4 + raw_fd_ostream OS(Path, ErrorInfo, sys::fs::F_None); +#else + raw_fd_ostream OS(Path, ErrorInfo, raw_fd_ostream::F_Binary); +#endif + if (!ErrorInfo.empty()) { + LLVMRustSetLastError(ErrorInfo.c_str()); + return false; + } + + NaClWriteBitcodeToFile(unwrap(M), OS); + return true; +} +#else +extern "C" bool +LLVMRustWritePNaClBitcode(LLVMModuleRef _M, + const char* _Path, + const bool _AcceptSupportedOnly) { + LLVMRustSetLastError("This Rust wasn't built with PNaCl support"); + return false; +} +#endif + +#if ENABLE_PNACL +/// isNaClBitcode - Return true if the given bytes are the magic bytes for +/// PNaCl bitcode wire format. Does not take ownership of Buffer. Placed here so +/// tools don't need to depend on extra components. +static inline bool isNaClBitcode(const MemoryBuffer *Buffer) { + return isNaClBitcode((const unsigned char *)Buffer->getBufferStart(), + (const unsigned char *)Buffer->getBufferEnd()); +} +#else + static inline bool isNaClBitcode(const MemoryBuffer *_Buffer) { + return false; + } + static inline ErrorOr NaClParseBitcodeFile(MemoryBufferRef _Buffer, + LLVMContext &_Context, + raw_ostream *_Verbose = nullptr, + bool _AcceptSupportedOnly = true) { + __builtin_unreachable(); + } +#endif + +extern "C" LLVMModuleRef +LLVMRustParseBitcode(LLVMContextRef ctxt, const char* name, const void* bc, size_t len) { + std::unique_ptr buf = + MemoryBuffer::getMemBuffer(StringRef(static_cast(bc), + len), + name, + false); + + LLVMModuleRef Mod = wrap(static_cast(nullptr)); + ErrorOr Src(nullptr); + if (isNaClBitcode(buf.get())) { + Src = NaClParseBitcodeFile(buf->getMemBufferRef(), *unwrap(ctxt), + nullptr, false); + + } else { + Src = llvm::parseBitcodeFile(buf->getMemBufferRef(), *unwrap(ctxt)); + } + + if (!Src) { + LLVMRustSetLastError(Src.getError().message().c_str()); + } else { + Mod = wrap(Src.get()); + } + + return Mod; +} +extern "C" void +LLVMRustStripDebugInfo(LLVMModuleRef M) { + llvm::StripDebugInfo(*unwrap(M)); +} + // LLVMArrayType function does not support 64-bit ElementCount extern "C" LLVMTypeRef LLVMRustArrayType(LLVMTypeRef ElementType, uint64_t ElementCount) {